事务隔离级别
数据库事务的隔离级别有4个,由低到高依次为:
- Read uncommitted(读未提交)
- Read committed(读提交)
- Repeatable read (重复读)
- Serializable(串行化)
这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题,具体见下表。
√: 可能出现 ×: 不会出现
事务的隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted 读未提交 | √ | √ | √ |
Read committed–读提交 Sql Server , Oracle默认 | × | √ | √ |
Repeatable read–重复读 MySQL默认 | × | × | √ |
Serializable 串行化 | × | × | × |
注意:我们讨论隔离级别的场景,主要是在多个事务并发的情况下,因此,接下来的讲解都围绕事务并发。
Read uncommitted 读未提交
Read Uncommitted是限制性最弱的隔离级别,因为该级别忽略其他事务放置的锁。使用Read Uncommitted级别执行的事务,可以读取尚未由其他事务提交的修改后的数据值,这些行为称为“脏”读。这是因为在Read Uncommitted级别下,读取数据不需要加S锁,这样就不会跟被修改的数据上的X锁冲突。
比如,事务1修改一行,事务2在事务1提交之前读取了这一行。如果事务1回滚,事务2就读取了一行没有提交的数据,这样的数据我们认为是不存在的。
当隔离级别设置为Read uncommitted时,就可能出现脏读,看以下案例。
公司发工资了,领导把5000元打到 A 的账号上,但是该事务并未提交,而 A 正好去查看账户,发现工资已经到账,是5000元整,非常高兴。可是不幸的是,领导发现发给 A 的工资金额不对,是2000元,于是迅速回滚了事务,修改金额后,将事务提交,最后 A 实际的工资只有2000元,A 空欢喜一场。
两个并发的事务,“事务1:领导给 A 发工资”、“事务2:A 查询工资账户”,事务2读取了事务1尚未提交的数据,即我们所说的脏读。
Read committed 读提交
Read committed(Nonrepeatable reads)是SQL Server、Oracle默认的隔离级别。该级别通过指定语句不能读取其他事务已修改但是尚未提交的数据值,禁止执行脏读。在当前事务中的各个语句执行之间,其他事务仍可以修改、插入或删除数据,从而产生无法重复的读操作,或“影子”数据。
比如,事务1读取了一行,事务2修改或者删除这一行并且提交。如果事务1想再一次读取这一行,它将获得修改后的数据或者发现这一样已经被删除,因此事务1的第二次读取结果与第一次读取结果不同,因此也叫不可重复读。
A 拿着工资卡去消费,系统读取到卡里确实有2000元,而此时她的老婆也正好在网上转账,把A工资卡的2000元转到另一账户,并在A之前提交了事务,当A扣款时,系统检查到 A 的工资卡已经没有钱,扣款失败,A 十分纳闷,明明卡里有钱的。
两个并发的事务,“事务1:A消费”、“事务2:A 的老婆网上转账”,事务1事先读取了数据,事务2紧接了更新了数据,并提交了事务,而事务1再次读取该数据时,数据已经发生了改变,出现了不可重复读。
当隔离级别设置为Read committed时,避免了脏读,但是可能会造成不可重复读。大多数数据库的默认级别就是Read committed(读提交),比如 Sql Server , Oracle
Repeatable read 重复读
当隔离级别设置为Repeatable read时,可以避免不可重复读。当 A 拿着工资卡去消费时,一旦系统开始读取工资卡信息(即事务开始),A 的老婆就不可能对该记录进行修改,也就是 A 的老婆不能在此时转账。
避免不可重复读,“事物1:A 消费”,“事物2:A的老婆网上转账”,事务1开始,未提交,事物2不能进行。
虽然Repeatable read避免了不可重复读,但还有可能出现幻读。
A 的老婆工作在银行部门,她时常通过银行内部系统查看 A 的信用卡消费记录。有一天,她正在查询到 A 当月信用卡的总消费金额(select sum(amount) from transaction where month = 本月)为80元,而 A 此时正好在外面胡吃海塞后在收银台买单,消费1000元,即新增了一条1000元的消费记录(insert transaction … ),并提交了事务,随后 A 的老婆将 A 当月信用卡消费的明细打印到A4纸上,却发现消费总额为1080元,A 的老婆很诧异,以为出现了幻觉,幻读就这样产生了。
注:MySQL的默认隔离级别就是Repeatable read(重复读)
Serializable 串行化
Serializable是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻读。
关注微信公众号:【皮卡战记】
