Mysql 索引
Contents
[NOTE] Updated September 3, 2023. This article may have outdated content or subject matter.
索引是 MySQL 对查询做的一个优化,把原本杂乱无章的数据,用有序的结构组织起来,让全表扫描变成有章可循的查询。
索引的目的是提高查找效率,对数据表的值集合进行了排序,并按照一定数据结构进行了存储。
索引的数据结构
在 MySQL 中,索引是在存储引擎层实现的,而不同的存储引擎根据其业务场景特点会有不同的实现方式。
B+树索引是关系型数据库中最常见的一种索引,也将是本文的主角。可以简单的认为 B+树是一种 N 叉树和有序数组的结合体。
B+树的 3 个优点:
- 层级更低,IO 次数更少
- 每次都需要查询到叶子节点,查询性能稳定
- 叶子节点形成有序链表,范围查询方便
Innodb B+树索引
聚集索引和非聚集索引
聚集索引(clustered index 主键索引)和辅助索引(secondary index 非聚集索引(非主键索引))的不同点在于叶子节点是否是完整行数据。
聚集索引就是按照每张表的主键构造一棵 B+树,叶子节点存放的是表的完整行记录。非聚集索引的叶子节点不包含行记录的全部数据。Innodb 存储引擎的非聚集索引的叶子节点的内容为主键索引值。
一个经验性的加索引原则是字段的选取范围很广。
在对非聚集索引进行查找时,可能会因为选择对象的问题引发回表,即回到主键索引树搜索的过程。避免回表操作是提升 SQL 查询效率的常规思路及重要方法。那么如何避免回表?
- 覆盖索引(covering index):即从辅助索引中就可以得到查询的记录,而不需要查询聚集索引中的记录。使用覆盖索引的一个好处是辅助索引不包含整行记录的所有信息,故其大小要远小于聚集索引,因此可以减少大量的IO操作。
- 最左匹配:最左匹配可以是字符串索引的最左 N 个字符,也可以是联合索引的最左 M 的字段。避免前后模糊匹配使得 SQL 语句无法使用到索引。
- 索引下推:直接取辅助索引的值进行过滤,即数据库在取出索引的同时,会根据 where 条件直接过滤掉不满足条件的记录,减少回表次数。
- MRR 优化:Multi-Range Read Optimization,把二级索引结果放入缓冲区排序再去回表,减少磁盘的随机访问,并且将随机访问转化为较为顺序的数据访问。
联合索引
对表上多个列建索引。根据多个索引组合排序,查找时对于联合索引的前缀内容查找都能命中索引。
唯一索引和主键索引
InnoDB对聚簇索引处理如下:
- 如果定义了主键,那么InnoDB会使用主键作为聚簇索引
- 如果没有定义主键,那么会使用第一非空的唯一索引(NOT NULL and UNIQUE INDEX)作为聚簇索引
- 如果既没有主键也找不到合适的非空索引,那么InnoDB会自动生成一个不可见的名为ROW_ID的列名为GEN_CLUST_INDEX的聚簇索引,该列是一个6字节的自增数值,随着插入而自增
很明显,缺少主键的表,InnoDB会内置一列用于聚簇索引来组织数据。而没有建立主键的话就没法通过主键来进行索引,查询的时候都是全表扫描,小数据量没问题,大数据量就会出现性能问题。
唯一索引和普通索引
- 普通索引查找到满足条件的第一个记录后,还会继续去查找下一个记录,直到碰到第一个不满足该条件的记录;
- 而对于唯一索引来说,一旦找到一个满足条件的记录后,就会立即停止继续检索。
常见的索引失效场景
- 范围查询(>,<,<>)
- 查询条件类型不一致
- 查询条件使用了函数
- 模糊查询
- 不使用组合索引的首个字段当条件
为什么会失效?
- 顺序读比离散读性能要好:不使用覆盖索引的情况下,优化器只有在数据量小的时候才会选择使用非聚集索引。数据量大时,通过聚集索引顺序读数据行的性能会比通过非聚集索引离散读数据行要好(如读取整行数据)。
- 无法使用 B+索引快速查找。具体如上 5 点描述。
精简一下上述内容,根本原因是:
- 全局扫描的效率高于建立索引
- 索引涉及强制的类型转换
- 索引上做相关的运算操作
具体表现:
(1) 组合索引未使用最左前缀,例如组合索引(A,B),where B=b不会使用索引;
(2) like未使用最左前缀,where A like ‘%China’;
(3) 搜索一个索引而在另一个索引上做order by,where A=a order by B,只使用A上的索引,因为查询只使用一个索引 ;
(4) or会使索引失效。如果查询字段相同,也可以使用索引。例如where A=a1 or A=a2(生效),where A=a or B=b(失效)
(5) 如果列类型是字符串,要使用引号。例如where A=‘China’,否则索引失效(会进行类型转换);
(6) 在索引列上的操作,函数(upper()等)、or、!=(<>)、not in等;
名词解释
- 主键索引:顾名思义该类索引由表的主键组成,从左到右由小到大排序。一个 Innodb 存储表只有一张主键索引表(聚集索引)。
- 普通索引:最为平常的一种索引,没有特别限制。
- 唯一索引:该索引的字段不能有相同值,但允许有空值。
- 组合索引:由多列字段组合而成的索引,往往是为了提升查询效率而设置。