MySQL索引&执行计划分析
索引的作用
提供了类似于书中目录的作用,目的是为了优化查询
索引的种类(算法)
- B树索引
- Hash索引
- R树
- Full text
- GIS
B树 基于不同的查找算法分类介绍
B tree生成过程:
leaf节点:每个page为16kb,其中均匀的存放“盒子”,盒子中存放数据。每个盒子都有自己的编号。被称为叶子节点。
internal节点:枝节点会将每个叶子节点所存放的最小值提取出来集中存放,存放的位置被称为枝节点。
root节点:提取枝节点的最小值,落到根节点上。
B tree工作过程:
例如读取数据x:
- 遍历根节点,找到符合条件的数据索引
- 根据索引提供的对应的指向枝节点的指针找到对应的枝结点page
- 在枝节点page中再进行比对,找到对应叶结点所在page在page中存放着数据的页码
- 根据页码找到对应的存放数据的page
b+tree:
- 相邻的叶结点直接互相存在指针,减少遍历根节点和枝节点的重复劳动。相当于章节结尾的导读,优化范围查找
b*tree:
- 相邻枝节点也存在相互的指针,优化范围查找
功能上的分类
辅助索引(S)怎么构建B树结构的?
(1)辅助索引是基于表的任意列进行生成的【任意列】
(2)取出索引列的所有值【取出列的所有键值】
(3)进行所有键值的排序
(4)将所有的键值按顺序落到BTree索引的叶子节点上【排序后落到叶子节点上】
(5)进而生成枝节点和根节点【生成BTree索引】
(6)叶子节点除了存储键值之外,还存储了相邻叶子节点的指针,另外还会保存原表数据的指针
(辅助索引的叶子节点直接存在指针(优化范围查找);还会保存原表数据的指针,用于查找记录)
聚集索引(C)怎么构建B树结构的?
(1)建表时有主键列(如ID)【保证数据在数据页上的有序存储】
(2)表中进行数据存储,会按照ID列的顺序,有序的存储一行一行的数据到数据页(page)上(这个动作叫做聚集索引组织表:有序的使用段区页)【将有序的数据页(page)落到叶子节点上】
(3)表的数据页被作为聚集索引的叶子节点(有聚集索引的原表数据页,就是聚集索引的叶子节点)
(4)把叶子节点的主键值生成上层枝节点和根节点。
主键:(值不可重复,也不可为空(NULL),主键值不能被重用)
既有辅助索引又有聚集索引
辅助索引叶子节点的指针指向聚集索引–>找到对应的记录
只有辅助索引
如果没有聚集索引,只有辅助索引,那么查找记录将是无序的。(相当于书有页码,但是页码是乱的你说气不气)
聚集索引和辅助索引构成区别总结
- 聚集索引只能有一个,非空唯一,一般是主键 (聚集引索,唯一非空主键)
- 辅助索引,可以有多个,是配合聚集索引使用的 (辅助引索,可多个,配合聚集引索)
- 聚集索引叶子节点,就是磁盘的数据行有序存储的数据页
- MySQL是根据聚集索引,组织存储数据,数据存储时就是按照聚集索引的顺序进行存储数据
- 辅助索引,只会提取索引键值,进行自动排序生成B树结构
辅助索引的细分
- 单列的辅助索引
理想化:
2. 联合多列辅助索引(覆盖引索)//要查的值就是索引的值
SQL只需要通过索引就可以返回查询所需要的数据,而不必通过二级索引查到主键之后再去查询数据。
3. 唯一索引 //唯一值多的索引列可减少索引树的遍历次数
辅助索引建立原则
- 经常拿来做where条件的列 可作为辅助索引
- 尽量拿唯一值多的列做辅助索引(减少索引树遍历的次数)
索引树的高度
(1)数据行多, 分表
(2)索引列字符长度 ,前缀索引
(3)char > varchar ,表设计
(4)enum 优化索引高度,能用则用
索引管理
辅助索引建立
在k2列中建立索引
1 | mysql> use; |
索引的查看:
desc t100w;
分类:
- PRI :主键引索
- MUL:非唯一辅助索引
- UNI:唯一性索引
唯一索引建立
判断指定列是否可以建立唯一索引
方法一:去重判断
方法二:查看是否存在重复列
方法三:直接建立
前缀索引(只能应用于字符串列)
alter table city add index idx_name(name(5));
<==取前五个字符串
联合索引
建立联合索引:
1 | mysql> alter table city add index idx_co_po(countrycode,population); |
删除联合索引:
mysql> alter table city drop index idx_co_po;
执行计划分析-type分析
作用
上线新的查询语句之前,进行提前预估语句的性能
在出现性能问题时,找到合理的解决思路
执行计划获取
1 | mysql> desc select * from oldboy.t100w where k2='EF12'\G |
重点关注
able: t100w
|表信息type: ref
|索引的应用级别possible_keys: idx_k2
|可能会使用到的索引key: idx_k2
|实际上使用的索引key_len: 17
|联合索引覆盖长度rows: 293
|查询的行数(越少越好)Extra: NULL
|额外的信息
type:索引
执行计划的分析
type 索引的应用级别
ALL :全表扫描,不走索引
原因:没建立索引
建立索引不走的
以下情况可能会出现ALL的情况(查询条件不是确定值的话都可能出现ALL的情况)
1 | mysql> desc select * from t100w; //查全表 |
Index :全索引扫描
1 | mysql> desc select k2 from t100w; //这里查询的k2 就是辅助索引列 |
range :索引范围扫描
1 | 辅助索引 : > < >= <= like(开头不带%)受双向指针优化,in or(避免出现) |
ref : 辅助索引等值查询
1 | mysql> desc select * from city where countrycode='CHN'; |
const,system : 主键或唯一键等值查询
1 | mysql> DESC select * from city where id=10; |
Extra
using filesort
:只要出现此提示,一般是where条件和order by条件没有一起建立联合索引导致的
where走了索引 但是后面的order by条件数据库要给他重新排序,比较消耗计算机资源
//首先考虑建立联合索引
联合索引建立前:
联合索引建立后:
key_len: <==针对字符
1 | desc |
扩展:
key_len 到底长好 还是短好?
维度一: 索引列的列值长度来看
越短越好,一般针对前缀索引(前缀索引好处:1.参差不齐的列均匀都规整起来。2.列值长度限制在一定范围)维度二: 从联合索引覆盖长度来看
覆盖长度越长越好
explain(desc)使用场景(面试题)
题目意思: 我们公司业务慢,请你从数据库的角度分析原因
- mysql出现性能问题,我总结有两种情况:
(1)应急性的慢:突然夯住
应急情况:数据库hang(卡了,资源耗尽)
处理过程:
- show processlist; 获取到导致数据库hang的语句
- explain 分析SQL的执行计划,有没有走索引,索引的类型情况
- 建索引,改语句
索引应用规范
业务
- 产品的功能
- 用户的行为
“热”查询语句 —>较慢—>slowlog
“热”数据
建立索引的原则(DBA运维规范)
说明
为了使索引的使用效率更高,在创建索引时,必须考虑在哪些字段上创建索引和创建什么类型的索引。
那么索引设计原则又是怎样的?
(必须的) 建表时一定要有主键,一般是个无关列
略.回顾一下,聚集索引结构.
选择唯一性索引
- 唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。
例如,学生表中学号是具有唯一性的字段。为该字段建立唯一性索引可以很快的确定某个学生的信息。
如果使用姓名的话,可能存在同名现象,从而降低查询速度。
优化方案:
1 | (1) 如果非得使用重复值较多的列作为查询条件(例如:男女),可以将表逻辑拆分 |
(必须的) 为经常需要where 、ORDER BY、GROUP BY,join on等操作的字段,排序操作会浪费很多时间。
1 | where A B C ----》 A B C |
如果为其建立索引,优化查询
- 注:如果经常作为条件的列,重复值特别多,可以建立联合索引。
尽量使用前缀来索引
- 如果索引字段的值很长,最好使用值的前缀来索引。
限制索引的数目
索引的数目不是越多越好。
可能会产生的问题:
- 1)每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。
- 2)修改表时,对索引的重构和更新很麻烦。越多的索引,会使更新表变得很浪费时间。(增删改索引会涉及到短时间的锁表)
- 3)优化器的负担会很重,有可能会影响到优化器的选择.
percona-toolkit中有个工具,专门分析索引是否有用
删除不再使用或者很少使用的索引(percona toolkit)
pt-duplicate-key-checker
表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。数据库管理
员应当定期找出这些索引,将它们删除,从而减少索引对更新操作的影响。
大表加索引,要在业务不繁忙期间操作(涉及到锁表和排序)
尽量少在经常更新值的列上建索引,10万行以下的表不需要建索引
建索引原则
- (1) 必须要有主键,如果没有可以做为主键条件的列,创建无关列
- (2) 经常做为where条件列 order by group by join on, distinct 的条件(业务:产品功能+用户行为)
- (3) 最好使用唯一值多的列作为索引,如果索引列重复值较多,可以考虑使用联合索引
- (4) 列值长度较长的索引列,我们建议使用前缀索引.
- (5) 降低索引条目,一方面不要创建没用索引,不常使用的索引清理,percona toolkit(xxxxx)
- (6) 索引维护要避开业务繁忙期
关于联合索引 *****
- (1) where A、GROUP BY B、ORDER BY C —> (A,B,C)//联合索引顺序
- (2) where A B C
- (2.1) 都是等值 ,在5.5 以后无关索引顺序,把控一个原则唯一值多的列放在联合索引的最左侧
- (2.2) 如果有不等值,例如以下情况
select where A= and B> and C= 索引顺序,ACB ,语句改写为 ACB
不走索引情况
没有查询条件,或者查询条件没有建立索引
1 | select * from tab; 全表扫描。 |
- 1、对用户查看是非常痛苦的。
- 2、对服务器来讲毁灭性的。
1 | SQL改写成以下语句: |
改:
- 1、换成有索引的列作为查询条件
- 2、将name列建立索引
查询结果集是原表中的大部分数据,应该是25%以上。
查询的结果集,超过了总数行数25%,优化器觉得就没有必要走索引了。
假如:tab表 id,name
id:1-100w ,id列有(辅助)索引select * from tab where id>500000;
解决方案:
- 如果业务允许,可以使用limit控制。
怎么改写 ? - 结合业务判断,有没有更好的方式。如果没有更好的改写方案
- 尽量不要在mysql存放这个数据了。放到redis里面。
索引本身失效,统计数据不真实
- 1.索引有自我维护的能力。
- 2.对于表内容变化比较频繁的情况下,有可能会出现索引失效(来不及更新索引信息,优化器选择一次后发现不行以后就不会选择该索引)。
解决方案:一般是删除重建
现象:有一条select语句平常查询时很快,突然有一天很慢,会是什么原因
select? —>索引失效,统计数据不真实
DML ? —>锁冲突,资源耗尽
查询条件使用函数在索引列上,或者对索引列进行运算,运算包括(+,-,*,/,! 等)
造成原因:条件列中出现运算则会不走索引
例子:
错误的例子:select * from test where id-1=9;
正确的例子:select * from test where id=10;
隐式转换导致索引失效.这一点应当引起重视.也是开发中经常会犯的错误.
这样会导致索引失效. 错误的例子:
造成原因:查询的列中带有函数操作,直接全表扫描
1 | mysql> alter table tab add index inx_tel(telnum); //在电话号码创建辅助索引 |
单独的>,<(防止出现结果集大于全表25%) ,in 有可能走,也有可能不走,和结果集有关,尽量结合业务添加limit
or
或in
尽量改成union
1 | EXPLAIN SELECT * FROM teltab WHERE telnum IN ('110','119'); |
原因:改写后索引的应用级别高
like “%_” 百分号在最前面不走(针对字符串列)
1 | EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '31%' 走range索引扫描 |