您的位置:首页 > 数据库 > MySQL

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

2018-03-06 09:10 375 查看
选择索引字段的原则,比如外键字段、数据类型较小的字段、经常用于查询或排序的字段、表关联的字段等等

索引应该建在选择性高的字段上(键值唯一的记录数/总记录条数),选择性越高索引的效果越好、价值越大,唯一索引的选择性最高;
组合索引中字段的顺序,选择性越高的字段排在最前面;
where条件中包含两个选择性高的字段时,可以考虑分别创建索引,引擎会同时使用两个索引(在OR条件下,应该说必须分开建索引);
不要重复创建彼此有包含关系的索引,如index1(a,b,c) 、index2(a,b)、index3(a);
组合索引的字段不要过多,如果超过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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: