# mysql的事务隔离级别介绍
# 简述
关于事务隔离级别在接触mysql时就已经了解过了,有几个种类,会带来哪几种问题,但每次到实际场景时,却又不记得什么样的隔离级别会造成什么样的问题,应该如何解决. 其实每次重新查资料再分析都是可以解决问题的, 但是这中间又是重复推理学习的过程, 所以对于已经理解的东西, 就将自己的心路历程记录下来, 不仅可以带给他人帮助也帮助自己思考. 这里想一遍将事务隔离级别讲清楚.
# mysql引擎与事务的关系
只有InnoDB支持事务 , 会有另外文章解析与myisam的比较
# 数据库事务的四个特征
即ACID
- 原子性(Atomicity)
- 一致性(Consistency)
- 隔离性(Isolation)
- 持久性(Durability)
# 并发事务会带来的问题
其他文章会详细介绍这四种问题
- 脏读
一句话: 能读到未提交事务的数据变化
解决方案:
- 设置
read committed以上的事务隔离级别
- 不可重复读
一句话: 多次读取数据一直在变化
解决方案:
- 设置
repeatable read以上的事务隔离级别- 由要进行修改操作的事务显示使用排他锁
- 或者由重复读的事务显示使用共享锁
- 幻读
一句话: 多次读取数据, 数据的
数量一直在变化解决方案:
- 设置
serializable以上的事务隔离级别
- 数据丢失
# mysql的事务隔离级别种类
- read uncommitted
- read committed
- repeatable read
- serializable
# 事务隔离级别的所依赖的技术
事务隔离级别依赖MVCC和数据库锁, 以下做简单介绍, 深入了解可以再查找资料.
# MVCC
MVCC即
多版本并发控制MVCC实质上是给每一行数据增加了3个字段: DB_TRX_ID、DB_ROLL_PTR、DB_ROW_ID. 也可以说给每一行加上了两个字段: 创建时间, 过期时间(删除时间)
Innodb中的
RC和RR是基于MVCC实现的高性能事务, 另外文章会介绍快照读相关内容解释如何实现的高性能.
# 共享锁(S)和排他锁(X)
# 分别简述事务隔离级别
简述事务隔离级别的实现原理, 以下都是以两个事务A,B来举例说明.
# read uncommitted
事务A读取数据时, 事务B读取数据和修改数据会加上排他锁, 事务B结束事务时会释放排他锁.
关键点抽取: 读取数据不加锁, 可以随意读取被加锁的数据
# read committed
事务A读取数据时会加共享锁, 获取到数据后立马释放锁, 事务B读取数据和修改数据时会加上排他锁, 事务B结束事务时会释放排他锁.
关键点抽取: 读取加共享锁, 读完立马释放, 不等事务结束
# repeatable read
事务A读取数据时会加共享锁, 直到事务结束才释放锁, 事务B读取数据也会加上共享锁, 但当事务B修改数据时想加排他锁则会等待事务A结束释放共享锁才行.
关键点抽取: 读取加共享锁, 等事务结束才释放
# serializable
事务A读取数据时会加共享锁, 直到事务结束才释放锁, 事务B读取和修改数据时会加上表级排他锁, 直到事务结束才释放.
关键点抽取: 修改数据会加表级排他锁
# 使用事务隔离级别所对应能解决的并发问题
| 事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| RU(read uncommitted) | × | × | × |
| RC(read committed) | √ | × | × |
| RR(repeatable read) | √ | √ | × |
| serializable | √ | √ | √ |
# 不同隔离级别所对应出现的问题
# RR下出现死锁
表结构为columns: id,c,d
表中已有数据为(0,0,0),(5,5,5),(10,10,10),id=9的数据行不存在
| session A | session B | |
|---|---|---|
| T1 | begin; select * from t where id=9 for update; | |
| T2 | select * from t where id=9 for update; insert into t values(9,9,9); (block) | |
| T3 | insert into t values(9,9,9); (Deadlock found) |
现在来分析上面场景出现的问题
上面问题导致的原因是间隙锁
- T1时刻session A在(5,10)之间加了间隙锁
- T2时session B也同样加了间隙锁, 间隙锁之间是不会互斥的
- 之后无论是哪个session在(5,10)之间insert都会被阻塞, 如果同时有多个线程insert, 则会deadlock, 只有第一条insert会执行成功.
避免上述问题的方案
使用read committed隔离级别并且binlog_format=row
# 实践
# 查询数据库事务隔离级别
show variables like '%isolation%';
# 设置事务隔离级别
# 查询事务隔离级别
SELECT @@session.tx_isolation;
SELECT @@global.tx_isolation;
# 设置read uncommitted级别:
set session transaction isolation level read uncommitted;
# 设置read committed级别:
set session transaction isolation level read committed;
# 设置repeatable read级别:
set session transaction isolation level repeatable read;
# 设置serializable级别:
set session transaction isolation level serializable;
# 以上是设置session级别, 如需设置全局, 则将session改为global
set global transaction isolation level repeatable read;
# 手动开启事务
# 开启事务
START TRANSACTION;
# 或者
begin;
# 提交事务
commit;
# 回滚事务
rollback;
# 实验
# 待补充
# 总结需掌握的重点
几种并发问题的典型场景
RR是如何解决幻读的
# 全文参考资料
← MySQL的@与@@区别 全局锁 →