事务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 的行为不同。
在 Serializable 隔离级别下 —— 普通 select 的sql语句也会变成当前读,即加共享读锁;
在 RC 隔离级别下 - 每次 select 都会建立新的快照;
在 RR 隔离级别下
快照读本质上读取的是历史数据(原理是回滚段),属于无锁查询。
事务1在第一次查询时建立快照;事务2虽然修改了id为1数据,但是RR下是基于快照读,是读到的快照建立时的数据;
如果快照建立比update完会怎样呢?
事务2先提交了update,事务1select时建立快照,就会读到事务2修改的数据。
如果想要事务启动时建立快照,需要添加with consistent snapshot;
虽然表面上事务2比事务1的select先执行,但是事务1开启事务并建立快照先开始,读到的是未修改的数据;
事务2基于旧数据修改了数据,会重新建立快照,事务1再更新,会基于2000进行修改。