以下几个概念是事务隔离级别要实际解决的问题,所以需要搞清楚都是什么意思。
脏读
脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并一定最终存在的数据,这就是脏读。
可重复读
可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据都是一致的。通常针对数据更新(UPDATE) *** 作。
不可重复读
对比可重复读,不可重复读指的是在同一事务内,不同的时刻读到的同一批数据可能是不一样的,可能会受到其他事务的影响,比如其他事务改了这批数据并提交了。通常针对数据更新(UPDATE) *** 作。
幻读
幻读是针对数据插入(INSERT) *** 作来说的。假设事务A对某些行的内容作了更改,但是还未提交,此时事务B插入了与事务A更改前的记录相同的记录行,并且在事务A提交之前先提交了,而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用,但其实是事务B刚插入进来的,让用户感觉很魔幻,感觉出现了幻觉,这就叫幻读。
事务隔离级别SQL 标准定义了四种隔离级别,MySQL 全都支持。这四种隔离级别分别是:
- 读未提交(READ UNCOMMITTED)读提交 (READ COMMITTED)可重复读 (REPEATABLE READ)串行化 (SERIALIZABLE)
可重复读是 MySQL 的默认级别。
二、开始测试 前提准备查看隔离级别
show variables like '%tx_isolation%'
数据库表
DROP TABLE IF EXISTS `goods`; CREATE TABLE `goods` ( `goods_id` int(8) NOT NULL AUTO_INCREMENT, `goods_name` varchar(255) DEFAULT NULL, PRIMARY KEY (`goods_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 34 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
默认表中有一条数据
读未提交
首先设置全局隔离级别为读未提交。
set global transaction isolation level read uncommitted;
两事务中 *** 作的执行顺序如下:
A事务开启线程查询
A事务修改
B事务开启线程查询,拿到的数据是banana。
A事务此时rollback,表中存储数据是apple
上面B产生了脏读,要想解决脏读这个问题可以采用读已提交的隔离级别
读已提交首先设置全局隔离级别为读已提交。
set global transaction isolation level read committed;
两事务中 *** 作的执行顺序如下:
A开启线程查询
B开启线程查询,查询与A一致,都是表中数据;
A线程修改数据
B线程查询,A事务未提交,B中查询仍是apple
A线程提交
B线程查询得到提交后数据
读提交解决了脏读的问题,但是无法做到可重复读
可重复读
首先设置全局隔离级别为可重复读。
set global transaction isolation level repeatable read;
(下图过程测试实现可重复读)
A开启线程查询
B开启线程查询,查询与A一致,都是表中数据;
A线程修改数据并提交
B线程在同一线程内查询,得到结果未变。与可重复读的概念理解一致,就是同一事务里不同时间点查询,不会受到其他事务修改数据的影响。
此时C新开启线程查询,读到就是修改后的数据
(下图过程测试产生否幻读的问题)
A开启线程查询
B开启线程插入数据并提交
A线程再次查询,没有查到3 cherry
A线程插入3 cherry,报主键=3已经存在。
此时就产生了幻读,A事务本身查询看不到主键=3的数据,但插入时却仍报主键冲突。
串行化A开启线程查询
B开启线程插入数据,
总结:读未提交会产生脏读问题;
为了解决脏读,使用读已提交,会产生可不重复读问题;
为了解决不重复读,使用可重复读;
MySQL 已经在可重复读隔离级别下解决了幻读的问题,通过行锁和间隙锁的组合 Next-Key 锁实现。
最简单粗暴的方式是串行,影响性能最大,故很少使用;
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)