隔离性与隔离级别
ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性)
隔离级别:
1.读未提交(RU)是指,一个事务还没提交时,它做的变更就能被别的事务看到。
2.读提交(RC)是指,一个事务提交之后,它做的变更才会被其他事务看到。(Oracle 默认级别)
3.可重复读是指(RR),一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。(mysql默认级别)
4.串行化(SERIALIZABLE ),顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
MVCC
多版本并发控制,同一条记录在系统中可以存在多个版本,即不同时刻启动的事务会有不同的 read-view
假设一个值从 1 被按顺序改成了 2、3、4,在回滚日志里面就会有类似下面的记录。
当前值是 4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的 read-view。如图中看到的,在视图 A、B、C 里面,这一个记录的值分别是 1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。对于 read-view A,要得到 1,就必须将当前值依次执行图中所有的回滚操作得到。
回滚段/undo log
每条记录在更新的时候都会同时记录一条回滚操作
当系统里没有比这个回滚日志更早的 read-view ,回滚日志会被删除
视图
在 MySQL 里,有两个“视图”的概念:
1.一个是 view。它是一个用查询语句定义的虚拟表,在调用的时候执行查询语句并生成结果。创建视图的语法是 create view … ,而它的查询方法与表一样。
2.另一个是 InnoDB 在实现 MVCC 时用到的一致性读视图,即 consistent read view,用于支持 RC(Read Committed,读提交)和 RR(Repeatable Read,可重复读)隔离级别的实现。
例:
表:
mysql> CREATE TABLE `t` (
`id` int(11) NOT NULL,
`k` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1),(2,2);
事务 A 查到的 k 的值是 1,事务 B 查到的 k 的值是 3
MVCC实现一致性读视图
InnoDB 里面每个事务有一个唯一的事务 ID,叫作 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请的,是按申请顺序严格递增的。数据表中的一行记录,可能有多个版本 (row),每个版本有自己的 row trx_id。可以通过 undo log计算每个版本的数据(当系统里没有比这个回滚日志更早的 read-view ,回滚日志会被删除,因为不需要回滚)。
InnoDB 为每个事务构造了一个数组,用来保存这个事务启动瞬间,当前正在“活跃”的所有事务 ID。“活跃”指的就是,启动了但还没提交。
数组里面事务 ID 的最小值记为低水位,当前系统里面已经创建过的事务 ID 的最大值加 1 记为高水位。
这个视图数组和高水位,就组成了当前事务的一致性视图(read-view)。
而数据版本的可见性规则,就是基于数据的 row trx_id 和这个一致性视图的对比结果得到的。
这个视图数组把所有的 row trx_id 分成了几种不同的情况。
这样,对于当前事务的启动瞬间来说,一个数据版本的 row trx_id,有以下几种可能:
1.如果落在绿色部分,表示这个版本是已提交的事务或者是当前事务自己生成的,这个数据是可见的;
2.如果落在红色部分,表示这个版本是由将来启动的事务生成的,是肯定不可见的;
3.如果落在黄色部分,那就包括两种情况
a. 若 row trx_id 在数组中,表示这个版本是由还没提交的事务生成的,不可见;
b. 若 row trx_id 不在数组中,表示这个版本是已经提交了的事务生成的,可见。
为了简化分析,我先把其他干扰语句去掉,只画出跟事务 A 查询逻辑有关的操作:
事务 A 查询语句的读数据流程是这样的:
1.找到 (1,3) 的时候,判断出 row trx_id=101,比高水位大,处于红色区域,不可见;
2.接着,找到上一个历史版本,一看 row trx_id=102,比高水位大,处于红色区域,不可见;
3.再往前找,终于找到了(1,1),它的 row trx_id=90,比低水位小,处于绿色区域,可见。
一个数据版本,对于一个事务视图来说,除了自己的更新总是可见以外,有三种情况:
1.版本未提交,不可见;
2.版本已提交,但是是在视图创建后提交的,不可见;
3.版本已提交,而且是在视图创建前提交的,可见。
当前读
只能读当前的值,称为“当前读”(current read)。
- update 语句
- 加锁:加上 lock in share mode 或 for update
读已提交与可重复读区别
1.在可重复读隔离级别下,只需要在事务开始的时候创建一致性视图,之后事务里的其他查询都共用这个一致性视图;
2.在读提交隔离级别下,每一个语句执行前都会重新算出一个新的视图。
例如
这时,事务 A 的查询语句的视图数组是在执行这个语句的时候创建的,时序上 (1,2)、(1,3) 的生成时间都在创建这个视图数组的时刻之前。但是,在这个时刻:
(1,3) 还没提交,属于情况 1,不可见;
(1,2) 提交了,属于情况 3,可见。
所以,这时候事务 A 查询语句返回的是 k=2。
显然地,事务 B 查询结果 k=3。