深入理解MySQL索引和性能优化

选择索引字段的原则,比如外键字段、数据类型较小的字段、经常用于查询或排序的字段、表关联的字段等等

  1. 索引应该建在选择性高的字段上(键值唯一的记录数/总记录条数),选择性越高索引的效果越好、价值越大,唯一索引的选择性最高;
  2. 组合索引中字段的顺序,选择性越高的字段排在最前面;
  3. where条件中包含两个选择性高的字段时,可以考虑分别创建索引,引擎会同时使用两个索引(在OR条件下,应该说必须分开建索引);
  4. 不要重复创建彼此有包含关系的索引,如index1(a,b,c) 、index2(a,b)、index3(a);
  5. 组合索引的字段不要过多,如果超过4个字段,一般需要考虑拆分成多个单列索引或更为简单的组合索引;

使用索引的原则

1. 如果没有唯一性要求,可以选择普通索引
2. 如果列上有唯一性要求,可以选择唯一索引
3. 如果是需要模糊搜索,建议选择全文索引

4. 如果有多个条件一起查询,可以选择组合索引

组合索引举例说明:

索引:在对表需要进行查询或者排序操作时,可以对表中某个或者某几个字段添加索引,对

一个字段添加索引就是单个索引,对多个字段添加索引时就是组合索引。

create index  A_index on A(id,custName); 

给表A的id、custName字段建立组合索引,组合索引对查询条件是单个字段或者两个字段

都起作用,但是有些地方还是要注意:

select * from A where id > 1 and custName = ‘tom’;  — 这种情况会走索引

select * from A where id > 1;  —  这种情况也会走索引,尽管只是使用了组合索引中一个字段

注* :但是下面的情况就不会走索引

select * from A where custName = ‘tom’;  –因为custName位于组合索引的第二个字段



举下面一个场景的例子,创建这样的索引是有效的吗?

select * from t1, t2 where  t1.col_1 = t2.ab and t1.col_2 in (12, 38); -- 创建索引如下 create index idx_t1_query on t1(col_1, col_2);
-- 或者仅创建索引如下
create index idx_t1_col2 on t1(col_2);

再比如,该表最常使用的SQL场景有以下两种类型,应该如何创建索引?

select * from t1 where t1.PartId = 'xxxx' and t1.STATE = 2 and t1.PROCID = 'yyyy' select * from t1 where (t.PartId = 'xxxx' or t1.ActualPartId = 'xxxx' ) and t1.STATE = 2 and t1.PROCID = 'yyyy' -- 创建一个“全覆盖的索引”,把查询条件都包含的索引 create index idx_t1_query on t1(partId, actualpartId, state, procid); -- 还是分开创建如下两个索引 create index idx_t1_PartId on t1(partId, state, procid) create index idx_t1_actualPartId on t1(actualpartId, state, procid)

以执行计划和逻辑IO的统计数据显示,两个场景的测试结果都是后者索引有明显的效果,大家有兴趣可以自己测试验证一下。当然,生产环境远比这些要复杂,各表的数据量及数据分布情况也会影响引擎的执行方式,引擎对索引选择与要求也会不一样,此处仅以简单语句做示例进行说明。

组合索引查询的各种场景:

组合索引 Index (A, B, C)

  • 下面条件可以用上该组合索引查询:
    • A>5
    • A=5 AND B>6
    • A=5 AND B=6 AND C=7
    • A=5 AND B=6 AND C IN (2, 3)
  • 下面条件将不能用上组合索引查询:
    • B>5                                           ——查询条件不包含组合索引首列字段
    • B=6 AND C=7                            ——理由同上
  • 下面条件将能用上部分组合索引查询:
    • A>5 AND B=2                            ——当范围查询使用第一列,查询条件仅仅能使用第一列
    • A=5 AND B>6 AND C=2             ——范围查询使用第二列,查询条件仅仅能使用前二列
    • A=5 AND B IN (2, 3) AND C=2   ——理由同上

组合索引排序的各种场景:

组合索引 Index(A, B)

  • 下面条件可以用上组合索引排序:
    • ORDER BY A                   ——首列排序
    • A=5 ORDER BY B            ——第一列过滤后第二列排序
    • ORDER BY A DESC , B DESC      ——注意,此时两列以相同顺序排序
    • A>5 ORDER BY A            ——数据检索和排序都在第一列
  • 下面条件不能用上组合索引排序:
    • ORDER BY B                   ——排序在索引的第二列
    • A>5 ORDER BY B            ——范围查询在第一列,排序在第二列
    • A IN(1,2) ORDER BY B    ——理由同上
    • ORDER BY A ASC , B DESC        ——注意,此时两列以不同顺序排序

索引合并的简单说明:

  • 数据库能同时使用多个索引
    • SELECT * FROM TB WHERE A=5 AND B=6
      • 能分别使用索引(A) 和 (B);
      • 对于这个语句来说,创建组合索引(A,B) 更好;
      • 最终是采用组合索引,还是两个单列索引?主要取决于应用系统中是否存在这类语句:SELECT * FROM TB WHERE B=6
    • SELECT * FROM TB WHERE A=5 OR B=6
      • 组合索引(A, B)不能用于此查询(目前的数据库也很智能,部分OR条件也能够使用组合索引,但效果不是很稳定);
      • 很明显,分别创建索引(A) 和 (B)会更好;
  • 删除无效的冗余索引
    • TB表有两个索引(A, B) 和 (A),对应两种SQL语句:SELECT * FROM TB WHERE A=5 AND B=6 和 SELECT * FROM TB WHERE A=5
      • 执行时,并不是WHERE A=5 就用 (A); WHERE A=5 AND B=6  就用 (A, B);
      • 其查询优化器会使用其中一个以前常用索引,要么都用(A, B), 要么都用 (A)。
      • 所以应该删除索引(A),它已经被(A, B)包含了,没有任何存在的必要。

附,查询指定数据表的索引定义情况:

--Sqlserver: sp_helpindex 'tableName' --或者 select t2.name tabName, t3.name indName, t4.name colName, t1.* from sys.index_columns t1 join sys.tables t2 on t1.object_id = t2.object_id join sys.indexes t3 on t2.object_id = t3.object_id and t1.index_id = t3.index_id join sys.columns t4 on t2.object_id = t4.object_id and t1.column_id = t4.column_id where t2.name = 'tableName' order by t3.name, t1.index_column_id  
--Oracle: select * from user_ind_columns a where a.TABLE_NAME = upper('tableName') order by a.INDEX_NAME, a.COLUMN_POSITION;

使用索引需要注意以下几点:
1. 按需使用索引
2. 索引所在的列基数越大越好 , 男女这种字段建立索引的效果并不大 ,基数很小
3.在组合索引上要注意最左原则


我们想要知道我们的sql语句写的好不好,怎么来判断?

我们先说下sql语句是怎么执行的,举个例子

select u.name i.expression from user u left join userinfo i on u.id=i.uid where u.id in (1,3,4,55,67,76) order by u.id limit 10;

这条sql语句,会先执行哪一块? 执行的原理是什么?


select u.name i.expression from user u left join userinfo i on u.id=i.uid where u.id in (1,3,4,55,67,76) order by u.id limit 10;

sql语句执行的逻辑是这样的
第一步: 将user表和 userinfo表 做笛卡尔积
1.FROM  子句对其后面的左表user和右表执userinfo行笛卡尔积, 产生虚拟表VT1

2.ON 子句对VT1中的数据根据ON的条件进行过滤,产生虚拟表VT2
问题:怎么过滤的?

3.JOIN子句 将未符合条件的保留表中的数据添加都VT2中,形成VT3

4.WHERE子句 对VT3中的数据进行WHERE条件过滤,形成VT4

5.GROUP BY 子句对VT4中的数据进行分组操作,然后形成VT5

6.CUBE | ROLLUP 子句进行操作形成VT6


7.HAVING 对VT6中的数据进行HAVING 条件过滤,然后形成VT7

8.SELECT 从VT7中选择要获取的字段,然后形成VT8

9.DISTINCT 去重数据,形成VT9

10.ORDER BY 对VT9的结果排序后,形成VT10

11.LIMIT 从VT10中取出指定的数据,形成VT11,返回给用户



我们想要知道我们的sql语句写的好不好,怎么来判断?

方法一: 直接在数据库上测试,看看执行时间

方法二: explain select xxxx 查看


其中需要关注的几个参数:
type 的值有多个  

const:表最多有一个匹配行,const用于比较primary key 或者unique索引。
eq_ref:它用在一个索引的所有部分被联接使用并且索引是UNIQUE或PRIMARY   KEY”。

eq_ref可以用于使用=比较带索引的列。
ref 对于每个来自于前面的表的行组合,所有有匹配索引值的行将从这张表中读取。
range 给定范围内的检索,使用一个索引来检查行。
ref列显示使用哪个列或常数与key一起从表中选择行。

rows 显示MYSQL执行查询的行数,简单且重要,数值越大越不好,说明没有用好索引。

建立索引的缺点:

不能因为建索引可以提高查询效率,就建立很多索引,建索引一方面要占用物理存储空间,另一方面

在进行dml操作(插入、更新、删除)时,会降低效率。

参考资料:http://mp.weixin.qq.com/s/jRzYSEG0haf2phMaBNnVkg,     

                http://www.cnblogs.com/zhaoguan_wang/p/4604025.html?utm_source=tuicool&utm_medium=referral

    原文作者:mysql
    原文地址: https://blog.csdn.net/zjuwangleicn/article/details/79453488
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞