索引
目的:提高查询效率,
原理本质:通过不断的缩小想要获得数据的范围来筛选出最终需要的结果,就像查字典一样,比如查找hello,我们必定是先找到h开头的单词,再找he开头的单词…
磁盘IO和预读
磁盘读取数据依靠的是机械运动,每次读取数据花费的时间可以分为寻道时间,旋转时间,传输时间
- 寻道时间:磁臂移动到指定磁道所需要的时间,一般在5ms以下
- 旋转延迟:我们经常说的磁盘转速,比如一个磁盘7200转(表示一分钟可以转7200次,旋转延迟就是1/120/2=4.17ms)
- 传输时间: 从磁盘(读出/写入)磁盘的时间,一般是零点几毫秒
因为磁盘IO是非常高昂的操作,操作系统做了一些优化,会将相邻的数据也都读取到内存缓冲区中
注意📢:访问磁盘的成本大概是访问内存的十万倍左右
索引的数据结构
索引是为了提高查询效率,而数据是存储在硬盘中,所以我们需要提高磁盘查询的速度
又因为访问磁盘的成本太高,所以我们需要有一个数据结构能够满足下列要求:
- 每次查找数据时把磁盘IO次数控制在一个很小的数量级,最好是常数数量级
在一般场景中,我们如果要查询有顺序的数据,我们会使用二叉树,而二叉树因为高度不可控,IO次数取决于树高度,如果树的高度太高会导致产生较多次的磁盘IO。所以我们不能选择二叉树
然后,前辈们就发明了B+树,一颗高度可控的多路搜索树
假设当前数据表的数据为N,每个磁盘块的数据项的数量是m,则树高度h = log(m+1)N
磁盘块的大小也就是一个数据页的大小,是固定的,如果数据项的空间越小,数据项的数量越多,也就是m越大,h就会越小。即索引字段越小,树高度越小。
所以要求把真实的数据放到叶子节点而不是内层节点
查询优化
-
最左前缀匹配原则:mysql会一直向右匹配直到遇到范围查询(>, <, between, like)
比如
a = 1 and b = 2 and c > 3 and d = 4- 如果建立(a,b,c,d)顺序的索引,d是用不到索引的
- 如果建立(a,b,d,c)的索引,则都可以用到
-
选择区分度高的列作为索引(字段不重复的比例越高越好,比如主键Id的字段区分度就是1,因为主键唯一,不可能出现重复)
-
尽量扩展索引,而不是新建索引(因为索引的维护是需要开销的,数据量越多,所花费的时间和算力就不断递增)
比如:已经有了一个(a)的索引,现在要增加(a,b)的索引,则只需要将(a)索引修改为(a,b)索引
explain命令
我们可以使用explain命令来查看sql语句的执行计划,查看该sql语句有没有使用上索引,有没有做全表扫描,这个在数据库优化中,是一个非常强力的工具
下图是mysql中explain命令的输出结果中的字段解释
其中rows是核心指标,绝大部分rows小的语句执行一定很快。所以优化基本上都是在优化rows