MySQL 数据库 9(Day57)

主题:索引查询进阶(等值/范围/运算/函数/隐式转换/模糊查询)、索引条件下推(ICP)、覆盖索引与回表、事务 ACID 特性及并发读现象

结构总览

MySQL 数据库 9
  ├─ B+树、聚集索引、辅助索引、覆盖索引、回表、联合索引复习
  ├─ 索引测试:选择性、结果集规模、字段长度与分页
  ├─ 查询条件对索引的影响:等值、范围、运算、函数、类型转换、LIKE
  ├─ 索引条件下推(ICP):索引层先过滤,减少不必要的回表
  └─ 事务:ACID、COMMIT/ROLLBACK、隔离级别与并发读现象

关键要点

InnoDB 索引与回表

  • B+树非叶子节点主要保存索引键和指针,叶子节点按顺序连接,适合等值、范围和排序查询。
  • 聚集索引叶子节点保存完整行;辅助索引叶子节点通常保存索引列和主键值。
  • 辅助索引先定位主键,再回到聚集索引取完整记录的过程称为回表。
  • 查询所需字段全部被索引覆盖时,可以直接从索引返回结果,称为覆盖索引。
  • 联合索引 (name, age, gender) 应遵循最左前缀;遇到范围条件后,右侧列通常不能继续缩小索引扫描范围。

索引选择与查询方式

索引不是越多越好。索引会占用磁盘空间,并增加 INSERT/UPDATE/DELETE 的维护成本。选择索引列时应结合非空性、选择性、基数、字段长度、查询频率和数据分布判断。

SELECT name, COUNT(*) AS total
FROM s1
GROUP BY name
ORDER BY total DESC;
 
SELECT COUNT(DISTINCT name) AS distinct_name_count
FROM s1;
 
SHOW INDEX FROM s1;

全表列表、统计大部分记录或返回 SELECT * 时,索引未必带来收益。应优先考虑分页、字段裁剪和基于主键/唯一索引的范围分页:

SELECT id, name, price
FROM product
WHERE id > 1000
ORDER BY id
LIMIT 20;

常见条件与索引效果

查询方式通常的索引表现
高区分度字段等值查询可以显著缩小扫描范围
命中大量记录的等值查询回表成本高,优化器可能选择全表扫描
小范围查询通常适合使用索引
过大范围查询需要扫描大量索引记录,收益可能下降
索引字段参与运算/函数通常不能直接按原索引值定位
类型不一致可能发生隐式转换,影响索引使用
LIKE 'prefix%'通常可以利用索引有序性
LIKE '%keyword' / LIKE '%keyword%'普通 B+树索引通常无法有效定位
-- 不推荐:对索引字段运算
SELECT COUNT(id) FROM s1 WHERE id * 12 = 10000;
 
-- 改写:把运算放在常量侧
SELECT COUNT(id) FROM s1 WHERE id = 10000 / 12;
 
-- 不推荐:对日期列做函数
SELECT COUNT(id) FROM s1 WHERE YEAR(created_at) = 2025;
 
-- 改写为范围条件
SELECT COUNT(id)
FROM s1
WHERE created_at >= '2025-01-01'
  AND created_at < '2026-01-01';

应尽量保持查询参数类型与列定义一致,例如整数列使用数值参数,字符串列使用字符串参数。最终是否使用索引以及是否值得使用,必须通过 EXPLAIN 或 EXPLAIN ANALYZE 验证。

索引条件下推(ICP)

索引条件下推(Index Condition Pushdown,ICP) 会把能够在索引层判断的条件提前执行。以联合索引 (name, age) 为例:

CREATE INDEX idx_user_name_age ON user(name, age);
 
SELECT *
FROM user
WHERE name LIKE 'egon%'
  AND age = 18;

没有 ICP 时,数据库可能先扫描满足 name 的辅助索引记录、回表,再判断 age。使用 ICP 时,age = 18 可以先在索引层过滤,只有更可能满足条件的记录才回表。

对比项覆盖索引索引条件下推
是否回表查询字段被覆盖时不需要仍可能需要回表
主要作用直接从索引返回结果提前过滤,减少回表次数
常见执行信息Using indexUsing index condition
适用条件查询所需字段都在索引中查询还需要索引之外的完整字段

查看或临时切换 ICP:

SHOW VARIABLES LIKE 'optimizer_switch';
SET SESSION optimizer_switch = 'index_condition_pushdown=off';
SET SESSION optimizer_switch = 'index_condition_pushdown=on';
EXPLAIN SELECT * FROM user WHERE name LIKE 'egon%' AND age = 18;

Using index condition 不表示完全不回表;实际收益还取决于扫描范围、过滤比例、返回字段和数据分布。必要时用 EXPLAIN ANALYZE 观察真实执行行数与耗时。

事务与 ACID

事务是一组必须作为整体执行的数据库操作。成功时提交,出现异常时回滚,常用于转账、支付和库存扣减等多语句业务。

特性含义
原子性 Atomicity全部成功或全部撤销
一致性 Consistency执行前后满足约束和业务规则
隔离性 Isolation并发事务按隔离规则互不产生不应有的影响
持久性 Durability提交结果在故障恢复后仍应保留
START TRANSACTION;
UPDATE account SET balance = balance - 100 WHERE id = 1;
UPDATE account SET balance = balance + 100 WHERE id = 2;
COMMIT;

如果中途失败,应在异常处理路径执行 ROLLBACK,不能只依赖某一条 SQL 的执行结果。

并发读现象与隔离级别

现象含义
脏读读到了其他事务尚未提交、随后可能回滚的数据
不可重复读同一事务两次读取同一行,期间被其他事务提交修改
幻读同一事务按相同条件重复查询,结果集出现新增/减少的符合条件记录
丢失更新并发修改互相覆盖,后提交结果覆盖先提交结果

常见隔离级别:

隔离级别脏读不可重复读幻读
READ UNCOMMITTED可能可能可能
READ COMMITTED避免可能可能
REPEATABLE READ避免避免需结合快照读/当前读与锁机制分析
SERIALIZABLE避免避免避免
SELECT @@transaction_isolation;
SELECT @@global.transaction_isolation;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

普通 SELECT 与 SELECT ... FOR UPDATE 的并发行为不同,实验时至少使用两个数据库连接,并记录事务开始、读取、修改和提交的先后顺序。

当日总结

  • 二级索引查询完整行通常需要回表,覆盖索引可以避免回表。
  • 查询范围、选择性、返回字段和数据分布共同决定索引收益。
  • 函数、字段运算、隐式类型转换和前置通配符可能使索引难以有效使用。
  • ICP 在索引层提前过滤,减少回表;它不等于覆盖索引。
  • 事务通过 ACID 保证多条操作的可靠性,隔离级别影响并发读现象。
  • 验证索引和事务行为应结合 EXPLAIN ANALYZE 及多连接并发实验。

相关页面