MYSQL-DAY2

什么是 MVCC(多版本并发控制,无锁机制 )?它解决了什么问题?

MVCC,即多版本并发控制,是 InnoDB 用来提高并发读写能力的一种机制。它并不是完全无锁,而是让普通 SELECT 尽量通过读取数据的历史版本,避免与写操作发生锁等待。

InnoDB 在修改数据时,会通过 Undo Log 保留旧版本, 并形成版本链。事务执行快照读时,会根据 Read View 判断某个版本是否可见;如果当前版本不可见,就沿版本链查找符合条件的历史版本。

MVCC 主要解决读写冲突问题,使读操作通常不阻塞写操作、写操作也不阻塞普通读,同时可以避免脏读,并在可重复读隔离级别下保证同一事务多次快照读结果一致,从而减少锁竞争、提高数据库并发性能。

UPDATEDELETESELECT ... FOR UPDATE 等当前读仍然需要加锁,所以 MVCC 不能理解成数据库完全无锁。

MVCC 解决了什么问题

1. 减少读写阻塞

如果没有 MVCC,当一个事务正在修改数据时,另一个事务查询这条数据,可能只能等待写事务释放锁。

有了 MVCC:

  • 写事务修改当前版本;
  • 读事务读取符合条件的历史版本。

因此普通查询通常不需要等待写事务完成。

也就是常说的:

读不阻塞写,写不阻塞普通读。

2. 避免脏读

假设事务 B 把余额修改为 800,但还没有提交。

事务 A 不能直接读取这个未提交的 800,因为事务 B 最后可能回滚(如果回滚了,那么就是脏读)。

MVCC 会通过版本可见性判断,让事务 A 读取之前已经提交的版本,例如 1000,从而避免读到未提交的数据。


3. 实现可重复读

在 MySQL InnoDB 的 REPEATABLE READ 隔离级别下,同一个事务中的多次快照读通常复用同一个 Read View。

例如:

复制代码
事务 A 第一次查询:1000
事务 B 修改并提交:800
事务 A 第二次查询:1000

虽然数据库最新值已经是 800,但事务 A 仍然按照原来的事务快照读取 1000,所以两次结果一致。

InnoDB 如何实现 MVCC

MySQL InnoDB 主要依赖三个部分:

Undo Log

保存数据修改前的历史信息。

例如将:

复制代码
1000

修改为:

复制代码
800

当前数据中保存 800,Undo Log 中保留可以还原出 1000 的信息。

版本链

当前数据和 Undo Log 中的历史版本,通过回滚指针连接起来:

复制代码
600 → 800 → 1000
Read View

Read View 是一套版本可见性规则,用来判断:

某个数据版本对当前事务是否可见。

如果最新版本不可见,InnoDB 就沿着版本链寻找更旧的版本,直到找到一个可见版本。


MVCC 并不是完全不加锁

普通查询属于快照读,通常使用 MVCC:

sql 复制代码
SELECT * FROM account WHERE id = 1;

但以下操作需要读取最新数据,通常仍然要加锁:

sql 复制代码
SELECT * FROM account WHERE id = 1 FOR UPDATE;

UPDATE account SET balance = 800 WHERE id = 1;

DELETE FROM account WHERE id = 1;

所以 MVCC主要解决的是:

普通读操作和写操作之间的冲突。

它并不能解决两个事务同时修改同一行数据时的写写冲突。

隔离级别 普通 SELECT 的处理 Read View 使用方式 两次查询可能结果
READ UNCOMMITTED 可以读取未提交版本 不提供可靠的一致性快照 可能脏读
READ COMMITTED 使用 MVCC 快照读 每次 SELECT 创建新的 Read View 第一次 1000,第二次可能 800
REPEATABLE READ 使用 MVCC 快照读 第一次快照读创建,后续复用 第一次 1000,第二次仍是 1000
SERIALIZABLE 事务内普通 SELECT 通常转为锁定读 更依赖锁保证串行执行 可能等待其他事务

redo log、undo log 和 binlog 有什么区别?

Undo Log 负责撤销,Redo Log 负责宕机后重做,Binlog 负责复制和数据恢复。

Undo Log 和 Redo Log 都属于 InnoDB 存储引擎,而 Binlog 属于 MySQL Server 层。

Undo Log 记录的是数据修改前的信息,主要用于事务回滚,保证事务的原子性;同时还会保存历史版本,配合 Read View 实现 MVCC。

Redo Log 记录的是InnoDB 对数据页所做的修改,主要用于崩溃恢复。事务提交后,即使数据页还没有真正刷入磁盘,MySQL 重启时也可以根据 Redo Log 恢复已提交的数据,从而保证事务的持久性。

Binlog 记录的是数据库已经发生的数据变更,支持 Statement、Row 和 Mixed 三种格式,主要用于主从复制,以及配合全量备份进行时间点恢复。

数据库发生崩溃后,InnoDB 会

先根据 Redo Log 恢复数据页,再通过 Undo Log 回滚未提交的事务。事务正常提交时,MySQL 还会通过两阶段提交协调 Redo Log 和 Binlog,保证主库中的事务状态和 Binlog 记录保持一致。
Undo Log:修改前是什么。

Redo Log:数据页改了什么。

Binlog:事务最终改变了什么。

日志 所属层级 记录内容 主要作用
Undo Log InnoDB 存储引擎 数据修改前的信息 事务回滚、支持 MVCC
Redo Log InnoDB 存储引擎 数据页发生了哪些修改 崩溃恢复、保证持久性
Binlog MySQL Server 层 数据库发生了哪些数据变更 主从复制、时间点恢复

InnoDB 中有哪些常见的锁?

InnoDB 的锁可以按照锁模式和锁定范围分类。

按照锁模式,主要有共享锁 S 和排他锁 X;此外还有表级的意向共享锁 IS 和意向排他锁 IX,用于表示事务准备

在表中的记录上加共享锁或排他锁。

按照锁定范围,可以分为表级锁和索引范围锁。表级锁包括 IS、IX 和 AUTO-INC 锁;索引范围锁主要包括 Record Lock(记录锁)、Gap Lock(间隙锁,开区间)、Next-Key Lock(临界锁,左闭右开) 和 Insert Intention Lock。其中 Record Lock 锁定具体索引记录,Gap Lock 锁定索引间隙,Next-Key Lock 是记录锁与间隙锁的组合。

什么是死锁?如何排查和避免?

死锁是指两个或多个事务互相持有对方需要的锁,同时又等待对方释放锁,形成循环等待,导致事务都无法继续执行。例如事务 A 先锁记录 1 再等待记录 2,而事务 B 先锁记录 2 再等待记录 1,就会产生死锁。

InnoDB 默认会自动检测死锁,并选择一个代价较小的事务进行整体回滚,让其他事务继续执行。排查时可以通过 SHOW ENGINE INNODB STATUS 查看最近一次死锁,重点分析各事务正在执行的 SQL、已经持有的锁和正在等待的锁;频繁死锁时可以临时开启 innodb_print_all_deadlocks,也可以通过 data_locksdata_lock_waitsINNODB_TRX 查看实时锁等待。

避免死锁的主要方法是统一事务访问数据的顺序、缩短事务时间、建立合适索引、减少锁定范围、避免不必要的 FOR UPDATE,并在应用层对死锁事务进行有限次数的整体重试。

为什么深分页查询比较慢?如何优化?

深分页通常是指使用较大的 LIMIT offset, size 进行分页。它慢的原因是 MySQL 不能简单地直接跳到 offset 位置,而是需要按照查询条件和排序规则扫描前 offset + size 条数据,丢弃前 offset 条,最终只返回 size 条。偏移量越大,扫描和丢弃的数据越多。如果使用二级索引并查询非索引字段,还可能产生大量回表;如果排序字段没有合适索引,还会产生额外排序开销。

优化方式主要有:

第一,使用基于主键或排序字段的游标分页,例如通过 WHERE id < lastId ORDER BY id DESC LIMIT 20,避免扫描前面所有数据;

第二,必须支持跳页时,可以使用覆盖索引加延迟关联,先从索引中查询当前页主键,再回表查询完整数据;

第三,根据查询条件和排序字段建立合适的联合索引;

此外还可以避免 SELECT *、先分页再关联、减少精确 COUNT(*)、限制最大页码以及通过 EXPLAIN ANALYZE 检查实际扫描和排序情况。

sql 复制代码
LIMIT 1000000, 10

表示:

跳过前 1,000,000 条数据,再取 10 条数据。

例 使用游标分页

不要传页码,而是传上一页最后一条数据的 ID。

相关推荐
隔窗听雨眠1 小时前
Oracle到PostgreSQL迁移实战:pg_hint_plan执行计划控制完全指南
数据库·postgresql·oracle
IKUN家族1 小时前
常见的依懒
java·服务器·数据库
程序员-Benothing2 小时前
MySQL 的存储引擎有哪些?它们之间有什么区别?
后端·mysql·面试·职场和发展
DBA_G2 小时前
解析GBase 8s数据库锁机制
数据库·oracle
captain3762 小时前
多线程进阶
java·开发语言·数据库
initialize13062 小时前
Oracle数据库 binary XML data类型同步
xml·数据库
倔强的石头1062 小时前
连接池参数治理-HikariCP怎么配才稳
数据库·oracle
zcmodeltech2 小时前
智慧城市沙盘模型多系统协同控制系统设计:基于STM32与Modbus RTU的园区-城市-数字孪生联动方案
服务器·数据库·人工智能·stm32·嵌入式硬件·信息可视化·智慧城市
ashiho2 小时前
【Redis】原理篇 3w字详解
数据库·redis·缓存·bootstrap