MySQL事务与索引做了什么?

课程:B站大学

记录学习极客时间团队MySQL45讲,进阶数据分析和数据处理

MySQL中的事务和索引的作用

  • [第一章:事务隔离 ------ 为什么你改了我还看不见?](#第一章:事务隔离 —— 为什么你改了我还看不见?)
    • [1.1 事务基础](#1.1 事务基础)
    • [1.2 四大隔离级别](#1.2 四大隔离级别)
    • [1.3 实现原理:MVCC + Read View](#1.3 实现原理:MVCC + Read View)
    • [1.4 长事务的危害(重点!)](#1.4 长事务的危害(重点!))
    • [1.5 最佳实践](#1.5 最佳实践)
  • [第二章:深入浅出索引(上)------ 数据结构与B+树](#第二章:深入浅出索引(上)—— 数据结构与B+树)
    • [2.1 三种索引模型对比](#2.1 三种索引模型对比)
    • [2.3 主键索引 vs 二级索引](#2.3 主键索引 vs 二级索引)
    • [2.4 页分裂与自增主键](#2.4 页分裂与自增主键)
    • [2.5 重建索引的正确方式](#2.5 重建索引的正确方式)
  • [第三章:深入浅出索引(下)------ 覆盖索引与索引优化](#第三章:深入浅出索引(下)—— 覆盖索引与索引优化)
    • [3.1 回表现象](#3.1 回表现象)
    • [3.2 覆盖索引(Covering Index)](#3.2 覆盖索引(Covering Index))
    • [3.3 最左前缀原则](#3.3 最左前缀原则)
    • [3.4 索引下推 ICP(Index Condition Pushdown)](#3.4 索引下推 ICP(Index Condition Pushdown))

第一章:事务隔离 ------ 为什么你改了我还看不见?

1.1 事务基础

事务保证一组SQL要么全成功,要么全失败(如转账:查询→扣减→更新)。

⚠️ 只有 InnoDB 支持事务,MyISAM 不支持,这是 InnoDB 取代 MyISAM 的核心原因之一。

ACID 四大特性 :原子性、一致性、隔离性、持久性。本章聚焦隔离性

并发三大问题

问题 定义
脏读 读到其他事务未提交的修改
不可重复读 同一事务内两次读同一行,结果不一致
幻读 同一事务内两次范围查询,结果集行数不一致

1.2 四大隔离级别

隔离级别 脏读 不可重复读 幻读 核心特点
Read Uncommitted 读未提交的修改
Read Committed 只有提交后才可见
Repeatable Read(默认) ❌* 事务期间视图不变
Serializable 读写加锁,完全串行

*InnoDB 在 RR 级别通过 MVCC + 间隙锁解决幻读。

经典示例(初始值=1,两个事务操作):

隔离级别 V1 V2 V3
RC 1 2 2
RR 1 1 2

1.3 实现原理:MVCC + Read View

不同隔离级别的核心差异在于何时创建 Read View

  • RC:每条SQL执行时创建新视图(读提交)
  • RR:事务启动时创建,全程复用(可重复读)
  • RU:无视图,直接读最新值(读未提交)
  • Serializable:读写加锁(串行性)

MVCC 多版本链

每条记录更新时生成 undo log(回滚日志),形成版本链。不同事务的 Read View 对应不同版本。

回滚日志清理时机:当系统中没有比该日志更早的 Read View 时,才会被删除。


1.4 长事务的危害(重点!)

  1. 空间暴涨:undo log 无法清理 → 曾有案例:数据 20GB,undo log 膨胀到 200GB
  2. 锁资源占用:长事务持有行锁/表锁,阻塞其他事务

监控长事务

sql 复制代码
SELECT * FROM information_schema.innodb_trx 
WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60;

1.5 最佳实践

sql 复制代码
-- 推荐:显式启动
BEGIN;
-- ...SQL...
COMMIT;

-- 优化:提交后自动开启下一个事务(省去BEGIN开销)
COMMIT WORK AND CHAIN;

-- 查看隔离级别
SHOW VARIABLES LIKE 'transaction_isolation';

-- Oracle迁移时设为RC
SET GLOBAL transaction_isolation = 'READ-COMMITTED';

⚠️ 避免 SET autocommit=0,长连接下极易产生意外长事务。


第二章:深入浅出索引(上)------ 数据结构与B+树

2.1 三种索引模型对比

模型 等值查询 范围查询 更新 适用场景
哈希表 O(1) 等值查询NoSQL
有序数组 O(logN) 静态数据
二叉搜索树 O(logN) MySQL InnoDB

为什么不用二叉树?

  • 100万节点 → 树高20 → 20次磁盘IO(机械盘单次IO≈10ms)
  • B+树 N≈1200(16KB页/索引大小),树高4可存 17亿 数据

哈希表定义

哈希表是一种以键 - 值(key-value)存储数据的结构,我们只要输入待查找的值即 key,

就可以找到其对应的值即 Value。哈希的思路很简单,把值放在数组里,用一个哈希函数把

key 换算成一个确定的位置,然后把 value 放在数组的这个位置。

适合:哈希表这种结构适用于只有等值查询的场景

有序数组

有序数组在等值查询和范围查询场景中的性能就都非常优秀、可以先用二分法擦汗寻

,有序数组索引只适用于静态存储引擎,比如你要保存的是 2017 年某个城市的所有人

口信息,这类不会再修改的数据

2.2 二叉搜索树结构

  • 非叶子节点:只存索引键值 + 指针,不存数据
  • 叶子节点:存完整数据(聚簇索引)或主键值(二级索引)
  • 叶子链表:双向链表连接 → 支持高效范围查询

2.3 主键索引 vs 二级索引

类型 别名 叶子节点存 查询特点
主键索引 聚簇索引 整行数据 只需扫一棵B+树
二级索引 非聚簇索引 主键值 回表查主键索引

✅ 尽量用主键查询,避免回表开销。


2.4 页分裂与自增主键

  • 自增主键:顺序追加 → 极少页分裂 → 空间利用率高
  • 随机主键(如UUID):频繁页分裂 → 空间利用率降至约50%

为什么推荐自增主键?

维度 自增主键 业务主键(如身份证号)
插入性能 顺序写,无挪动 随机写,频繁挪动
二级索引空间 int 4~8字节 varchar ~20字节
结论 节省约60%空间 不推荐

例外:表只有一个索引且是唯一索引 → 可用业务字段做主键(KV场景)。


2.5 重建索引的正确方式

sql 复制代码
-- 普通索引重建(先建后删,避免抖动)
ALTER TABLE T ADD INDEX idx_k_new(k);
ALTER TABLE T DROP INDEX idx_k;

-- 主键/全表重建(Online DDL,5.6+)
ALTER TABLE T ENGINE=InnoDB;

❌ 错误:先删主键再建主键 → 全表重建两次!


第三章:深入浅出索引(下)------ 覆盖索引与索引优化

3.1 回表现象

表结构

sql 复制代码
CREATE TABLE T (
  ID int PRIMARY KEY,
  k int NOT NULL,
  s varchar(16),
  INDEX k(k)
) ENGINE=InnoDB;

查询SELECT * FROM T WHERE k BETWEEN 3 AND 5

执行流程

步骤 操作 说明
k索引树找到 k=3 → ID=300 扫描二级索引
回表 → 主键索引查整行R3 随机IO
k索引树找 k=5 → ID=500 扫描二级索引
回表 → 主键索引查整行R4 随机IO
k=6 不满足条件,结束 ---

代价:扫描3条索引记录,回表2次。


3.2 覆盖索引(Covering Index)

定义 :索引包含了查询所需的所有字段 → 无需回表

sql 复制代码
-- 原查询(需回表2次)
SELECT * FROM T WHERE k BETWEEN 3 AND 5;

-- 优化后(覆盖索引,0次回表)
SELECT ID FROM T WHERE k BETWEEN 3 AND 5;

实战场景:市民表高频查询「身份证号 → 姓名」

sql 复制代码
-- 建立联合索引 (身份证号, 姓名)
-- 查询只需扫描联合索引,无需回表
SELECT 姓名 FROM 市民 WHERE 身份证号 = 'xxx';

⚠️ 权衡:索引有维护成本,只为高频请求建冗余联合索引。


3.3 最左前缀原则

B+树索引项按字段顺序排序,只要满足最左前缀就能利用索引

联合索引 (name, age) 结构

生效规则

查询条件 是否用索引 说明
WHERE name='张三' 命中最左字段
WHERE name LIKE '张%' 命中最左M个字符
WHERE age=10 不满足最左前缀

字段顺序设计原则

  1. 复用优先 :已有 (a,b) → 无需再建 (a)
  2. 空间优先 :需单独建 (b) 时,把字段小的 放联合索引前列
    • 例:nameage 大 → 建 (name, age) + 单建 (age)

3.4 索引下推 ICP(Index Condition Pushdown)

MySQL 5.6+ 引入,在索引遍历时提前过滤不满足条件的记录,减少回表次数。

场景(name, age) 联合索引

sql 复制代码
SELECT * FROM tuser 
WHERE name LIKE '张%' AND age=10 AND ismale=1;

对比

无索引下推执行流程

索引下推执行流程

版本 机制 回表次数
5.5及之前 只用 name 定位,全部回表后在Server层过滤 4次
5.6+(ICP) 引擎层直接用 age 过滤,不满足的跳过 2次

✅ ICP 将过滤条件下推到引擎层,大幅减少回表IO。


如果觉得本文对你有帮助,欢迎点赞 + 收藏 + 关注,持续更新MySQL实战系列笔记!

相关推荐
小码农 - 初1 小时前
MySQL 的约束
mysql
NoteStream1 小时前
【C语言基础】分支和循环(上)
c语言·开发语言·c++·经验分享·笔记·算法·c#
刘洋浪子2 小时前
AndroidStudio保存日志菜单
android
朱涛的自习室2 小时前
从 Prompt 到 Graph:AI 工程的进化史
android·前端·人工智能
Dear~yxy2 小时前
MySQL企业级实战
数据库·mysql
zzm6282 小时前
位置偏差估计与无偏排序学习——WSDM 2018论文精读笔记
笔记·学习
kdxiaojie3 小时前
Linux 驱动研究 —— SDIO (1)
linux·运维·笔记·学习·sdio
weixin_727535623 小时前
mysql高频八股问答200
sql·mysql
3A Cloud4 小时前
Architecture Diagram Skill 详细介绍
人工智能·笔记·信息可视化