# mysql的事务隔离级别介绍

# 简述

关于事务隔离级别在接触mysql时就已经了解过了,有几个种类,会带来哪几种问题,但每次到实际场景时,却又不记得什么样的隔离级别会造成什么样的问题,应该如何解决. 其实每次重新查资料再分析都是可以解决问题的, 但是这中间又是重复推理学习的过程, 所以对于已经理解的东西, 就将自己的心路历程记录下来, 不仅可以带给他人帮助也帮助自己思考. 这里想一遍将事务隔离级别讲清楚.

# mysql引擎与事务的关系

只有InnoDB支持事务 , 会有另外文章解析与myisam的比较

# 数据库事务的四个特征

即ACID

  1. 原子性(Atomicity)
  2. 一致性(Consistency)
  3. 隔离性(Isolation)
  4. 持久性(Durability)

# 并发事务会带来的问题

其他文章会详细介绍这四种问题

  1. 脏读

一句话: 能读到未提交事务的数据变化

解决方案:

  1. 设置read committed以上的事务隔离级别
  1. 不可重复读

一句话: 多次读取数据一直在变化

解决方案:

  1. 设置repeatable read以上的事务隔离级别
  2. 由要进行修改操作的事务显示使用排他锁
  3. 或者由重复读的事务显示使用共享锁
  1. 幻读

一句话: 多次读取数据, 数据的数量一直在变化

解决方案:

  1. 设置serializable以上的事务隔离级别
  1. 数据丢失

# mysql的事务隔离级别种类

  1. read uncommitted
  2. read committed
  3. repeatable read
  4. serializable

# 事务隔离级别的所依赖的技术

事务隔离级别依赖MVCC和数据库锁, 以下做简单介绍, 深入了解可以再查找资料.

# MVCC

MVCC即多版本并发控制

MVCC实质上是给每一行数据增加了3个字段: DB_TRX_ID、DB_ROLL_PTR、DB_ROW_ID. 也可以说给每一行加上了两个字段: 创建时间, 过期时间(删除时间)

Innodb中的RC和RR是基于MVCC实现的高性能事务, 另外文章会介绍快照读相关内容解释如何实现的高性能.

# 共享锁(S)和排他锁(X)

  1. 共享锁 mysql锁介绍

  2. 排他锁 mysql锁介绍

# 分别简述事务隔离级别

简述事务隔离级别的实现原理, 以下都是以两个事务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)

现在来分析上面场景出现的问题

上面问题导致的原因是间隙锁

  1. T1时刻session A在(5,10)之间加了间隙锁
  2. T2时session B也同样加了间隙锁, 间隙锁之间是不会互斥的
  3. 之后无论是哪个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;

# 实验

# 待补充

# 总结需掌握的重点

  1. 几种并发问题的典型场景

  2. RR是如何解决幻读的

# 全文参考资料

地址1 (opens new window)

地址2 (opens new window)

地址3 (opens new window)

掘金-全面了解mysql锁机制(InnoDB)与问题排查 (opens new window)

浅谈Mysql共享锁、排他锁、悲观锁、乐观锁及其使用场景 (opens new window)

修改于: 8/11/2022, 3:17:56 PM