事务的7种传播行为4种隔离级别以及脏写脏读不可重复读幻读
一、四种隔离级别:
数据库事务的隔离级别有4个,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。
第1级别:Read Uncommitted(读未提交内容)
(1)所有事务都可以看到其他未提交事务的执行结果
(2)本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少
(3)该级别引发的问题是——脏读(Dirty Read):读取到了未提交的数据
第2级别:Read Committed(读已提交内容)
(1)这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)
(2)它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变
(3)这种隔离级别出现的问题是——不可重复读(Nonrepeatable Read):不可重复读意味着我们在同一个事务中执行完全相同的select语句时可能看到不一样的结果。
|——>导致这种情况的原因可能有:(1)有一个交叉的事务有新的commit,导致了数据的改变;(2)一个数据库被多个实例操作时,同一事务的其他实例在该实例处理其间可能会有新的commit
第3级别:Repeatable Read(可重读)
(1)这是MySQL的默认事务隔离级别
(2)它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行
(3)此级别可能出现的问题——幻读(Phantom Read):当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行
(4)InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题
MVCC
多版本并发控制(Multi-Version Concurrency Control, MVCC)是 MySQL 的 InnoDB 存储引擎实现隔离级别的一种具体方式,用于实现提交读和可重复读这两种隔离级别。而未提交读隔离级别总是读取最新的数据行,要求很低,无需使用 MVCC。可串行化隔离级别需要对所有读取的行都加锁,单纯使用 MVCC 无法实现。
- 在 MVCC 中事务的修改操作(DELETE、INSERT、UPDATE)会为数据行新增一个版本快照。
版本号
1)系统版本号 SYS_ID:是一个递增的数字,每开始一个新的事务,系统版本号就会自动递增。
2)事务版本号 TRX_ID :事务开始时的系统版本号。
第4级别:Serializable(可串行化)
(1)这是最高的隔离级别
(2)它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。
(3)在这个级别,可能导致大量的超时现象和锁竞争(所以一般不使用这个解决幻读)
二、脏写、脏读、不可重复读和幻读
1、脏写、脏读
脏写,就是我刚才明明写了一个数据值,结果过了一会却没了。而它的本质就是事务 B 去修改了事务 A 修改过的值,但是此时事务 A 还没提交,所以事务 A 随时会回滚,导致事务 B 修改的值也没了,这就是脏写的定义。
脏读。它的本质是事务 B 去查询了事务 A 修改过的数据,但是此时事务 A 还没提交,所以事务 A 随时会回滚导致事务 B 再次查询就读不到刚才事务 A 修改的数据了,这就是脏读。
其实总结一句话,无论是脏写还是脏读,都是因为一个事务去更新或者查询了另外一个还没提交的事务更新过的数据。因为另外一个事务还没提交,所以它随时可能会回滚,那么必然导致你更新的数据就没了,或者你之前查询到的数据就没了,这就是脏写和脏读两种场景。
2、不可重复读
A在查询时,同时有B,C进行数据的更新并且事务提交,这时A的多次查询结果不同。
其实要说没问题也可以是没问题的,毕竟事务 B 和 事务 C 都提交之后,事务 A 多次查询查到它们修改的值,是 OK 的。但是你要说有问题,也可以是有问题的,就是事务 A 可能第一次查询到 A 值,那么它可能希望的是在事务执行期间,如果多次查询数据,都是同样的一个 A 值,它希望这个 A 值是它重复读取的时候一直可以读到的。它希望这行数据的值是可重复读的
但是此时,明显 A 值是不可重复读的。因为事务 B 和事务 C 一旦更新值并且提交了,事务 A 会读到别的值,所以此时这行数据的值是不可重复读的。此时对于你来说,这个不可重复读的场景,就是一种问题
3、幻读
简单来说,你一个事务 A,先发送一条 SQL 语句,里面有一个条件,要查询一批数据出来,如 SELECT * FROM table WHERE id > 10
。然后呢,它一开始查询出来了 10 条数据。
接着事务 A 此时第二次查询,再次按照之前的一模一样的条件执行 SELECT * FROM table WHERE id > 10
这条 SQL 语句,由于其他事务插入了几条数据,导致这次它查询出来了 12 条数据。
于是事务 A 开始怀疑自己的眼镜了,为什么一模一样的 SQL 语句,第一次查询是 10 条数据,第二次查询是 12 条数据?难道刚才出现幻觉了?这就是【幻读】
解决幻读:(在可重复读的隔离级别下)
【Next-Key Locks 】是 MySQL 的 InnoDB 存储引擎的一种锁实现。
MVCC 不能解决幻影读问题,Next-Key Locks 就是为了解决这个问题而存在的。在可重复读(REPEATABLE READ)隔离级别下,使用 MVCC + Next-Key Locks 可以解决幻读问题。
Record Locks
锁定一个记录上的索引,而不是记录本身。如果表没有设置索引,InnoDB 会自动在主键上创建隐藏的聚簇索引,因此 Record Locks 依然可以使用。
Gap Locks
锁定索引之间的间隙,但是不包含索引本身。例如当一个事务执行以下语句,其它事务就不能在 t.c 中插入 15。SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE;
Next-Key Locks
它是 Record Locks 和 Gap Locks 的结合,不仅锁定一个记录上的索引,也锁定索引之间的间隙。它锁定一个前开后闭区间,例如一个索引包含以下值:10, 11, 13, and 20,那么就需要锁定以下区间:(-∞, 10]
(10, 11]
(11, 13]
(13, 20]
(20, +∞)
三、事务的七种传播特性
事务的传播特性共有7种:
1、PROPAGATION_REQUIRED:支持当前事务,如果不存在就新建一个(默认) ;
2、PROPAGATION_REQUIRES_NEW:开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起;
3、PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
4、PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果有事务存在,挂起当前事务;
5、PROPAGATION_MANDATORY:必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常;
6、PROPAGATION_NEVER:以非事务方式运行,如果有事务存在,抛出异常;
7、PROPAGATION_NESTED:如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于封装事务进行提交或回滚。如果封装事务不存在,行为就像PROPAGATION_REQUIRES一样。
传播特性的作用
传播特性主要控制是使用老的事务,还是创建新的事务,或者事务是否运行在上下文中。
评论区