MySQL调优4---索引

Posted FarmerSun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL调优4---索引相关的知识,希望对你有一定的参考价值。

如果合理的设计且使用索引的mysql是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车。

索引分类

1.PRIMARY KEY 主键索引:一个表只能有一个主键,不允许有空值。
2.UNIQUE INDEX 唯一索引:索引列的值必须唯一,但允许有空值。
3.INDEX 普通索引:最基本的索引,它没有任何限制
4.FULLTEXT 全文索引:主要用来查找文本中的关键字,而不是直接与索引中的值相比较。
5.INDEX 组合索引:多个字段上创建的索引

索引的作用

1.快速查找,减少服务器需要扫描的数据量
2.减少IO操作,避免临时表

索引的数据结构

1.Memory存储引擎使用哈希表。
优点:快,在内存中。缺点:不能持久化,重启没有了。
2.Mylsam和InnoDB使用B+树
优点:
1.B+树每个叶子节点可以包含更多的节点(相比于二叉树,平衡二叉树,红黑树等),既降低了树的高度,又将数据范围变为多个区间,加快了检索速度。
2.非叶子节点仅存储key值(B树非叶子节点也存储数据),叶子节点存储key值和数据(Mylsam存储引擎存储的是数据文件的地址)。
3.叶子节点两两之间指针连接,顺序查询性能更高。

聚簇索引和非聚簇索引

聚簇索引

数据和相邻的键值存储在一起;访问速度更快,在同一个树中;使用覆盖索引查询可以直接取出数值。

非聚簇索引

数据文件跟索引文件分开存放,先查询到索引,再去对应的文件位置查询数据

索引B+树的高度计算

B+TREE高度

了解B+Tree索引的大概结构后,我们接下来讲解一下如何计算索引树的高度。

我们先做如下假设:

表的记录数是N;

每个BTREE节点平均有B个索引KEY(即1,2,3,4,5… …),这时候B+TREE索引树的高度就是logB/logN。

另外我们知道,由于索引树每个节点大小固定,所以索引KEY越小,B值就越大,即每个BTREE节点上可以保存更多的索引KEY。并且索引树的高度是logBN,那么B值越大,索引树的高度越小,那么基于索引查询的性能就越高。所以我们可以得到结论:相同表记录数的情况下,索引KEY越小,索引树高度就越小。

每个索引节点一般都是操作系统页的整数倍,操作系统页可通过命令得到该值得大小,且一般是4094,即4k。而InnoDB的pageSize可以通过命令得到,默认值是16k。

关于预读:在索引树上查到某个KEY(例如id=3),需要先找到这个KEY所在的叶子节点(因为B+Tree索引只有叶子节点上有具体的数据),这个查找过程从根节点到叶子节点,需要经过整个树。当找到叶子节点后,会根据预读原理将整个节点数据全部加载到内存中,然后基于二分法找到最终的KEY。

OK,到这里,我们距离真正计算一个拥有1600w数据的表的索引树的高度,只差每个索引KEY所占空间了。

以BIGINT为例,存储大小为8个字节。INT存储大小为4个字节(32位)。索引树上每个节点除了存储KEY,还需要存储指针。所以每个节点保存的KEY的数量为pagesize/(keysize+pointsize)(如果是B-TREE索引结构,则是pagesize/(keysize+datasize+pointsize))。

假设平均指针大小是4个字节,那么索引树的每个节点可以存储16k/((8+4)*8)≈171。那么:一个拥有1600w数据,且主键是BIGINT类型的表的主键索引树的高度就是(log10^7)/log171 ≈ 24/7.4 ≈ 3.2。

假设平均指针大小是6个字节,那么索引树的每个节点可以存储16k/((8+6)*8)≈146。那么:一个拥有1600w数据,且主键是BIGINT类型的表的主键索引树的高度就是(log10^7)/log146 ≈ 24/7.2 ≈ 3.3。

假设平均指针大小是8个字节,那么索引树的每个节点可以存储16k/((8+8)*8)≈128。那么:一个拥有1600w数据,且主键是BIGINT类型的表的主键索引树的高度就是(log10^7)/log128 ≈ 24/7 ≈ 3.4。

由上面的计算可知:一个千万量级,且存储引擎是MyISAM或者InnoDB的表,其索引树的高度在3~5之间。

索引监控

show status like \'Handler_read%\';
Handler_read_first:读取索引第一个条目的次数
Handler_read_key:通过index获取数据的次数
Handler_read_last:读取索引最后一个条目的次数
Handler_read_next:通过索引读取下一条数据的次数
Handler_read_prev:通过索引读取上一条数据的次数
Handler_read_rnd:从固定位置读取数据的次数
Handler_read_rnd_next:从数据节点读取下一条数据的次数

数值越大,说明索引利用率更高

索引使用注意事项

1.回表

主键查询不会触发回表,其他索引会触发回表。
因为innodb的主键索引树叶子结点上保存的是全行数据,主键查询可以一次定位到数据的行数,取出数据内容;
而其他索引需要两次查询,第一次查询到主键值,再根据主键值查询出对应的数据。

2.覆盖索引

explain的输出结果Extra字段为Using index时,能够触发索引覆盖。只需要在一棵索引树上就能获取SQL所需的所有列数据,无需回表,速度更快。

3.索引覆盖

将需要查询的结果字段建立组合索引
能够一次取出索要查询的结果字段,不需要回表

4.最左匹配

索引查询从最左边开始匹配
组合索引需要考虑索引的顺序和分组的合理性,如下,建立a、b、c三个索引,查询结果如表所示

5.前缀索引

在一个很长的字符串上建立索引,选取长度过长会消耗太多的内存,让索引查询效率变慢,所以需要选择一个合适的长度。
可以截取字符串的前几位和总数做对比,取出一个合适的长度。
前缀索引是一种能使索引更小更快的有效方法,但是也包含缺点:mysql无法使用前缀索引做order by 和 group by。

6.索引下推

mysql默认启用索引下推,我们也可以通过修改系统变量optimizer_switch的index_condition_pushdown标志来控制

SET optimizer_switch = \'index_condition_pushdown=off\';

索引下推适合INNODB引擎的二级索引。即第一次用第一级索引取出符合条件的部分数据,然后第二级别索引继续过滤条件查询,最终结果再回表根据主键查询。如下SQL:
现有student表建有组合索引(name,score),要求查询分数在90分以上且姓赵的同学。

SELECT *FROM student WHERE name LIKE \'赵%\' AND score>90;

先查询出姓赵的同学,然后根据分数过滤拿到主键id,根据主键id回表查询
Like:匹配模式必须要左边确定不能以通配符开头

7.union all,in,or都能够使用索引,但是推荐使用in

必须要保证 OR 两端的条件都存在可以用的索引,该查询才可以使用索引

8.范围列可以用到索引,但是范围列后面的列无法用到索引,索引最多用于一个范围列,范围条件是:<、<=、>、>=、between

9.强制类型转换会全表扫描

本来有一个字符串的字段建立了索引,查询的时候字符串后面匹配一个整型值,会导致全表扫描。
oracle数据库类型不匹配会报错

10.阿里规范,join表最好不超过三张,非要使用在关联条件上创建索引

11.更新频繁的字段不是和建索引,索引维护消耗资源,增删改都会重新更新索引。更新索引时会有页分裂和合并等IO操作。

一般区分度在80%以上的时候就可以建立索引,区分度可以使用 count(distinct(列名))/count(*) 来计算

12.合理使用索引,并不是越多越好。

单表索引建议控制在5个以内
单索引字段数不允许超过5个(组合索引)
能使用limit的时候尽量使用limit

以上是关于MySQL调优4---索引的主要内容,如果未能解决你的问题,请参考以下文章

MySQL调优索引优化

深入理解MySQL:InnoDB 引擎日志事务索引锁 及MySQL调优

MYSQL数据库的设计与调优

MySQL 数据库规范--调优篇(终结篇)

深入理解MySQL:InnoDB 引擎日志事务索引锁主备调优

深入理解MySQL:InnoDB 引擎日志事务索引锁主备调优