【mysql】事务 mvcc 锁

1 事务

并发控制的单元,要么都做,要么都不做

redolog
  • 物理日志,记录物理修改细节(崩溃不会丢)
  • 核心作用:
    • 崩溃恢复,commit后,内存buffer中未刷盘,重启后redo(持久性)
    • 提升性能,基于wal原则,先写redolog再写数据文件
  • 工作流程:
    • 事务执行时,修改内存buffer pool中脏页
    • 同时写入redolog buffer
    • commit时,触发fsync将redolog buffer写入redo log 文件
    • 后台线程异步刷盘buffer pool到磁盘ibd文件
  • 细节:
    • 存储形式:两个文件循环写入,
    • 日志状态:prepare和commit 两阶段
    • 核心参数:innodb_flush_log_at_trx_commit 控制刷盘策略
  • 生命周期
    • 写入:事务执行中持续添加
    • 失效:redolog对应脏页被刷到数据文件后,可被覆盖
    • 清理:循环写入覆盖
undolog
  • 逻辑日志,记录修改前旧状态(错了能撤回)
  • 核心作用:
    • 事务执行失败或主动回滚时rollback(原子性)
    • mvcc通过undolog找到历史版本 (隔离性 rr rc)
  • 工作流程
    • 事务执行dml时,先读取旧状态,写入undolog
    • 若commit,undolog不会立刻删除(可能被其他事务mvcc读取)
    • 若rollback,执行undolog反向操作
    • 后台purge线程,异步清理(当没有事务需要读取)
  • 关键细节
    • 存储形式:共享表空间
    • 类型:insert 提交后直接删除(无需mvcc);update delete 提交后保留,等待purge清理
    • undo与rollback 通过128个rollback segment管理
  • 生命周期
    • 写入:执行dml时同步写入
    • 保留:事务提交后标记为可清理
    • 清理:purge线程定期扫描,清理过期log
acid特性

a:依赖undolog,失败时回滚
c:依赖redolog,提交时redolog刷盘,崩溃时重新修改
i:依赖undolog,mvcc读取历史版本+锁机制
d:前三点满足

2 隔离级别

为了提升mysql并发处理sql语句的性能

  • ru 读未提交 隔离异常123
  • rc 读已提交 读时mvcc,读取最新版本行数据 异常23
  • rr 可重复度 读时mvcc,读取事务开始前快照,默认 异常3
  • s
-- 设置隔离级别  
SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL REPEATABLE READ;  
-- 或者采用下面的方式设置隔离级别  
SET @@tx_isolation = 'REPEATABLE READ';  
SET @@global.tx_isolation = 'REPEATABLE READ';  
-- 查看全局隔离级别  
SELECT @@global.tx_isolation;  
-- 查看当前会话隔离级别  
SELECT @@session.tx_isolation;  
SELECT @@tx_isolation;  
-- 手动给读加 S 锁  
SELECT ... LOCK IN SHARE MODE;  
-- 手动给读加 X 锁  
SELECT ... FOR UPDATE;  
-- 查看当前锁信息  
SELECT * FROM information_schema.innodb_locks;

3 不同隔离级别异常

  • 脏读:事务B读到A未commit数据,Arollback
  • 不可重复度:同一事务B两次读间隔中,Acommit了update
  • 幻读:同一事务B两次查询过程中,Acommit了insert delete(范围读),导致快照读和当前读不同

4 mvcc

一致性非锁读

  • readview
    • rc :每次select创建
    • rr:每次启动事务创建
m_ids :创建 read view 时,当前数据库活跃事务(开启未提交的事务)的事务 id 列表;  
min_trx_id :创建 read view 时,m_ids 中的最小事务 id;  
max_trx_id :创建 read view 时,当前数据库将为下一个事务分配的事务 id;并不一定是 m_ids  
中的最大事务 id;  
creator_trx_id :创建 read view 所在事务的 id


聚集索引记录的隐藏行

trx_id :当某个事务对某条聚集索引记录进行修改时,将会把当前事务的 id 赋值给 trx_id ;  
roll_pointer :当某个事务对某条聚集索引记录进行修改时,会将上一个版本的记录写到 undo  
log,然后通过 roll_pointer 指向旧版本记录,通过它可以找到修改前的记录

事务可见性
rc
rr
快照读
当前读

5 锁

在这里插入图片描述

  • 锁的访问模式:s x is ix ai
    • x冲突所有
    • s冲突 ix ai
    • ai冲突自身
锁 S X IS IX AI  
S 兼容 冲突 兼容 冲突 冲突  
X 冲突 冲突 冲突 冲突 冲突  
IS 兼容 冲突 兼容 兼容 兼容  
IX 冲突 冲突 兼容 兼容 兼容  
AI 冲突 冲突 兼容 兼容 冲突  
  • 锁算法
    • record lock 单个行记录上的锁
    • gap lock 间隙锁 rr级别以上
    • next-key lock r+g锁,锁定左开右闭区间
    • insert intention lock 意向锁 insert 时
-- 查看是否支持间隙锁,默认支持,也就是 innodb_locks_unsafe_for_binlog = 0;  
SELECT @@innodb_locks_unsafe_for_binlog

锁  G I R N 持有
GAP(请求) 兼容 兼容 兼容 兼容  
Insert Intention  (请求) 冲突 兼容 兼容 冲突  
Record(请求) 兼容 兼容 冲突 冲突  
Next-key(请求) 兼容 兼容 冲突 冲突

一个事务已经获取了插入意向锁,对其他事务是没有任何影响的;  
一个事务想要获取插入意向锁,如果有其他事务已经加了 gap lock 或 Next-key lock 则会阻塞;这个是重点,死锁之源
维度record lock(记录锁)gap lock(间隙锁)S 锁(共享锁)/ X 锁(排他锁)
锁定对象具体的索引记录行(如 id=10 的某条数据)索引记录之间的间隙(如 (5,10) 区间,无实际记录)可作用于 record lockgap lock,是锁的访问模式(共享 / 排他)
核心作用阻止其他事务修改或加排他锁于同一条记录阻止其他事务在间隙中插入新记录,防止幻读- S 锁:允许多事务并发读,阻止其他事务加 X 锁;

- X 锁:阻止其他事务读(加 S 锁)或写(加 X 锁)
锁模式分类可分为 S-record lock(共享记录锁)、X-record lock(排他记录锁)通常为 X-gap lock(排他间隙锁,阻止插入),也存在共享间隙锁(但场景极少)- S 锁:共享模式,多事务可同时持有;

- X 锁:排他模式,同一行仅一个事务可持有
兼容性(与自身 / 其他锁)- 同一条记录的 S-record lock 兼容(多事务可同时加);

- 与 X-record lock 冲突(同一记录仅一个 X 锁)
同间隙的 gap lock 互相兼容(多个事务可同时加间隙锁),但与 record lock 需结合临键锁逻辑判断- S 锁与 S 锁兼容,与 X 锁冲突;

- X 锁与 S、X 锁均冲突
典型场景事务更新单条记录(如 UPDATE ... WHERE id=10事务执行范围查询(如 SELECT ... WHERE id>5 FOR UPDATE),InnoDB 自动加间隙锁防止幻读- S 锁:SELECT ... LOCK IN SHARE MODE

- X 锁:UPDATE/DELETESELECT ... FOR UPDATE

全局锁
表级锁:对页或表加锁
行级锁:对表的索引加锁,包括聚集索引和辅助索引

  • 加锁对象 rc rr
    • 聚集索引
    • 辅助唯一索引
    • 辅助非唯一索引
    • 无索引
    • 聚集索引 范围查询

6 死锁

  • 相反加锁顺序死锁
    • 不同表加锁顺序相反
    • 相同表不同行加锁顺序相反
    • 解决:调整加锁顺序
  • 锁冲突死锁
    • rr级别下,插入意向锁与gap锁冲突死锁
    • 解决:降低隔离级别
####系统表
-- 开启标准监控  
CREATE TABLE innodb_monitor (a INT) ENGINE=INNODB;  
  
-- 关闭标准监控  
DROP TABLE innodb_monitor;  
  
-- 开启锁监控  
CREATE TABLE innodb_lock_monitor (a INT) ENGINE=INNODB;  
  
-- 关闭锁监控  
DROP TABLE innodb_lock_monitor  

####系统参数
-- 开启标准监控  
set GLOBAL innodb_status_output=ON;  
  
-- 关闭标准监控  
set GLOBAL innodb_status_output=OFF;  
  
-- 开启锁监控  
set GLOBAL innodb_status_output_locks=ON;  
  
-- 关闭锁监控  
set GLOBAL innodb_status_output_locks=OFF;  
-- 将死锁信息记录在错误日志中  
set GLOBAL innodb_print_all_deadlocks=ON;

技术支持: https://github.com/0voice

### MySQL 事务MVCC 实现原理 #### 一、事务的概念及其特性 在数据库环境中,事务是指一系列作为一个整体执行的操作序列。这一系列操作要么全部成功完成并永久生效;如果其中任何一个环节失败,则整个事务都会回滚至初始状态,确保数据一致性[^1]。 - **原子性(Atomicity)**:事务中的所有操作视为单一工作单元,该单元内各项更改应同生共死。 - **一致性(Consistency)**:事务前后需保持一致的状态转换,即遵循预定义规则。 - **隔离性(Isolation)**:各并发运行的事务间相互独立不受干扰。 - **持久性(Durability)**:一旦提交,即使发生系统崩溃也能够恢复成果。 #### 二、MVCC概述 为了提升并发处理能力的同时保障读写的正常交互,MySQL引入了多版本并发控制(MVCC)[^2]。其核心在于维护同一份记录的不同时间点上的多个版本,以此支持不同类型的读取行为——快照读与当前读: - **快照读(Snapshot Read)**:无需加即可获取指定时刻的数据视图,适用于大多数SELECT查询场景; - **当前读(Current Read)**:涉及定机制以防止其他会话修改正在访问的对象实例,常用于UPDATE/DELETE以及特定条件下带有FOR UPDATE或LOCK IN SHARE MODE修饰符的SELECT语句中。 #### 三、InnoDB存储引擎下的MVCC实现细节 作为MySQL默认使用的高性能事务型表管理器,InnoDB实现了上述提到的两种主要读取模式,并通过以下组件共同作用达成高效稳定的并发控制效果[^3]: - **隐藏列**:每行记录额外携带两个隐含属性`DB_TRX_ID`(最近更新此条目的事务ID)`DB_ROLL_PTR`(指向undo日志的位置),用作判断可见性的依据之一; - **Undo Log**:当某笔交易对现有资料进行了变更时,旧版会被复制到undo log区域保存起来直到不再需要为止(比如超出了必要的保留期限或是相关联的事物已经结束); - **ReadView**:每当启动一个新的只读类事物之前都要构建一个read view对象,里面包含了创建瞬间活跃着的所有活动事物列表以及其他必要信息用来决定哪些历史版本是可以被看见的。 #### 四、可见性逻辑判定流程 针对某个给定的记录版本R,假设现在有一个正在进行中的事务T想要对其进行读取,那么根据如下准则来确定是否能观察到它: 1. 若R是在T开启之后才产生的变动,则不可见; 2. 假设R是由另一个尚未完结且位于T之前的事务S所引起的变化,此时要看S是否处于已准备提交但还未正式commit阶段: - 是 -> R暂时不可见; - 否 -> 继续下一步骤; 3. 检查产生R的那个事务U是否存在于当前T持有的readview之内: - 存在-> 不可见 ; - 缺失-> 可见; 此外还有专门针对某些特殊情况设计的小于最低限度id比较规则等辅助手段帮助更精准地界定可视范围边界条件[^4]。 ```sql -- 示例SQL展示如何查看当前系统的最小未分配事务ID SHOW ENGINE INNODB STATUS\G ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值