事务的并发带来了什么问题
当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
例子:
1. 张三初始工资1000元,人事修改为2000 但是未提交
2. 张三读取工资 发现修改为2000
3. 人事发现修改错误,回滚了提交。张三再读发现变成了1000
张三读到的2000就是脏数据
指事务不独立执行时发生的现象,表现为新增删除的时候。 事务A操作所有数据,变更了所有行的数据。然后事务B新增了一条数据,A事务读数据发现了一条未被修改的数据,好像出现了幻觉
例子:
1. 公司所有人工资统一为1000元。事务A为每人加了100 变为1100
2. 事务B 人事新增一名员工,工资为1000元。
3. 事务A 再次读取发现还有一个人的工资没更新 而且人也多了一个
如果能避免在事务提交前 禁止添加新数据,则能避免出现该问题
在事务A中先读取一个值,然后事务B更新了此值,事务A中在读这个值就会变得不一致,同一个事务中前后读一个值返回的不一致
例子:
1. 张三初始工资为1000 事务A中 张三读取到1000
2. 事务B 人事修改为2000 提交。
3. 事务A 再次读取工资发现变成了2000,前后数据不一致
不可重复读:同一事务中前后读取数据不一致 重点在于修改,幻读在于新增和删除
为了解决上面的问题,mysql提出了四种隔离级别
事务隔离级别
- read uncommitter 未提交读
- read committer 提交读
- repeatable read 可重复读
- serializable 串行化
下面模拟集中情况来解释说明集中级别
CREATE TABLE `test` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`account` float DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
insert into test (account) values(1000);
insert into test (account) values(1000);
Read uncommitter
打开两个会话 会话1中执行
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
select * from test;
结果为:
会话2中执行 未提交事务
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
UPDATE test SET account = 1200 WHERE id = 1;
会话1中执行
select * from test
结果:
结论
read uncommitter 为最低的事务隔离级别。即使其他事务未提交也能读取到脏数据,但是读到的数据可能没有真正的发生改变,因为只有事务commit后才会提交到数据库
Read committer
打开两个会话 会话1中执行
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
select * from test;
结果为:
会话2中执行 未提交事务
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
UPDATE test SET account = 1200 WHERE id = 1;
会话1中执行
select * from test
结果
会话2中执行
commit
会话1中执行
select * from test
结果
结论
read committer 不会读取到其他未提交事务的数据,可以避免脏读,但是同一事务1条数据可能有多个值
repeatable read
打开两个会话 会话1中执行
set session transaction isolation level repeatable read;
start transaction;
select * from test;
结果为:
会话2中执行 未提交事务
set session transaction isolation level repeatable read;
start transaction;
UPDATE test SET account = 1200 WHERE id = 1;
会话1中执行
select * from test
结果
会话2中执行
commit
会话1中执行
select * from test
结果
会话1执行
commit;
select * from test
insert into test (id,account) value(3,1000);
结果 会插入失败
结论
repeatable Read 可以可重复读 同一事务多次读取数据不会变,但是如果外部确实已经更新或者添加,会出错或者更改。
Serializable
将事务等级开启到最高,会串行化执行,所有其他的事务等待挂起。
如果超时(这个时间可以进行配置),会出现Lock wait time out提示
结论
当我们将当前会话的隔离级别设置为serializable的时候,其他会话对该表的写操作将被挂起。可以看到,这是隔离级别中最严格的,但是这样做势必对性能造成影响。所以在实际的选用上,我们要根据当前具体的情况选用合适的隔离级别。
小结
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
read uncommittor | 可能 | 可能 | 可能 |
read committor | 不可能 | 可能 | 可能 |
repeatable read | 不可能 | 不可能 | innodb不可能 |
serializable | 不可能 | 不可能 | 不可能 |