InnoDB 存储引擎的索引

一、InnoDB 索引分类

索引是在存储引擎层实现的,而不是在服务器层实现的,所以不同存储引擎具有不同的索引类型和实现。

如果索引太多,应用程序的性能可能会受到影响。而索引太少,也会对查询性能产生影响。

InnoDB 支持三种索引:B+树索引哈希索引全文索引

一般机械硬盘每秒至少可以做100次IO,每次约需要0.01秒。

可以使用EXPLAIN分析SQL语句的执行情况。

B树和B+树的对比

1. B+ 树索引

是大多数 MySQL 存储引擎的默认索引类型。因为不再需要进行全表扫描,只需要对树进行搜索即可,所以查找速度快很多。除了用于查找,还可以用于排序分组范围查找。可以指定多个列作为索引列,多个索引列共同组成键,即联合索引

适用于全键值键值范围键前缀查找,其中键前缀查找只适用于最左前缀查找。如果不是按照索引列的顺序进行查找,则无法使用索引。

InnoDB 的 B+Tree 索引分为主索引辅助索引

主索引叶子节点 data 域记录着完整的数据记录,这种索引方式被称为聚簇索引。因为无法把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引

聚簇索引最大限度地提高了IO密集型应用的性能,如果数据都在内存中,则访问顺序没那么重要,聚簇索引也就没有优势了。

实际上这里B+树索引并不能找到一个给定键值的具体行,而是找到数据行所在的。然后数据库通过把页读入内存,再在内存中进行查找。
在这里插入图片描述
上图中B+树高度为2,每页可存放2条记录,扇出(指向3个下层节点)为3,实际上叶子应该是双向链表,首尾相连。

InnoDB 聚簇索引的选择

  • 有主键时,根据主键创建聚簇索引
  • 没有主键时,会用第一个唯一且非空的索引列做为主键,成为此表的聚簇索引
  • 如果以上两个都不满足,那么InnoDB内部会生成一个隐藏的主键作为聚簇索引,这个隐藏的主键是一个6字节的列,该列的值会随着数据的插入自增。

InnoDB 索引的规则

  • InnoDB自增长的列只能有一个并且必须是索引,同时必须是索引的第一个列(如果是联合索引)。
  • 对于一个外键列,如果没有显式地对这个列加索引,InnoDB会自动对其加一个索引

辅助索引(非聚簇索引) 的叶子节点记录着主索引的主键的值和辅助索引的key值,因此在使用辅助索引进行查找时,需要先查找到主键值,然后再到主索引中进行查找。为什么存放的主键,而不是记录所在地址呢,因为记录所在地址可能会变,但主键很少变。

如果辅助索引对应多个主键值,则并列在叶子节点,即 (a,1), (a,2), (a,3),(b,2)这样。
在这里插入图片描述

聚簇索引要比非聚簇索引查询效率高很多,特别是范围查询排序查找的时候。当辅助索引范围查找比较大时(一般20%),会直接通过聚簇索引来全表扫描查找数据(顺序读比较快)。

聚簇索引的创建和删除需要重建一张表,效率比较低,对辅助索引的创建和删除无需重建表。

2. 哈希索引

哈希索引能以 O(1) 时间进行查找,但是失去了有序性

  • 无法用于排序分组
  • 只支持精确查找,无法用于部分查找范围查找

InnoDB 存储引擎有一个特殊的功能叫“自适应哈希索引”,当某个索引值被使用的非常频繁时,InnoDB 会在 B+Tree 索引之上再创建一个哈希索引,这样就让 B+Tree 索引具有哈希索引的一些优点,比如快速的哈希查找。

InnoDB 的哈希索引采用拉链法处理冲突。

InnoDB 生成哈希索引的过程都是自动的,不能人为干预

3. 全文索引

InnoDB 存储引擎在 MySQL 5.6.4 版本中也开始支持全文索引

全文索引,用于查找文本中的关键词,而不是直接比较是否相等。

查找条件使用 MATCH AGAINST,而不是普通的 WHERE。

全文索引使用倒排索引实现,它记录着关键词到其所在文档的映射。

-> 倒排索引-百度百科

二、和 MyISAM 索引的对比

MyISAM 支持 B+树索引全文索引空间数据索引

1. MyISAM 和 InnoDB B+树索引的区别

MyISAM 的索引都是非聚集的,主键和非主键的区别只是是否唯一且非空。MyISAM的行标识符可以用“文件号:页号:槽号”来定位实际数据行。

InnoDB 的索引分聚集非聚集。如果单纯比较MyISAM 和 InnoDB的非聚集索引,MyISAM 会更快一些。

MyISAM中的索引文件和数据文件是分开的,数据按照插入顺序存放。因此排序范围查找要比InnoDB慢很多。

InnoDB 的存储引擎表是索引组织表,即表中的数据按照主键的顺序存放,聚集索引就是按照每张表的主键构造一颗B+树,叶子节点中存放的即为整张表的行记录数据,也将聚集索引的叶子节点称为数据页,每个数据页都通过双向链表进行连接。进行排序范围查找速度很快。

聚簇索引的存储并不是在物理上连续的,而是在逻辑上连续,即通过双向链表访问可以有序。

在这里插入图片描述

MySQL中MyISAM和InnoDB的索引方式以及区别与选择

2. 空间数据索引

MyISAM 存储引擎支持空间数据索引(R-Tree),可以用于地理数据存储。空间数据索引会从所有维度来索引数据,可以有效地使用任意维度来进行组合查询

必须使用 GIS 相关的函数来维护数据。

三、Cardinality 值

Cardinality 值表示索引中唯一值的数目的估计值(采样估计)。Cardinality/总行数应尽可能的接近1,如果非常小,就没有有必要创建这个索引。

优化器会根据这个值来判断是否使用这个索引,优化器还会根据其他条件决定是否使用使用哪个索引。

可以使用ANALYZE TABLE命令更新Cardinality 值,建议在非高峰时期更新。

在这里插入图片描述

四、索引优化

最好设置InnoDB的主键为自增,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。

1. 独立的列

在进行查询时,索引列不能是表达式的一部分,也不能是函数的参数,否则无法使用索引。

例如下面的查询不能使用 actor_id 列的索引:

SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;
2. 联合索引(多列索引)

本质上联合索引也是一颗B+树,不同的是联合索引的键值的数量大于等于2,索引首先按照最左列进行排序,其次是第二列,等等。

在需要使用多个列作为条件进行查询时,使用联合索引可能比使用多个单列索引性能更好。如果某列具有足够好的选择性,则无需建联合索引,对该列建索引就好。

例如下面的语句中,可以把 actor_id 和 film_id 设置为联合索引

SELECT film_id, actor_ id FROM sakila.film_actor
WHERE actor_id = 1 AND film_id = 1;

比如上面这条语句,对单个actor_id列查询也可以使用联合索引,而对单个film_id列查询则不能用联合索引了,因为相同film_id值的键不在一起。

当第一个key相同时,联合索引会根据第二个索引排序。如果查询需要根据第二个键值排序则无需另外排序。

在这里插入图片描述

比如对联合索引(a, b, c)来说

select ... from table where a=xxx order by b;   
select ... from table where a=xxx and b=xxx order by c;
select ... from table where a=xxx order by b DESC, c DESC;
# 上面三句无需再进行排序
select ... from table where a=xxx order by c;
select ... from table where a>xxx order by b; # a 涉及范围查询,无法使用索引的排序
select ... from table where a=xxx order by b DESC, c ASC;
# 上面三句需要再排序

索引列都是正序排序的,只有当索引的列顺序和ORDER BY子句的顺序完全一致,并且所有列的排序方向(倒序或正序)都一样,才可以使用索引而无需再排序。另外,如果某列涉及范围条件,也无法使用索引的排序。

还是对联合索引(a, b, c)来说

select ... from table where a>xx;  # 可以利用索引进行范围查找
select ... from table where a=xx and b between 30 and 50; # 可以利用索引进行范围查找
select ... from table where a=xx and b=88 and c > 40; # 可以利用索引进行范围查找
select ... from table where a=xx and c > 40; # 不能利用索引进行范围查找
select ... from table where c > 40; # 不能利用索引进行范围查找

如上面的例子所示,一般使用正确的情况下也只能对一个属性进行范围查找(利用索引)。如果想对两个属性进行范围查找,尽量将其中一个属性变为多个等值条件查询,比如b in (29,30,31)

联合索引有联合主键索引(主索引)、联合普通索引(辅助索引)。

联合索引的案例学习可以参考《高性能MySQL》的5.4节。

3. 联合索引列的顺序

不需要考虑排序分组时,一般让选择性最强的索引列放在前面

如果每次查询的字段不一定,选择性较弱经常出现的X字段一般也放在前面。如果把选择性强不经常出现的Y字段放在第一个,如果查询没有Y字段则整个联合索引都用不了了。

索引的选择性是指:不重复的索引值和记录总数的比值。最大值为 1,此时每个记录都有唯一的索引与其对应。选择性越高,查询效率也越高。

例如下面显示的结果中 customer_id 的选择性比 staff_id 更高,因此最好把 customer_id 列放在联合索引的前面

SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity,
COUNT(DISTINCT customer_id)/COUNT(*) AS customer_id_selectivity,
COUNT(*)
FROM payment;
staff_id_selectivity: 0.0001
customer_id_selectivity: 0.0373
               COUNT(*): 16049
4. 前缀索引

对于 BLOB(binary large object,二进制大对象)、TEXT 和 VARCHAR 类型的列,必须使用前缀索引,只索引开始的部分字符。

对于前缀长度的选取需要根据索引选择性来确定。

前缀索引的缺点:无法使用前缀索引做ORDER BYGROUP BY

5. 覆盖索引

辅助索引中就可以得到查询的记录,而不需要查询聚集索引中的记录。(MySQL 5.0 以上支持覆盖索引)

  • 索引通常远小于数据行的大小,只读取索引能大大减少数据访问量。
  • 对于 InnoDB 引擎,若辅助索引能够覆盖查询,则无需访问聚簇索引辅助索引不包含整行记录的所有信息,故大小远小于聚簇索引,减少大量IO操作。
  • 一些存储引擎(例如 MyISAM在内存中只缓存索引,而数据依赖于操作系统来缓存。因此,只访问索引可以不使用系统调用(通常比较费时)。

在这里插入图片描述

6. 冗余的索引

在这里插入图片描述
在这里插入图片描述

7. Multi-Range Read 优化

MySQL 5.6 开始支持MRR优化。MRR优化的目的是为了减少磁盘的随机访问,并且将随机访问转化为较为顺序的数据访问。InnoDB和MyISAM的范围查询JOIN查询操作会使用。

MRR优化的好处:

  • MRR 使数据访问变得较为顺序。在查询辅助索引时,首先根据得到的查询结果,按照主键进行排序,并按照主键排序的顺序进行书签查找。
  • 减少缓冲区中页被替换的次数。
  • 批量处理对键值的查询操作。
8. Index Condition Pushdown 优化

ICP 优化也是MySQL 5.6 开始支持的一种根据索引进行查询的优化方式。

在支持ICP后,MySQL 数据库会在取出索引的同时,判断是否可以进行WHERE条件的过滤,也就是将WHERE的部分过滤操作放在了存储引擎层。

五、索引的优点
  • 大大减少了服务器需要扫描的数据行数。

  • 帮助服务器避免进行排序和分组,以及避免创建临时表(B+Tree 索引是有序的,可以用于 ORDER BYGROUP BY 操作。临时表主要是在排序和分组过程中创建,因为不需要排序和分组,也就不需要创建临时表)。

  • 将随机 I/O 变为顺序 I/O(B+Tree 索引是有序的,会将相邻的数据都存储在一起)。

六、索引的使用条件
  • 对于非常小的表、大部分情况下简单的全表扫描比建立索引更高效;

  • 对于中到大型的表,索引就非常有效;

  • 但是对于特大型的表,建立和维护索引的代价将会随之增长。这种情况下,需要用到一种技术可以直接区分出需要查询的一组数据,而不是一条记录一条记录地匹配,例如可以使用分区技术

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值