8、MySQL相关问题补充

一、联合索引和最左匹配原则

1、最左匹配原则

在建立联合索引后,查询条件需要按照索引的顺序从左到右完全匹配,才能使用到索引。

2、相关问题

前提:user表包含主键ID,A,B,C列,创建(A,B,C)联合索引

(1)select * from user where B=2 and A=3 and C=4; 会用到联合索引吗?

会。优化器会将查询条件自动重排序为 where A=3 and B=2 and C=4;

  • 可用索引的查询条件:where A=?; where A=? and B=?; where A=? and B=? and C=?;
  • 不可用索引的查询条件:where B=?;(跳过了A)。 where A=? and C=?;(跳过了B)。WHERE A = 3 AND B = '2' AND C = 4;(A列若是字符串类型,发生隐式转换导致索引失效)。

(2)select * from user where B = 2 and A > 3 and C = 4; 会用到联合索引吗?

部分使用。自动重排,先使用 A 做范围查询,在使用B筛选(索引下推),C无法使用索引

联合索引的使用遵循 "最左前缀原则" ,并且 一旦遇到范围查询(如 >, <, BETWEEN, LIKE 'xx%' 等),后续列将无法用于索引查找

(3)select * from user where A = 2 and B > 3 and C = 4; 会用到联合索引吗?

会。A等值匹配精准定位,B在A=2的范围内进行范围扫描,C会使用索引下推。

原则:创建联合索引时,尽量把用于等值查找的列放在最左边。

二、深分页

复制代码
SELECT * FROM orders ORDER BY id LIMIT 1000000, 20;

上面的SQL就是深分页,执行 LIMIT offset, size 时,必须先扫描并跳过前 offset。offset 越大,扫描到的无用数据越多,I/O 和 CPU 开销越大。

解决方案:

1、在自增主键或时间字段排序的场景下,可以记录上一页最后一条数据的id,下一页在这个id之后开始查询。可以使用到id索引,但是不能随意跳转页。

复制代码
-- 第一页
SELECT * FROM orders ORDER BY id LIMIT 20;

-- 假设最后一条 id = 99999999
-- 第二页
SELECT * FROM orders WHERE id > 99999999 ORDER BY id LIMIT 20;

-- 第 N 页:始终基于上一页的边界值

2、延迟关联,必须用offset时,减少回表次数。但仍需扫描 100 万索引项,只是比全表回表快一些,无法根本解决深分页问题。

复制代码
-- 先通过覆盖索引找到目标 id(避免回表)
SELECT o.* 
FROM orders o
INNER JOIN (
    SELECT id 
    FROM orders 
    ORDER BY id 
    LIMIT 1000000, 20
) tmp ON o.id = tmp.id;

3、业务层限制,只给用户展示前100页的信息,其他信息使用搜索查询。

三、undo log 和 redo log

1、undo log(回滚日志

记录事务开始前的数据状态,是 InnoDB 存储引擎 实现 事务原子性(Atomicity)多版本并发控制(MVCC) 的核心机制之一。

实现事务的原子性(回滚): undo log 本身存储在 InnoDB 的 undo tablespace (MySQL 8.0+)或 系统表空间 (旧版本)中,每个事务修改数据前,会先在 undo log 中记录反向操作,从而支持事务的回滚

  • INSERT → 记录 DELETE
  • UPDATE → 记录修改前的旧值
  • DELETE → 记录整行数据

对于MVCC的支持体现在 :当一个事务正在读取某行数据时,另一个事务可能正在修改它。InnoDB 通过 undo log 构建该行的历史版本 ,让读事务看到符合其一致性视图的数据(避免加锁,提高并发),例如:

  • 事务 A 在时间 T1 开始,执行 SELECT * FROM account WHERE id = 1;
  • 事务 B 在 T2 修改了该行并提交
  • 事务 A 再次查询时,仍应看到 T1 时刻的值 → InnoDB 从 undo log 中找到对应版本返回

事务提交后,undo log 不会立即删除。InnoDB 后台有 purge 线程,定期清理不再被任何事务需要的 undo log。判断依据是,所有活跃事务的 Read View 都不需要该 undo 版本

2、redo log(重做日志)

用于保证事务的持久性、支持崩溃恢复。记录事务提交标志,InnoDB通过检查redo log中是否有事务的提交标志,来判断该事务是否已经提交。redo log记录事务的ID、操作、状态。

两阶段提交记录:事务的提交过程如下:

  • redo log写入事务信息,状态是PREPARE
  • bin log写入事务信息
  • redo log写入事务信息,状态是COMMIT

bin log只记录一次事务信息,在崩溃恢复的过程中,特定情况下起到"仲裁"作用。InnoDB是基于 redo log 做崩溃恢复的。

崩溃恢复流程

  • InnoDB 扫描 redo log,对于有commit记录的事务,直接重做
  • 如果事务只有prepare状态的数据,则去查询bin log,bin log中有事务信息则说明事务已提交,会重做;bin log中有事务信息则说明事务未提交,会回滚

四、索引跳跃扫描

MySQL 8.0 引入的一种优化器策略 ,默认是开启的,用于在 复合索引(联合索引)的前导列(leading column)未出现在查询条件中时,仍然能高效利用该索引

复制代码
CREATE TABLE t (
    gender ENUM('M', 'F') NOT NULL,      -- 只有 2 个值
    age INT NOT NULL,
    name VARCHAR(50),
    KEY idx_gender_age (gender, age)     -- 联合索引
);

-- 注意:WHERE 中没有 gender!
SELECT * FROM t WHERE age = 25;

gender 取值很少(只有 'M' 和 'F'),可以执行下列查询:

复制代码
SELECT * FROM t WHERE gender = 'M' AND age = 25
UNION ALL
SELECT * FROM t WHERE gender = 'F' AND age = 25

虽然执行了 2 次索引查找,但总成本 远低于全表扫描。这就是"跳跃":跳过前导列的限制,通过枚举其离散值来复用索引。

触发条件:前导列是 低基数字段(如性别、状态、布尔值、枚举),如果前导列的值有 100 万个不同值,枚举成本太高,不会触发。

EXPLAIN的EXTRA列中显示Using index for skip scan,就说明使用了索引跳跃。

相关推荐
爪哇天下2 小时前
Mysql实现经纬度距离的排序(粗略的城市排序)
数据库·mysql
独自破碎E2 小时前
MySQL中有哪些日志类型?
数据库·mysql
笨蛋不要掉眼泪2 小时前
Redis核心数据类型与命令
数据库·redis·缓存
数据知道3 小时前
一文掌握 MongoDB 详细安装与配置(Windows / Linux / macOS 全平台)
linux·数据库·windows·mongodb·macos
Knight_AL3 小时前
Flink 状态管理详细总结:State 分类、Keyed State 实战、Operator State、TTL、状态后端选型
前端·数据库·flink
ADRU3 小时前
Dify API 数据库连接与 Session 管理架构调研
数据库·架构
zhglhy3 小时前
Redis Cluster 的数据分片机制
数据库·redis·缓存
奥特曼_ it3 小时前
【NLP舆情分析】基于python微博舆情分析可视化系统(完整系统源码+数据库+详细部署教程)✅
数据库·python·自然语言处理
Mr__Miss3 小时前
Mysql的redo log和undo log
数据库