一.事务隔离级别:
事务具有ACID属性,而事务的隔离级别可以不同程度的解决事务并发时可能产生的问题,可以根据不同业务逻辑需求,来选择不同的事务隔离等级,事务隔离等级越高,越能保证数据的一致性,但就更趋近于串行化,降低并发性能,导致效率变低。
四种事务隔离级别:
读未提交(RU) 在这种隔离级别下,脏读、幻读、不可重复读都不能解决
读已提交(RC) 在这种隔离级别下,解决了脏读。
可重复读(RR) 在这种隔离级别下,解决了脏读、不可重复读。
序列化/串行化(Serializable) 在这种隔离级别下,解决了脏读、幻读、不可重复读。
从上往下,隔离强度逐渐增强,性能逐渐变差。采用哪种隔离级别要根据系统需求权衡决定,其中,可重复读是 MySQL 的默认级别。这四种隔离级别可以在不同程度上解决事务在并发时产生的问题——脏读,不可重复读,幻读。
1.脏读:有AB两个事务,B事务对一条数据进行修改,但未提交;而A事务在这之后对同一条数据进行读操作,读到的若是未提交的修改后的数据,就说明产生了脏读现象。
2.不可重复读:有一个A事务和若干其他事务,A事务对一条数据(或多条符合同一查询条件的数据)进行多次的读操作;其他的事务对A事务所读的数据进行了修改(这里不包括插入新的数据),若是未提交,在RU级别可以读到修改的数据,若是已提交,在RC级别及以下可以读到修改的数据;那么这种情况就可能会导致A事务在多次读取数据的时候,发现读取的数据值不一致,这种现象被称为不可重复读。
3.幻读:幻读的概念在网上很难找到一个全面且完全正确的解释,这里的解释是我个人的理解,可幻读是针对数据插入(INSERT)操作来说的。假设事务A对某些行的内容作了更改,但是还未提交,此时事务B插入了与事务A更改前的记录相同的记录行,并且在事务A提交之前先提交了,而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用,但其实是事务B刚插入进来的,让用户感觉很魔幻,感觉出现了幻觉,这就叫幻读
MySQL事务隔离级别
事务隔离其实就是为了解决上面提到的脏读、不可重复读、幻读这几个问题,下面展示了 4 种隔离级别对这三个问题的解决程度。
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
读未提交(read-uncommitted) | 是 | 是 | 是 |
读已提交(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是 |
序列化/串行化(Serializable) | 否 | 否 | 否 |
只有串行化的隔离级别解决了全部这 3 个问题,其他的 3 个隔离级别都有缺陷。
1.未提交读(RU):
1.1实现机制:
事务在读数据的时候采用当前读
事务在修改数据的时候加共享锁,提交后释放(解决了修改时,数据被删除或修改的情况)
1.2脏读情况:会发生
创建一个表含以下信息并查看
再创建一个命令提示符对话框我们一共需要两个对话框解决问题接下来用A和B来解说两个框
左边是事务A框,右边是事务B框。
我们可以通过以下语句查看当前数据库的隔离级别AB都要有
show variables like 'transaction_isolation';
SELECT @@transaction_isolation
REPEATABLE-READ,也就是可重复读,这也是 MySQL 的默认级别AB都要有。
稍后,我们要修改数据库的隔离级别(两个对话一样的操作)
set session transaction isolation level read uncommitted;(修改隔离级别)
修改后再次查询
开始事务A
BEGIN; -- 或者使用 START TRANSACTION;
A事务查询表信息:
select * from runoob_shiwu2;
再次开启事务B BEGIN; -- 或者使用 START TRANSACTION;
BEGIN;
打开B事务插入新数据,但不提交事务
INSERT INTO runoob_shiwu2 VALUES(1,1,2);
打开A事务再次查询此表:读到了B事务未提交的操作,脏读情况发生,依旧读到了未提交的修改后的数据
select * from runoob_shiwu2;
1.3不可重复读情况:会发生
A事务查询id为30的数据
select * from runoob_shiwu2 where id=30;
B事务对id为30的数据进行修改:提交与否不影响结果
update runoob_shiwu2 set num = 100 where id=30;
A事务再次查询id为30的数据,与第一次读的num数据值不同,A事务在多次读取数据的时候,发现读取的数据值不一致,不可重复读情况发生
select * from runoob_shiwu2 where id=30;(查询)
1.4幻读情况:会发生
A事务查询id<30的数据,
select * from runoob_shiwu2 where id<30;
B事务插入一条id<30的数据,提交与否不影响结果
INSERT INTO runoob_shiwu2 values(10,10,3);
再次查询 A事务再次查询id<30的数据:查到的数据比第一次多一条,幻读情况发生
select * from runoob_shiwu2 where id<30;
2.已提交读(RC):
2.1实现机制:
事务在读数据的时候加读锁(当前读),读完即释放共享锁
事务在修改某数据时会加上写锁,直到事务结束再释放,这样的机制保证了RC隔离级别不会发生脏读,只有提交过的事务,才能被其他事务看见
2.2脏读情况:不会发生
和上面一样打开两个事务窗口AB,然后把事务隔离级别设置为RC,A,B一样
mysql> set session transaction isolation level read committed;
开始事务并查询表的信息A,B一样
B事务插入新数据,但不提交:
A再次查询 没有读到B事务插入的数据,脏读情况没有发生
2.3不可重复读情况:会发生
RC隔离级别下的不可重复读的情况和RU是类似的,只是B事务在修改数据的时候,需要提交。
COMMIT:事务确认,提交事务,使更改永久生效
2.4幻读情况:会发生
RC隔离级别下的幻读的情况和RU是类似的,只是B事务在插入数据的时候,需要提交。
COMMIT:事务确认,提交事务,使更改永久生效
3.可重复读(RR):
3.1脏读情况:不会发生
首先是开启分别开启两个会话,后续会用A事务和B事务来代指这两个会话,然后把事务隔离级别设置为RR。
set session transaction isolation level repeatable read;
RR隔离级别下的脏读的情况和RC一样
3.2不可重复读情况:不会发生
开始事务begin;
A事务查询id为30的数据:
B事务对id为30的数据进行修改:修改后提交事务
A事务再次查询id为30的数据:与第一次读的num数据值相同,没有发生不可重复读的情况
3.3幻读情况:会发生
开始事务
A事务查询表的全部数据
B事务插入一条新数据:需要提交事务
A事务插入和B事务一样的数据
A事务查询全部数据一开始可能会不显示多次查询就会出现幻读
4.序列化(Serializable):
实现机制:
事务在读取数据时,对整个表加读锁,提交或回滚事务后释放
事务在修改数据时,对整个表加写锁,提交或回滚事务后释放
这是最高的隔离级别,可以解决脏读,不可重复读和幻读,但同时效率也是最差的一个。它解决这些由于事务并发带了的问题的方法就是把这些操作变成串行操作,一旦不符合条件,就会被阻塞,所以效率特别差。
这个不用验证了,所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰了,一般不用,性能特别低。
参考