事务不靠虑隔离性会出现的问题
- 脏读
- 不可重复读
- 幻读
脏读:一个事务读取到了另一个事务修改但是未提交的数据,例如,小明查数据时读取到了a=30,这个时候小红修改了a=40,但是并没有提交,小明再次查询发现a数据改变了
不可重复读:同一个事务在多次查询时,另一个事务修改了这个数据,并且提交,导致查询到的数据不一致
幻读:同一个事务在多次查询时,另一个事务插入或者删除了某条数据,并且提交,导致查询到的数据条数不一样,像是产生了幻觉
事务的隔离级别
- 读未提交(READ_UNCOMMITTED)读未提交,即能够读取到别的事务没有被提交的数据,所以很明显这个级别的隔离机制无法解决脏读、不可重复读、幻读中的任何一种,因此很少使用
- 读已提交(READ_COMMITED)读已提交能够读取到别的事务已经提交的数据,能够解决脏读问题
- 可重复读(REPEATABLE_READ)可重复读可以确保同一个事务在多次读取同样的数据时读到同样的结果,理论上这个隔离级别不能解决幻读问题,但是MySQL数据库中的Innodb引擎通过MVCC(多版本并发控制)和间隙锁一定程度上解决了幻读问题
- 串行化(SERLALIZABLE)最高的隔离级别,强制事务排序,强制事务串行化执行,使之不可能相互冲突,从而解决幻读问题
这里我们验证一下可重复读隔离级别是否可以解决幻读问题
首先创建一个表t,并插入几条数据
CREATE TABLE `t` (
`a` int(11) DEFAULT NULL,
KEY `idx_a` (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `test`.`t` (`a`) VALUES ('1');
INSERT INTO `test`.`t` (`a`) VALUES ('3');
INSERT INTO `test`.`t` (`a`) VALUES ('4');
INSERT INTO `test`.`t` (`a`) VALUES ('5');
INSERT INTO `test`.`t` (`a`) VALUES ('8');
开启一个事务,并查询表中的数据
START TRANSACTION;
SELECT * FROM t;
开启另一个事务,插入一条数据并提交事务
START TRANSACTION;
INSERT INTO `test`.`t` (`a`) VALUES ('2');
COMMIT;
之后第一个事务再执行查询
SELECT * FROM t ;
可以看到并没有出现幻读现象
这是因为这里的读采用的都是快照读,快照读也即是普通读,普通读的执行方式是生成 ReadView,直接利用 MVCC 机制来进行读取,并不会对记录进行加锁。快照读会读取事务当前开启时生成的快照,因此另一个事务中的插入并不会对这个快照有影响。
那如果使用的是当前读会出现什么情况?
当前读,读取的是最新版本,并且需要先获取对应记录的锁,如以下这些 SQL 类型:
select ... lock in share mode
select ... for update
update 、delete 、insert
SELECT * FROM t FOR UPDATE;
可以看到查询的结果中出现了2这条数据