阿钊的写字板

专注于 Java 技术栈与分布式系统实践,分享技术难题攻克、系统优化实战与信创项目开发经验。

【高性能MySQL:第五章节(创建高性能的索引)】学习小节

本章讨论了索引,索引是数据库的物理设计,对于索引的深入理解和利用是高效使用 MySQL 的基础。

问题一:什么是索引、索引有哪些好处和缺点?

  • 什么是索引:
    索引是存储引擎为了快速找到记录的一种数据结构。可以简单的把索引理解为书的目录、大楼的楼层平面索引图,可以通过目录快速了解这本书讲的是什么、通过大楼的楼层平面索引图知道这栋大楼里有什么、以及我们如何到达那里。
  • 索引的优点:
    索引最大的好处就是可以快速获取到需要数据记录,因为索引是存储引擎的工作,所以每个存储引擎的索引机制都不同,根据 InnoDB 的 B-Tree 索引的优势,有下面三个优点:
  1. 减少需要扫描的数据量:因为 B-Tree 数据结构的优势,可以通过非叶子节点的 key 来选择合适的节点指针从而快速找到需要的叶子节点的数据指针获取数据,以此来达到减少随机 I/O 读取的次数,同时叶子节点的数据是根据链表的形式顺序存储的,所以对于顺序 I/O 也有较高的性能;
  2. 避免排序和临时表:因为叶子节点的数据是顺序存储的,所以对于 Order By 之类的操作也是可以使用顺序 I/O来提高查询效率,避免重复排序的问题;
  3. 可以将随机读写变为顺序读写:因为通过 key 在非叶子节点来找到叶子节点位置的,这个过程被称为随机读写,但是当找到叶子节点后叶子节点的数据是顺序存储的,所以随机读写会变为顺序读写,以此来提高查询效率;
  • 索引的缺点:
    • 索引的优点是可以提高数据的快速检索速度,但前提是要遵循索引的使用原则:最左前缀原则、索引顺序、选择合适的索引类型,否则会适得其反,甚至降低查询效率。
    • 对于数据量很小的表全表扫描可能效率会比走索引效果更好。对于几十万、百万级别的数据量索引的优势才能发挥的出来,对于千万、亿级别的数据量使用分区技术可能会更合适。
    • 查询中索引的列是一个表达式或者函数的参数,则无法使用索引,例如:where id + 1 =2或者 tochar(col_name);

问题二:MySQL 中支持哪些索引?这些索引的优缺点和适用场景有那些?
MySQL 中支持的索引有:B-Tree 索引、Hash 索引、空间数据索引(R-Tree)、全文索引、聚簇索引、覆盖索引。

  • B-Tree 索引
    使用 B-Tree 结构来存储数据。
    特点:只有叶子节点(三级节点)按照链表结构顺序存储所有数据,非节点是叶子节点的数据指针,用于通过层级索引的方式来提高查询效率。非叶子节点是其子节点的数据范围的最大值,数据范围永远是左边比右边小、右边比左边大,且从根节点到叶子节点的层次深度相同。
    优点:叶子节点按顺序存储,叶子节点到根节点的距离相同,查询效率高;
    缺点:当主键键值是数值类型且使用AUTO_INCREMENT来进行自增时是新增、修改、删除效率比较高。但当主键是 UUID 时因为其是随机的,在插入时如果数据页已经满了,需要通过数据页分裂来存储数据,此时会造成较大的性能开销,尤其是当插入主键是随机的大量数据时。
    适应场景:
  1. 全值匹配:全值匹配指的是和索引中的所有列进行匹配;
  2. 匹配最左前缀:匹配以指定 key 开头的记录,也就是是当索引存在多个字段时只匹配第一列;
  3. 匹配列前缀:只匹配列的值开头的部分,比如:like ‘d%’ 可以,like ‘%d’ 不行;
  4. 匹配范围值:在指定 key 之间的范围数据,要符合最左前缀原则;
  5. 索引查询:覆盖索引场景;
  • Hash 索引:Hash 索引基于 Hash 表实现,只有精确匹配的列 Hash 索引才有效。
    新增逻辑:
  1. 对索引列进行 Hash 计算,获取 Hash 码;
  2. 将 Hash 码以 key 的形式保存到索引文件中,value 值是每个数据行的物理位置指针;
    注意:Hash 索引只包含 Hash 值和行指针,而不存储字段值,只有 Hash 码是顺序存储的,数据行不是。
    查询逻辑:
  3. 先对where 查询的字段值进行 Hash 计算,获取 Hash 值;
  4. 使用获取的 Hash 值去索引文件中查询对应的记录指针,当出现 Hash 冲突时,存储引擎必须遍历链表中的所有行指针,逐行比较直到找到所有符合条件的行记录;
  5. 如果找到了指定的行记录指针,比较指针记录是否为指定值,以此来验证是需要查找的记录;
    优点:读取等值比较(=、in)查询效率极高;
    缺点:不支持范围查询和排序,且查询使用的是索引列的全部字段而非指定字段。当数据量大时,存在 Hash 冲突的问题。解决 Hash 冲突的办法就是在 where 查询条件中带入 Hash 值和对应的列值,这样当出现 Hash 冲突时语句的条件不成立,就能避免问题;
  • 空间数据索引(R-Tree)
    MyISAM表支持空间索引,可以用作地理数据存储。和B-Tree索引不同,这类索引无须前缀查询。空间索引会从所有维度来索引数据。查询时,可以有效地使用任意维度来组合查询。
    实际开发过程中没有使用过。

  • 全文索引
    全文索引是一种特殊类型的索引,它查找的是文本中的关键字,而不是直接比较索引中的值。全文索引更类似于搜索引擎做的事情,而不是简单的 where 条件匹配。
    一般情况下不会使用全文索引,如果有需要全文索引的场景,更多会将查询数据抽取到 ES 这种搜索框架去实现。

  • 聚簇索引
    聚餐索引不是一个索引类型,而是一种数据存储方式。当表中有聚簇索引时,它的数据行实际上存放在索引的叶子页上。聚簇索引就是在同一个表结构中保存了B-Tree 的索引和数据,因为 B-Tree 的节点页(非叶子节点)只包含了索引列,叶子页包含了行的全部数据。所以一个表只能有一个聚簇索引。
    InnoDB 通过主键聚集数据,也就是说被索引的数据基本上就是主键列。如果没有定义主键,InnoDB 会选择一个唯一的非空索引代替。如果没有这样的索引,InnoDB 会隐式定义一个主键来作为聚簇索引。
    优点:

  1. 可以把索引和数据保存在一起,这样可以便于覆盖索引的查询;
  2. 数据访问更快。聚簇索引将索引和数据保存在同一个B-Tree中,因此从聚簇索引中获取数据通常比在非聚簇索引中查找要快。
    缺点:如果主键不是顺利的在插入时可能会导致页分裂问题,此时聚簇索引的插入会导致性能问题。
  • 覆盖索引
    如果一个索引包含了所有需要查询的字段的值,我们就称之为覆盖索引;
    优点:提高读取效率:因为索引比数据更小,所以如果只需要读取索引,那么 MySQL 就会极大地减少数据访问量。其次索引是按照列值顺序存储的,所以范围查询会比从磁盘中 逐个读取每一行的 I/O次数要少的多。使用聚簇索引时,二级索引在叶子节点中保存了行的主键值,如果二级主键能够覆盖查询,则可以避免对主键索引的二次查询。
    覆盖索引可能失效的情况:
  1. 索引列的数量小于查询列的数量,导致没有索引可以覆盖这个查询,且查询列要符合最左前缀原则;
  2. 如果在索引中执行了前缀以 % 的like 操作,则无法匹配索引;

问题三:怎么知道索引是不是有效的?如何避免索引失效的问题?

  1. 避免冗余和重复的索引(可以使用Percona Toolkit的相关工具),如果需要建立同类型的索引,首先考虑的应该是扩展已有的索引;
  2. 删除没有使用的索引,但对于唯一索引要慎重,因为唯一索引是为了保证数据的完整性;
  3. 可以通过 alter table 重建表来重建相关索引,只需要将表的存储引擎修改为当前的引擎即可;

问题:

  • 什么是临时表,为什么需要临时表,为什么索引和临时表有关?
  • 对于大型系统,元数据信息表的技巧是如何使用的?
  • 唯一索引是不是 B-Tree 索引?它是如何实现的?
  • B-Tree 的叶子节点已经有了所有的行记录,为什么还需要聚簇索引?