进行后端开发经常会与数据库打交道,对于数据库的一些核心基础了解太浅,比如经常听到的数据库索引、锁等,在小项目开发过程中也没有过于严格的执行索引的建立,而是想当然的对表直接设置一个自增字段作为主键,这些还是源于对于索引的认识不够深喃。
索引
在此之前,对于索引的认识如下:
- 索引相当于一张表的附加表,存储着表中建立索引的字段值以及对应记录值的地址,在进行查询操作时,首先在索引表中进行查询,根据查询的条件找到相关记录的地址,再根据记录地址在表中存取对应的记录。
- 经常进行INSERT、UPDATE、DELETE操作就不要建立索引了,换言之,索引会降低插入、删除、更新等维护任务的速度。
- 索引分为聚集索引和非聚集索引。
- 目前MySQL的InnoDB引擎支持Hash索引、B+树索引和全文索引。
- 索引的最左匹配原则。
只停留在了解阶段是远远不够的,必须深入了解其中的每一个细节才能更好的掌握索引的奥秘,比如为什么索引会降低插入、删除等维护的速度、Hash索引和B+树索引的区别以及为什么索引会加快查询的速度等等。
说说话索引的基础
MySQL的数据存储是基于页的方式
各个数据页之间以双向链表的形式连接,在数据页内部各个数据之间也已单向链表的形式存在。
每个数据页会为存储在它里面的数据生成一个页目录,在通过主键查询时可以通过二分法在页目录中快速定位到对应的槽位置,再通过遍历该槽对应分组中的记录即可快速找到指定的数据。
对于非主键的字段作为查询条件,只能从最小记录开始遍历。
如果我们用这样一条未经优化的查询语句:select * from table_name where name='zhang'
,它的查询步骤如下:
- 定位到对应页。需要遍历双向链表,找到所在的页。
- 在所在页中找到相应的数据。由于没有添加索引,只有遍历页中的单向链表了。
可想而知,当数据量相当庞大时,遍历单向链表是多么的花费时间的事。
索引如何提高查询速度?
索引无非就是将无序的数据变成相对有序的。
以下是要找到id为8的记录简要步骤:
通过页目录可以在O(logN)的时间内快速定位到数据具体的页上,如果没有用索引的话,就需要遍历整个双向链表以及单向链表来找到具体的数据。
MySQL索引的底层实现是B+树,在叶子节点上存储数据,其他节点只是作为一个索引的作用。
索引怎么就降低增删改的速度?
首先知道了MySQL底层是B+树的结构。
B+树是平衡树的一种,它的非叶子节点只具有导航的作用,数据全部存储在叶子节点上,并且叶子节点之间是相链的,因此遍历一棵B+数只需要在线性时间内遍历叶子节点即可。
为了更好的保持B+树的结构性,在每次进行INSERT、UPDATE和DELETE后都需要维护树结构,这些维护工作也将导致额外的开销,导致索引降低了增删改的速度。
Hash索引
哈希索引即采用哈希算法,将键换算成哈希值,检索时不需要像B+树那样遍历节点,只需要一次哈希算法即可立即定位到相应的位置,速度极快。
虽然如此,但是哈希算法也有它的局限性。
哈希索引没法利用索引完成排序、不支持最左匹配原则、在存在大量重复键的情况下会有哈希碰撞问题、不支持范围查询。
MySQL的InnoDB引擎支持哈希索引,但是是一种自适应哈希,InnoDB存储引擎会根据表的使用情况自动为表生成哈希索引,不能人为干预是否在一张表中生成哈希索引。
聚集索引 & 非聚集索引
聚集索引:按照每张表的主键构造一棵B+树。
非聚集索引:以非主键创建的索引。
聚集索引在叶子节点存储的是表中的数据,并且聚集索引在逻辑上是连续的,而非聚集索引在叶子节点存储的是主键和索引列。当使用非聚集索引查询时,拿到叶子上的主键再去查找想要的数据。
聚集索引在一张表中只能有一个,而非聚集索引可以有多个。
说到非聚集索引就需要说到覆盖索引。非聚集索引查询需要在叶子节点拿到主键,再通过主键回表拿到具体的数据。而覆盖索引就是当要查询的列就是索引时,拿到主键后就不需要回表操作了。
例子:
创建了索引(username,age),在查询数据时:select username,age from user where username='zhang' and age=21
以上这条查询条件是会走索引的,并且要查询的列已经在叶子节点上了,符合覆盖节点,于是不需要再拿到对应的主键回表了。
索引最左匹配原则
索引可以是一个列(a),也可以是多个列共同联合构成(a,b,c,d),即联合索引。
索引只能匹配键值相等的情况,不知道范围查询,即遇到like、between、>、<时就不能进行匹配,后续匹配过程会退化成线性查找。列的排列顺序也将决定可命中索引的次数。
如果有索引(a、b、c、d),查询条件是a=1 and b=2 and c>3 and d=4
则会在每个节点依次命中a、b、c,无法命中d。(索引无法范围匹配)
对于in
以及=
等的顺序,MySQL会自动优化这些条件的顺序,以匹配更多的索引列。
例子:
如有索引(a, b, c, d),查询条件c > 3 and b = 2 and a = 1 and d < 4与a = 1 and c > 3 and b = 2 and d < 4等顺序都是可以的,MySQL会自动优化为a = 1 and b = 2 and c > 3 and d < 4,依次命中a、b、c。
索引的最左匹配原则就是对于查询条件会一直向右匹配直到遇到范围匹配或匹配条件结束。