《高性能MySQL》记录

创建高性能的索引

索引基础

B-Tree 索引

InnoDB 引擎的默认索引类型是 B+ Tree,按照原数据格式进行存储。

B-Tree 意味着所有的值都是按顺序存储的,并且每一个叶子到根的距离相同。B-Tree 索引能够加快访问数据的速度,通过根节点的指针不断向下比较查找,最终存储引擎要么找到对应的值,要么记录不存在。B-Tree 对索引列的顺序存储结构适合查找范围数据,如全值匹配、匹配最左前缀、匹配范围值等。除了按值查找,索引还可以用于 ORDER BY 操作。

B-Tree 索引存在以下限制:

  • 如果不是按照索引的最左列开始查找,则无法使用索引。对于复合索引(A,B,C),查询条件必须包含A列才能有效使用该索引。如果查询只使用B或C列,索引通常不会被使用。
  • 不能跳过索引中的列,必须按照索引定义的列顺序使用。
  • 如果查询中有某个列的范围查询,则其右边所有列都无法使用索引优化查询。

哈希索引

哈希索引是基于哈希表实现的索引类型,通过哈希函数将索引列值转换为哈希码来快速定位数据行。在MySQL中,只有MEMORY存储引擎显式支持哈希索引,并且这是MEMORY引擎的默认索引类型。

哈希索引是无序存储,而且只支持精确匹配。哈希索引的限制如下:

  • 不支持范围查询,如><BETWEEN等。
  • 不支持部分列匹配,必须使用索引的所有列才能生效,因为哈希索引始终是使用索引项的全部内容来计算哈希值的。
  • 哈希冲突问题,不同值可能产生相同哈希值,需要额外处理。

InnoDB 有一个自适应哈希索引,InnoDB 引擎会自动监控查询模式,对频繁访问的索引值创建自适应哈希索引。这实际上是 B-Tree 索引上的优化层,使用哈希值而非键本身进行查找。

空间数据索引(R-Tree)

空间数据索引是一种专门用于处理多维空间数据(如地理坐标、几何图形等)的索引技术,主要解决传统 B-Tree 索引无法有效处理空间数据的问题。

全文索引

全文索引是专门为文本内容搜索设计的索引技术,通过建立倒排索引实现高效文本检索。

高性能的索引策略

独立的列

索引列必须作为独立的表达式存在,不能参与计算或作为函数参数,否则索引会失效。MySQL 无法解析包含运算或函数的表达式,必须将列单独提取为查询条件,才能触发索引优化。

前缀索引与索引选择性

针对长字符串或文本类型,通过截取前 N 个字符建立索引,减少存储空间和 I/O 消耗。

关于如何选择合适的前缀长度,可以通过计算选择性进行评估:

1
SELECT COUNT(DISTINCT LEFT(column, N)) / COUNT(*) FROM table;  

选择接近完整列选择性的最短前缀(如对邮箱地址反转后建前缀索引)。

复合索引与最左前缀原则

最左前缀原则:查询必须从复合索引的最左列开始,且不能跳过中间列。示例:索引 (A,B,C) 支持 A=1 AND B=2 ,但不支持 B=2B=2 AND C=3 。使用复合索引时要注意范围查询限制,若某一列使用范围查询,其右侧的列无法使用索引优化。

关于复合索引的列选择,选择性高的列放左侧、优先筛选更多数据。若需 ORDER BY A,B ,索引顺序应为 (A,B)

聚簇索引

聚簇索引并非独立的索引类型,而是 InnoDB 的数据存储方式,将 B-Tree 索引和数据行合并存储在同一个结构中。数据行按主键顺序物理存储,叶子节点包含完整数据。聚簇索引优先选择自增主键,若无主键,选择首个唯一非空索引。两者均无时,InnoDB 隐式创建包含 rowid 的聚簇索引。

聚簇索引有以下优点:

  • 数据局部性:相关数据(如用户ID关联的邮件)物理相邻存储,减少磁盘 I/O 。
  • 查询性能:索引与数据共存于 B-Tree,避免二次查找。覆盖索引扫描可直接利用节点中的主键值。

聚簇索引的缺点:

  • 同时也导致了插入和更新的代价比较高:

    • 主键顺序插入最快,乱序插入易引发页分裂,需 OPTIMIZE TABLE 优化。

    • 更新聚簇索引列会强制移动行位置,增加 I/O 开销。

  • 存储问题:

    • 页分裂导致空间碎片化,全表扫描效率降低。
    • 二级索引体积较大,需回表查询。

覆盖索引

索引包含查询所需的所有字段,无需回表,显著减少I/O和锁竞争。高频查询字段较少时,可将这些字段加入索引。InnoDB的二级索引会隐式包含主键值,因此若查询包含主键和索引列,也可能触发覆盖索引。通过 EXPLAINExtra 列显示 Using index 时即表示使用了覆盖索引。

使用索引扫描排序

MySQL可通过两种方式排序:

  1. 文件排序(Filesort):临时磁盘排序,性能较差。
  2. 索引扫描排序:按索引顺序直接返回结果,EXPLAINtype 列为 index

要使用索引扫描排序,必须满足以下所有条件:

  • 列顺序一样:ORDER BY 子句的列顺序与索引定义完全一致。
  • 排序方向一样:所有列均为升序(ASC)或降序(DESC),混合方向需文件排序。
  • 最左匹配原则。

当出现以下情况时会失效:

  • 范围查询。
  • 包含非索引列。
  • 多表关联:仅当 ORDER BY 列全部来自关联的第一个表时才生效。

把索引压缩

通过压缩算法减少索引占用的存储空间,主要应用于MyISAM引擎(InnoDB的压缩机制不同):

  • 压缩前缀索引:默认仅压缩字符串,通过截取列值的前缀减少空间。
  • 整数压缩:需手动启用,对整数值进行差分编码或位压缩。

压缩索引可以节省存储同时提高 I/O 吞吐量,但是解压会增加计算负担(不适合 CPU 计算密集型场景),同时还会增加索引的更新代价。

冗余索引和重复索引

概念:

  • 重复索引:在相同列上按相同顺序创建的相同类型索引(如对列 A 同时创建两个 B-Tree 索引)。MySQL 需单独维护每个重复索引,优化器需逐个评估,影响性能。

  • 冗余索引:若已有联合索引 (A,B) ,再创建 (A) 则为冗余索引(因 (A,B) 可替代 (A) 的功能)。但 (B)(B,A) 不冗余。

这两种索引增加存储占用,且额外增加更新代价,降低性能。

若扩展已有索引会导致索引过大(如从 INT 列扩展为 INT+VARCHAR ),可保留冗余索引以兼顾不同查询性能。

索引和锁

InnoDB 行锁依赖索引:锁定的行通过索引定位。若查询使用索引,仅锁定符合条件的行;否则退化为全表扫描并锁定更多行。

锁类型:

  • 记录锁(Record Lock):锁定索引中的具体记录。
  • 间隙锁(Gap Lock):在可重复读(RR)隔离级别下,防止其他事务在索引间隙插入数据(解决幻读)。

性能影响:

  • 索引优化锁粒度:有效索引减少锁定行数,降低锁竞争,提高并发性。
  • 无索引的代价:若 WHERE 条件无法利用索引,InnoDB 会锁定全表或大量行,导致阻塞。

查询性能优化

MySQL 高级特性

优化服务器设置

复制