美文网首页
MySQL:索引

MySQL:索引

作者: HYIndex | 来源:发表于2021-04-04 23:29 被阅读0次

索引的底层实现

InnoDB存储引擎数据结构使用B+树

B+树

B+数据的基本结构如下图


为什么选用B+树

MySQL为什么要选B+树作为存储结构呢,与B树相比有哪些优点?

1. 减少磁盘访问,提高查询效率
B+树非叶子节点上是不存数据的,仅存键值,而B树节点中不仅存储键值,也会存储数据。因为数据页的大小是固定的(InnoDB中页的默认大小是16KB),如果不存储数据,那么就会存储更多的键值,相应的树的阶数N就会更大,树高就会越低,这样查询数据进行磁盘IO的次数就会大大减少,数据查询的效率也会更快。
以InnoDB的一个整数字段索引为例,阶数N大概是1200,这棵树高是4的时候,就可以存1200^3(约17亿)个值,因为根节点总是在内存中的,一个10亿行的表上一个整数字段的索引,查找一个值最多只需要访问3次磁盘。

2. 提高范围查找效率
因为B+树的所有数据均存储在叶子节点,而且是有序的,使得B+树范围查找,排序查找,分组查找以及去重查找变的简单,而B树的数据分散在各个节点上,实现起来比较困难。

普通索引和唯一索引如何选择?

普通索引不需要保证一条记录的唯一性,查询和更新操作都不需要保证数据页已经读到内存中,相反唯一索引为了保证唯一性,更新时必须要保证数据页在内存中,需要检查是否满足唯一性

查询操作的区别

  • 普通索引:查找到满足条件的第一个记录后,需要查找下一条记录,直到碰到不满足的记录
  • 唯一索引:查找满足条件的第一个记录就会停止检索

因为是innoDB的读写操作是以数据页为单位的,通常情况目标记录的下一个记录也会在内存中,对于普通索引来说,只是多了一次判断操作,这个CPU成本可以忽略不计,如果是目标记录恰好在某页的最后,下一条记录需要从磁盘中读取,这个I\O成本会大一些,但是这种情况出现的概率很低。
所以对于查询操作来说,唯一索引更快,但是性能差异非常小。

更新操作的区别

change buffer

当更新一个数据页时,如果数据页在内存中就直接更新,如果数据页还没有在内存中,在不影响数据一致性的前提下,InnoDB会将这些更新操作缓存再change buffer中,这样就不用从磁盘中读入数据了,大大提高了更新操作的性能。InnoDB会在下次访问这个数据页的时候将数据页读入内存然后执行change buffer中与这个页有关的操作,保证数据的最终一致性。

change buffer是可持久化的数据,也会被写到磁盘中,写入change buffer操作也会记录在redo log中。

merge:将change buffer中的操作应用到原数据页的过程称为merge,merge除了在查询操作时会触发,系统后台有线程会定期merge,数据库正常关闭(shut down)时也会执行merge操作。

优点

  • 减少读磁盘,明显提升更新操作的速度
  • 数据读入内存会占用buffer pool,可以减少内存使用,提高内存利用率

使用条件

  • 唯一索引的更新操作需要判断唯一性约束,必须将数据读到内存中才能判断,因此唯一索引的更换不能使用
  • 只有普通索引可使用
  • change buffer使用的是buffer pool中的内存,因此不能过大。

应用场景

  • 写多读少的业务,如账单、日志类的系统

如果业务更新后马上会做查询,那么merge的操作会被触发,这样随机访问磁盘的次数不会减少还增加了change buffer的维护代价,反而起到了反作用。

索引的选择

  • 在业务保证唯一性的前提下,尽量选择普通索引。
  • 如果更新后面马上伴随这查询,应该关闭change buffer

change buffer和redo log

使用change buffer的更新语句执行的过程:

  1. 如果数据页在内存中,直接更新内存
  2. 如果数据页不在内存中,在change buffer中记录更新操作
  3. 将1或2的动作记录在redo log中

区别

  • redo log主要是节省随机写磁盘的IO消耗(转为顺序写)
  • change buffer主要节省随机读磁盘的IO消耗

为什么MySQL优化器会选错索引

优化器选择索引的目的是找一个最优的方案,并用最小的代价去执行语句,扫描行数是影响执行速度的代价之一,扫描行数越少,意味着访问磁盘数据越少,消耗的CPU资源也越少(扫描行数并不是唯一判断标准,还会结合是否使用临时表、是否排序等因素进行综合判断)。
在不涉及临时表和排序的情况下,选错索引肯定是在判断扫描行数的时候出错了

扫描行数如何计算的

执行语句前MySQL并不能精确的知道这个条件的记录有多少条,只能根据统计信息来估算扫描记录数。

索引的基数

一个索引上不同的值越多,这个索引的区分度就越好,而一个索引上不同的值的个数称为基数,基数越大说明区分度越好。

基数的计算

MySQL使用采样统计(选择采样而不是全表扫描是为了节省计算成本):

  • InnoDB默认会选择N个数据页,统计这些页面上的不同值得到一个平均值,然后乘以索引的页面数得到基数。
  • 数据表持续更新的过程中,当变更的数据行占比超过1/M的时候,会自动触发做一次索引统计

解决方案

  1. 当发现explain的结果预估的rows值跟实际差距比较大可以使用analyze table命令解决
  2. 使用force index()强行选择某个索引
  3. 优化SQL语句引导MySQL选择更合适的索引
  4. 新建一个更合适的索引

字符串前缀索引

给一个字符串字段上加索引有如下两种选择:

  1. 整个字符串加索引:alter table user add index idx_email(email);
  2. 前六个字符索引:alter table user add index idx_email(email(6));

优点

  • 前缀索引的索引结构只保存了前n个字符,索引占用的空间会更小
  • 使用前缀索引定义合适的长度,即可以节省空间,又不会增加太多查询成本

缺点

  • 增加了查询额外扫描次数,需要查找到所有前缀匹配的记录,每条记录都要回表查询完整数据进行判断。
  • 使用前缀索引会破坏覆盖索引(查询字段上都建了索引,不需要回表)对查询性能的优化

其他方式

  • 倒序存储加前缀索引:当字符串的前n为重复度高的情况
  • hash字段:添加一个hash字段,保存字符串字段的校验码(如crc32)

这两种方法都不支持范围查找,都会产生额外的cpu计算消耗,hash字段的查询性能更稳定,crc32计算的值冲突概率非常小。

独立索引

必须是独立的索引字段才能用到索引,在索引上使用函数、表达式都会导致不能使用索引树搜索,从而导致慢查询。

CASE1:在索引上使用函数

建表语句如下:

CREATE TABLE `tradelog` (
  `id` int(11) NOT NULL,
  `tradeid` varchar(32) DEFAULT NULL,
  `operator` int(11) DEFAULT NULL,
  `t_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `tradeid` (`tradeid`),
  KEY `t_modified` (`t_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

如果要查询几年内某个月的交易总数,查询语句可能如下:
select count(*) from tradelog where month(t_modified)=7;
索引上使用函数可能会导致其失去有序性,从而不能使用树搜索(不代表使用索引,可以在索引上遍历),即使没有改变索引的有序性优化器还是不能用索引快速查找,所以要避免这种写法。

CASE2:隐式类型转换

假如有如下语句:
select * from tradelog where tradeid=110717;
tradeid字段是varchar类型,如果要和数字作比较会将其转换为数字类型,对于优化器来说上述语句相当于:
select * from tradelog where CAST(tradid AS signed int) = 110717;
可以看到隐式的在索引字段上使用了函数,从而导致不能使用树搜索。

CASE3:隐式编码转换

如果在做连表查询是,驱动表和被驱动表的字段编码类型不一致,会导致索引不能使用树搜索。

参考资料

【极客时间】MySQL实战45讲:09、10、11节

相关文章

  • MySQL索引及查询优化书目录

    MySQL索引的原理之索引目的 MySQL索引的原理之索引原理 MySQL索引的原理之索引的类型 MySQL索引的...

  • 高性能的索引策略

    MySQL查询基础-查询执行过程 MySQL聚簇索引 MySQL覆盖索引 MySQL索引扫描排序 MySQL冗余和...

  • MySQL索引的使用

    MySQL索引 MySQL索引可以快速提高MySQL的检索速度。索引分单列索引和组合索引单列索引:即一个索引只包含...

  • Mysql索引与锁

    本文以Mysql5.7为例测试。 1:mysql索引方法 Mysql的索引方法分为btree索引和hash索引。 ...

  • 索引(二)

    mysql索引的新手入门详解mysql索引之三:索引使用注意规则 索引(Index)是帮助 MySQL 高效获取数...

  • MySQL 索引分类

    MySQL索引的分类(根据数据结构) 索引的本质 MySQL官方对索引的定义为:索引(Index)是帮助MySQL...

  • MySQL--索引

    MySQL索引 查看索引 创建索引 创建唯一索引 创建主键索引 删除索引 删除主键 MySQL视图 创建视图 删除...

  • mysql索引

    索引 mysql索引的建立对于mysql的高效运行是很重要的,索引可以大大提高mysql的检索速度。索引分单列索引...

  • 5.2MySQL创建高性能索引考察点

    MySQL索引的基础和类型延伸:MySQL索引的创建原则延伸:MySQL索引的注意事项 索引的基础索引类似于书籍的...

  • MySql 数据查询优化

    1. MySQL索引类型: mysql的索引有5种:主键索引、普通索引、唯一索引、全文索引、聚合索引(多列索引)。...

网友评论

      本文标题:MySQL:索引

      本文链接:https://www.haomeiwen.com/subject/fodrkltx.html