InnoDB 事务隔离机制

2019-07-23  本文已影响0人  smartmhs

1、事务四大特性:ACID

Atomicity 原子性:事务的操作要么一起成功,要么一起失败;

Consistency 一致性:一致性是对数据可见性的约束,一个事务多次操作数据的中间状态对其他事务不可见;

Isolation 隔离性:多个事务并发执行时,一个事务不应该受到其他事务的影响,数据库支持不同的隔离级别来满足不同场景的需求;

Durability 持久性:事务完成之后,所有的操作结果都保存到了数据库之中,不会丢失,不能回滚;

2、事务并发产生的问题

脏读:对于两个事务 T1 和T2,T1读取了已经被T2更新但还没有被提交的字段,之后若T2进行回滚,T1读取的内容就是临时且无效的;

不可重复读:对于两个事务 T1 和 T2 , T1 读取了一个字段,然后T2 更新了该字段,之后T1再次读取同一个字段,值就不同了;

幻读:对于两个事务 T1,T2,T1 从表中读取了一个字段,然后T2在该表中插入了一些新的行,之后T1再次读取同一个表,就会多出几行;

不可重复读 和 幻读 的区别:不可重复读针对的是更新和删除操作,幻读针对的是插入操作,比如:T1 正在操作一条记录,如果加锁,T2 就不能对这条记录进行更新和删除,这就避免了 不可重复读,但无法避免 T2 插入新的数据,也就是无法避免幻读,InnoDB 中通过 gap 锁来解决幻读问题,这里我们不讨论 gap 锁;

3、InnoDB 如何解决事务并发问题

InnoDB 为了解决事务并发导致的脏读、不可重复读、幻读问题,提供了事务隔离机制,共有四种隔离级别:

读未提交:一个事务还未提交,他的变更就能被其他事务看到,这个级别就是没有任何隔离;

读已提交:一个事务的变更,只有在提交后才能被其他事务看到;

可重复读:一个事务执行过程中看到的数据,总是跟这个事务启动时看到的数据一致,即使数据被其他事务更改并提交,也是不可见的;

串行化:对于同一行记录,写会加写锁,读会加读锁,当出现读写锁冲突时,后访问的事务必须等之前的事务执行完成才能继续执行;

隔离级别越高,数据一致性越能得到保障,但并发性也就越低,MySQL 默认的隔离级别是 可重复读;

针对隔离级别的相关操作:

-- 查看隔离级别
show variables like 'transaction_isolation';

-- 设置当前会话隔离级别
set session transaction isolation level read uncommitted;
set session transaction isolation level read committed;
set session transaction isolation level repeatable read;
set session transaction isolation level serializable;

-- 设置整个库的隔离级别
set global transaction isolation level read uncommitted;
set global transaction isolation level read committed;
set global transaction isolation level repeatable read;
set global transaction isolation level serializable;

4、InnoDB 隔离级别的实现

InnoDB 的四种隔离级别,读未提交不需要做任何操作,做任何操作都读当前最新的值就可以了,串行化是严格的互斥操作,通过加锁来实现,读已提交和可重复读则通过 MVCC 来实现,下面我们重点研究 MVCC 的实现原理;

4.1 两种读模式

4.2 undo log

这里我们不做 undo log 的详细工作原理的研究,只需要知道 MVCC 需要根据 undo log 来实现多版本数据的查找就可以了;

4.3 表的隐藏表字段

4.4 一致性视图

以上就是一致性视图的工作原理;
RR 隔离级别是在事务执行第一条快照读语句时创建一致性视图的;
RC 隔离级别则每次执行快照读语句是都会创建最新的一致性视图;

4.5 例程

执行序号 session1 session2 session3
1 start transaction;
2 start transaction;
3 insert into test(num) values(100);
4 insert into test(num) values(101);
5 select num from test;
6 insert into test(num) values(102);
7 insert into test(num) values(103);
8 insert into test(num) values(104);
9 select num from test;
10 select num from test;
11 select num from test;

最终,三个 session 中的查询语句结果分别是多少?

  1. 假设 session1 中的事务 id 为 5,则 session2 中事务 id 为 6,session3 中四个事务的 id 分别为 7、8、9、10(MySQL 中不显示指定事务时,默认一条语句为一个事务);
  2. 根据以上假设:
    • session1 中第 5 行创建一致性视图时,活跃事务队列为(5, 6),低水位为 5,高水位为 7,由于 session2 的事务 id 在队列中,因此 session1 的事务对插入的 101 不可见,因此第 5 行的查询结果为 100;
    • session3 中第 9 行创建一致性视图时,活跃事务队列为(5, 6, 10),低水位为 5,高水位为 11,它不可见前面两个事务插入的数据,因此查询的结果为 102、103、104;
    • session2 中第 10 行创建一致性视图时,活跃事务队列为(5, 6),低水位为 5,高水位为 11(此时全局最大的事务 id 为 10),它对 session3 的几个事务插入的数据均可见,但对 session1 插入的数据不可见,因此查询的结果为 101、102、103、104;
    • session1 中第 11 行,由于之前已经创建过一致性视图了,因此这里不再创建,查询结果还是 100;
    • 以上操作均在 RR 隔离级别下进行,若是 RC 隔离级别,则每次快照读都会创建最新的一致性视图,具体结果可自行推导;

5、参考资料

上一篇下一篇

猜你喜欢

热点阅读