事务的基本要素(ACID)
MySQL8事务隔离级别
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
读未提交(read-uncommitted) | 是 | 是 | 是 |
不可重复读(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 否 |
串行化(serializable) | 否 | 否 | 否 |
事务的并发问题
1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读 取同一数据时,结果 不一致。
3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
查询当前的mysql8的默认隔离级别为第三级别可重复读(repeatable-read)
show variables like 'transaction_isolation';
(1)打开客户端A
先将事务的隔离级别设定为读未提交 set session transaction isolation level read uncommitted;
并开启事务 start transaction;
(2)在A客户端事务提交之前,打开另一个客户端B,更新表account,将id为1的balance-50
(3)此时虽然客户端B的事务还没有提交,但是客户端A就可以查询到B已经更新的数据:
(4)此时一旦客户端B的事务因为某种原因回滚rollback,所有的操作都将会被撤销,那么客户端A查询到的数据其实就是脏数据:
(5)客户端A执行更新语句update account set balance =balance -50 where id =1; 此时id为1的balance没有变成0,而是还是50,因为事务B中发生了回滚,A中之前读取到的50就是脏数据,真实的数据还是100.
解决了脏读问题,但是出现了不可重复读问题,存在幻读问题
(1)打开客户端A设置当前的事务隔离级别为read committed (未提交读)查询表account的所有记录:
(2) 将客户端B的事务的隔离级别调整为read committed级别,并开启事务,开始更新表account
(3) 此时,客户端B的事务还没有提交,客户端A是不能查询到B已经更新的数据,解决了 脏数据的问题
(4)客户端B提交事务
(5) 客户端A执行上一次相同的查询,结果就回发现与上一次查询结果不同,就产生了不可重复读的问题
(2) 打开客户端B将事务的隔离级别调整为repeatable read级别,并对表account进行更新,提交
(3)在客户端A查询表account的所有记录就回发现,无论客户端B是否提交查询的数据结果都是一致,没有出现不可重复读的问题
(4)在客户端A,接着执行update account set balance =balance -50 where id =1;balance 没有变为100-50=50
id =1 的balance变成了0,数据的一致性得到保证。可重复读的隔离级别下是用来 MVCC机制,select 操作不会更新版本号
是快照读(历史版本);insert、update和delete会更新版本号,是当前读(当前版本)。
(5)同时发现了一个问题MySQL8已经不出现幻读的情况了,打开客户端B,尝试插入一条新数据发现根本就不会提交
(1)在客户端A中设置当前事务模式为Serializable
(2)打开客户端B,并设置当前事务模式为serializable,插入一条记录报错,mysql中事务隔离级别为serializable时会锁表,
客户端A就不可能对account表进行任何操作,同理反过来,A中如果先查询,会发生锁表现象,B表中的任何操作也会被拒绝
发生锁表,因此不可能出现脏读数据、不可重复读、出现幻读的情况,这种隔离级别并发性比较低,开发中很少使用。
Serializable
完全串行化的读,每次读都需要获得表级共享锁,读写相互会相互互斥,这样可以更好的解决数据一致性的问题,但是同样会大大的降低数据库的实际吞吐性能。所以该隔离级别因为损耗太大,一般很少在开发中使用。
补充:
1、事务隔离级别为读提交(read committed 不可重复读,第二级别)时,写数据只会锁住相应的行
2、事务隔离级别为可重复读时(repeatable read ,第三级别),如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有 索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。
3、事务隔离级别为串行化时(serializable ,第四级别),读写数据都会锁住整张表
4、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。