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,就说明使用了索引跳跃。

相关推荐
2601_9495936543 分钟前
深入解析CANN-acl应用层接口:构建高效的AI应用开发框架
数据库·人工智能
javachen__43 分钟前
mysql新老项目版本选择
数据库·mysql
Dxy12393102161 小时前
MySQL如何高效查询表数据量:从基础到进阶的优化指南
数据库·mysql
Dying.Light1 小时前
MySQL相关问题
数据库·mysql
蜡笔小炘2 小时前
LVS -- 利用防火墙标签(FireWall Mark)解决轮询错误
服务器·数据库·lvs
韩立学长2 小时前
基于Springboot泉州旅游攻略平台d5h5zz02(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·旅游
Re.不晚2 小时前
MySQL进阶之战——索引、事务与锁、高可用架构的三重奏
数据库·mysql·架构
老邓计算机毕设2 小时前
SSM智慧社区信息化服务平台4v5hv(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm 框架·智慧社区、·信息化平台
麦聪聊数据3 小时前
为何通用堡垒机无法在数据库运维中实现精准风控?
数据库·sql·安全·低代码·架构
2301_790300963 小时前
Python数据库操作:SQLAlchemy ORM指南
jvm·数据库·python