携带足够列、能独自满足整条查询的索引

InnoDB 覆盖索引包含查询所需的列

覆盖索引包含某条查询求值和返回结果所需的列。在 InnoDB 中,二级索引记录还会携带主键列,因此即使二级索引定义没有再次列出主键,某条查询也可能已经被覆盖。覆盖描述的是一条具体查询与某个索引之间的关系,不是给索引贴上标签之后,整张表的所有查询都自动获得同样行为。 坏掉的样子 增加返回列可能使覆盖失效。原本能提供标识和时间戳的索引,不一定包含应用后来开始读取的大载荷。优化器仍然可能沿用这个索引定位匹配记录,再访问聚簇记录取得缺失字段。因此,“用到了索引”和“由索引覆盖查询”是两件不同的事,不能从计划中出现索引名字就直接推导后者。 覆盖也不保证访问范围足够小。一个扫描大量条目的查询,可以完全被索引覆盖,仍然花费很多工作。列顺序、过滤条件、排序要求和行数估计都很重要。不要把 Using index 覆盖标记当作查询很快的证明,也不要把它与 Using index condition 索引条件下推标记混淆;后者描述部分条件在索引层求值,并不证明整个返回投影都在索引里。 InnoDB 的可见性检查还会限制通常所说的“只读索引”解释。二级记录不包含与聚簇记录相同的隐藏事务元数据。遇到删除标记条目,或被更新事务修改过的二级索引页时,可能仍需访问聚簇记录才能获得正确可见版本。因此,在并发多版本活动下,一个覆盖计划并不普遍保证聚簇访问次数永远为零。 快速验证 准备一台新的 MySQL 8.4 实例,使用能够创建测试模式的客户端会话。本次实际运行版本是 MySQL 8.4.11。确认实验模式名尚未使用,遇到非预期 SQL 错误就停止,并保持同一个会话,以保留当前选中的数据库。下面建立一万行数据,其中一百笔已付款订单,并让载荷列明显比索引所需字段更宽: CREATE DATABASE xbcatm_index_lab; USE xbcatm_index_lab; CREATE TABLE orders ( id INT PRIMARY KEY, status VARCHAR(10) NOT NULL, created_at DATETIME NOT NULL, payload VARCHAR(1000) NOT NULL, INDEX by_status_created (status,created_at) ) ENGINE=InnoDB; INSERT INTO orders WITH digits(n) AS ( SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 ) SELECT number+1,IF(MOD(number+1,100)=0,'paid','open'), TIMESTAMP '2026-01-01 00:00:00' + INTERVAL (number+1) SECOND, REPEAT('x',640) FROM (SELECT a.n+10*b.n+100*c.n+1000*d.n AS number FROM digits a CROSS JOIN digits b CROSS JOIN digits c CROSS JOIN digits d) AS numbered; ANALYZE TABLE orders; SELECT COUNT(*),SUM(status='paid') FROM orders; 最后的两个计数应为一万与一百。二级索引明确包含状态与创建时间,InnoDB 隐式提供主键标识。先检查并运行只需要这些值的查询: ...

三月 3, 2028 · 2 分钟 · XB Field Notes