隔离级别相关面试题
MySQL事务隔离级别
- 未提交读——可以读到其他事务未提交的数据(最新的版本)
- 错误现象:脏读、不可重复读、幻读的现象
- 提交读(RC)——可以读到其他事务已提交的数据(最新已提交的版本)
- 错误现象:不可重复读、幻读现象
- 使用场景:希望看到最新的有效值
- 可重复读(RR)——在事务范围内,多次读能够保证一致性(快照建立时最新已提交版本)
- 错误现象:幻读现象,可以加锁避免
- 使用场景:事务内要求更强的一致性,但看到的未必是最新的有效值
- 串行读——在事务范围内,仅有读读可以并发,读写或写写会阻塞其他事务,用这种办法可以保证更强的一致性
- 错误现象:无
脏读
事务1将隔离级别设置成 未提交读,事务2隔离级别为任意级别;
事务1开启事务,查询账户信息,查到id为1的账号余额为1000,id为2的余额为1000;
事务2开启事务,将id为1的账户余额修改为2000;
事务1再次查询账户信息,发现与之前查询的不一致了(此时事务2还未结束)。
上述便是脏读现象。
不可重复读
事务1将隔离级别设置成 提交读,事务2没有显示的开启事务,关闭事务;
事务1开启事务,查询账户信息,查到id为1的账号余额为1000,id为2的余额为1000;
事务2将id为1的账户余额修改为2000,因为没有显示开启事务,所以这条sql语句就是一个事务,事务2已经提交;
事务1再次查询账户信息,发现与之前查询的不一致了。
上述便是不可重复读现象。
幻读
事务1将隔离级别设置成可重复读,事务2没有显示的开启事务,关闭事务;
事务1开启事务,查询账户信息,查到id为1的账号余额为1000,id为2的余额为1000;
事务2插入一条id为3,余额为1000的数据,因为没有显示开启事务,所以这条sql语句就是一个事务,事务2已经提交;
事务1再次插入id为3,余额为5000的数据,发现id为3的数据已经存在了,报了主键重复。
上述便是幻读现象。
加锁避免幻读
因为事务1要添加id为3的数据,可以给不存在的这条数据查询时加一把锁,事务2添加时就会阻塞。
串行读隔离级别避免幻读
快照读与当前读
当前读
读取最新提交的数据
- select … for update
- select ... lock in share mode
- insert、update、delete,都会按最新提交的数据进行操作
当前读本质上是基于锁的并发读操作。
快照读
读取某一个快照建立时(可以理解为某一时间点)的数据,也称为一致性读。
快照读主要体现在 select (不带for update)的语句执行时;不同隔离级别下,select 的行为不同。
-
在 Serializable 隔离级别下 —— 普通 select 的sql语句也会变成当前读,即加共享读锁;
-
在 RC 隔离级别下 - 每次 select 都会建立新的快照;
-
在 RR 隔离级别下
- 事务启动后,首次 select 会建立快照
- 如果事务启动选择了 with consistent snapshot,事务启动时就建立快照
- 基于旧数据的修改操作,会重新建立快照
快照读本质上读取的是历史数据(原理是回滚段),属于无锁查询。
RR下,快照建立时机——第一次select时
事务1在第一次查询时建立快照;事务2虽然修改了id为1数据,但是RR下是基于快照读,是读到的快照建立时的数据;
如果快照建立比update完会怎样呢?
事务2先提交了update,事务1select时建立快照,就会读到事务2修改的数据。
RR下,快照建立时机——事务启动时
如果想要事务启动时建立快照,需要添加with consistent snapshot;
虽然表面上事务2比事务1的select先执行,但是事务1开启事务并建立快照先开始,读到的是未修改的数据;
RR下,快照建立时机——基于旧数据修改数据时
事务2基于旧数据修改了数据,会重新建立快照,事务1再更新,会基于2000进行修改。