MySQL索引那些事

Posted 码儿快跑

tags:

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


【MySQL】索引那些事


每天涨点新芝士,今天来看一点关于 mysql 索引的一些小知识。


MySQL 索引的主要作用就是为了提高数据查询的效率。常见的索引模型有哈希表、有序数组、搜索树。今天先从常见的索引模型开始,谈谈 MySQL  索引这个话题。

常见的索引模型

哈希表

哈希表是一种以 key:value 存储数据的结构。用哈希函数把 key 换算成一个确定的位置,然后把 value 放在数组的这个位置,如果多个 key 经过哈希函数处理得到的位置相同时。会在该位置拉出一个链表来存放。

当从哈希表中取数据时,先使用哈希函数对 key 获取到位置,然后遍历链表取 key 对应的值。


有序数组

有序数组就是将数据按顺序存放于数组中,当需要取数据时可以通过二分法快速取出。时间复杂度为 O(log(N))。


但是在需要插入或删除数据时,有序数组需要移动的插入或删除位置之后的所有数据,这时性能较差。


所以有序数组适合存储不经常做增删的静态数据


搜索树

搜索树有又有二叉搜索树和多叉搜索树。


二叉搜索树每个节点的左儿子小于父节点,父节点又小于右儿子,其当需要从二叉搜索树中查询数据或增删数据时时间复杂度均为O(log(N))。


多叉搜索树可以有多个儿子,而且多个儿子从左到右依次递增。


所以同样的树高多叉搜索树可以存储更多是数据。在数据库中多使用多叉搜索树而很少使用二叉搜索树。


那为什么数据库中多使用多叉搜索树而不使用二叉搜索树呢?

为了减少访问磁盘的次数。因为数据不仅在内存中,还要存储于磁盘中。当使用二叉搜索树时树会变得很高。在一个高度为 N 的二叉树中查询可能需要访问 N 个结点,当需要从磁盘中读取时是非常慢的,性能太低。当使用 N 叉树时,可以大大降低访问磁盘的次数,提高效率。

在 InnoDB 中,以整数字段为索引举例,N 叉树的 N 差不多为 1200。当树高为 4 时,已经可以存放 17 亿。最多只需要访问三次磁盘就可以取出数据。


了解完常见的索引模型,再来看看MySQL中最常见的InnoDB的索引模型是怎样的。

InnoDB 的索引模型

InnoDB 使用了 B+ 树索引模型,每个索引对应一颗 B+ 树。


主键索引的 B+ 树叶子节点存储的是数据行。所以整表都是根据主键顺序以索引的形式存放的。


而普通索引的 B+ 树,其叶子结点存放的是主键。

索引的查询方式

B+ 树为了维护索引有序性,在插入新值的时候需要做必要的维护。根据 B+ 树的算法,当一个数据页满的时候就需要申请一个新的数据页并移动部分数据过去,这叫做页裂变。当然,当相邻的两个数据也数据过少时,也会进行页合并。

主键如何设置

在一般模式下推荐使用自增主键(你懂得,自增主键应该设置为 bigint unsigned 类型),因为这样在索引维护中,插入数据时都是追加操作,因为索引自增,就不涉及到移动其他记录或者分裂。


那有没有什么情况下不使用自增主键?

当然有。当只有一个索引,而且这个所以是唯一索引时,可以使用业务字段来作为索引。这样可以在查找时少遍历一颗索引树。但是也要酌情考虑插入时索引维护的问题。

常说的回表是怎么回事

当在表中既有主键索引又有普通索引时,当通过普通索引检索数据时的执行顺序是这样的:


  1. 在普通索引索引树上查找满足条件的行,拿到主键。

  2. 回到主键索引索引树上去拿数据。

  3. 到普通索引索引树上查找,拿到满足条件的主键。

  4. 回到主键索引索引树上去拿数据。

  5. 到普通索引索引树上查找,拿到满足条件的主键。

  6. 回到主键索引索引树上去拿数据。

  7. .......

  8. 直到在普通索引上拿数据不满足条件,结束查找。

在这个过程中,每次回到主键索引树中取数据的操作就称为回表。

有什么办法可以避免回表吗?

可以通过覆盖索引的方式来避免回表,假如在表中主键索引为 id,普通索引为 age,当取数据时,如果不需要其他数据,可以只获取主键,这么写 sql 语句:

select id from table_name where age > 18;

这样在执行过程中,因为在 age 的索引树上已经存在主键 id 值(普通索引的索引树节点数据为主键),可以直接拿到 id,就不需要在回到主键索引树上去取数据,这样就是通过覆盖索引避免了回表。


其实不仅是在主键索引和普通索引之间可以通过覆盖索引来避免回表,也可以通过建立联合索引来避免回表,这种方式也被称为覆盖索引。


假如在表中有 id_no 和 name 两个字段,而在业务中有非常频繁的通过 id_no 来获取 name 的操作,而这时,如果建立 (id_no、name) 的联合索引,也可以有效的避免回表。当然,索引的维护是有一定的代价的,当遇到这样的情况时应当权衡考虑是够要通过建立联合索引来避免回表。

最左前缀原则

当我们建立 (id_no、name) 联合索引时,其索引树的节点数据同样为 (id_no、name),顺序也是相同的。如果在业务中既有 id_no 和 name 的联合查询,又有 id_no、name 的分别查询,是否需要在分别对 id_no、name 再创建索引呢?答案肯定是不需要的。


对于 id_no 的独立查询,可以使用 (id_no、name) 的联合索引索引树。对于 name 的独立查询就需创建 name 索引了。也就是说创建 (id_no、name)、(name)两个索引即可。


但是这时候可能又有一个问题:那我创建  (name、id_no)、(id_no) 两个索引可以吗?当这时候就需要根据从空间的角度考虑,id_no 是长度为 18 的字符串,而 name 是长度为  10 的字符串,当使用 (id_no、name)、(name)两个索引的组合相对于 (name、id_no)、(id_no) 的组合是占用空间较大的 id_no 在两颗索引树中只出现了一次,所以更省空间。


最左前缀不仅可以是联合索引的最左 N 个字段,也可以是字符串索引的最左 M 个字符。


请记住,如果通过调整联合索引的顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的。


索引下推

索引下推在 MySQL 5.6 及之后的版本中引入。

假设在表中有 (name、age) 的联合索引。我们需要查出姓王且年龄为20的男性用户,写出了这样的 sql 语句:

select * from table_name where name like '王%' and age=20 and gender='男';


在不支持索引下推的版本中,会在索引树上搜索姓王的第一条记录拿到主键,回表拿到数据验证年龄和性别,然后一次遍历之后的每一条姓王的用户。


在支持索引下推的版本中,在索引树上搜索时,会对索引中包含的字段优先判断,直接过滤不满足的数据。就例如在索引树中找到第一个姓王的用户,会直接看起年龄是不是20,若是,回表验证性别,若年龄不是20,则之间跳过,查询下一行。


所以我们在 where ... and ... 语句时,可以对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。

重建索引

为什么要重建索引?

在业务处理过程中,可能会有数据的删除,致使索引删除,或者页分裂,导致数据页有空洞,这时重建索引可以创建一颗新的索引树,把数据按顺序插入,提高数据页的利用率,更省空间。


那如何重建索引呢?

可能最直接想到的是这样:

-- 注意这是错误示例-- 重建索引alter table T drop index k;alter table T add index(k);-- 重建主键索引alter table T drop primary key;alter table T add primary key(id);

上边的操作会导致重建整个表。


正确的方法应该这样:

alter table T engine=InnoDB;


----没啥意思我是分割线----

最后在看一到题目,补充一下最左优先原则:


问题:假设在一张表中有联合主键索引 (a,b),并有普通索引 (c)、(c,a)、(c,b),这种设立索引的方式是否冗余?


答:InnoDB会把主键字段放到索引定义字段后面,同时会做去重操作。

  1. 在主键索引的索引树中,是先按照 a 排序,再按照 b 排序的,相当于order by a,b。

  2. 对于普通索引 (c) 的查找,在 c 的索引树上找到联合主键,然后在到主键索引树上去查找。主键索引树已经按照 a,b 排序。

  3. 对于索引 (c,a),在其索引树中先按照 c 排序再按照 a 排序,节点数据为主键b。因为联合主键索引树中已经按照 a,b 排序,故索引与 (c) 效果相同。所以此索引多余。

  4. 对于索引 (c,a),在其索引树中先按照 c 排序再按照 a 排序,节点数据为主键b。因为联合主键索引树中已经按照 a,b 排序,故索引与 (c) 效果相同。所以此索引多余。

  5. 对于索引 (c,a),在其索引树中先按照 c 排序再按照 a 排序,节点数据为主键b。因为联合主键索引树中已经按照 a,b 排序,故索引与 (c) 效果相同。所以此索引多余。

所以对于普通联合索引 (c,a) 与 (c) 效果相同,索引 (c,a) 可以删去。


好了,今天就来这么点吧。今天外边跑了一天,可真是累哟,依然老几样,分享、点赞、关注、在看四联发走起~


【MySQL】索引那些事

扫码关注我

扫码做胖友~


分享、在看与点赞

只要你点,我们就是胖友

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

MySQL索引那些事

一文搞懂底层mysql 索引那些事

一文搞懂底层mysql 索引那些事

MySQL和B+树的那些事&mysql 索引原理

MySQL索引那些事:什么是索引?为什么加索引就查得快了?

Mysql中join的那些事