一、脏读、不可重复读、幻读
-
脏读
读取了未提交的事务。
事务A读取了事务B中尚未提交的数据。如果事务B回滚,则A读取使用了错误的数据。
解决:如果一个事务在读的时候,禁止读取未提交的事务。 -
不可重复读
读取了提交的新事物,指更新操作。
期望两次读的结果一样,但中途另一个事务修改了数据并提交了,导致第二次读的结果变了。
解决:如果一个事务在读的时候,禁止任何事务写。 -
幻读
也是读取了提交的新事物,指增删操作。
不可重复是针对记录的update操作,只要在记录上加写锁,就可避免;幻读是对记录的insert操作,禁止幻读必须加上全局的写锁。比如在表上加写锁。
例:
事务A,先执行:
update table set name="hky" where id > 3
事务B,后执行,并且提交:
insert into table values(11, 'uu')
事务A,再select一下:
select * from table where id > 3
结果集:11,uu
事务A懵了,我tm刚更新完id>3的啊?!幻觉?!
由此,也可以看出不可重复读和幻读区别,不可重复读操作是本身记录读取产生的,幻读则是一个范围。
二、事务隔离级别
数据库事务的隔离级别有4个,由低到高依次为Read uncommitted 、Read committed 、Repeatable read 、Serializable ,这四个级别可以逐个解决脏读 、不可重复读 、幻读这几类问题。
-
Read uncommitted(读未提交)
最低的隔离级别。一个事务可以读取另一个事务并未提交的更新结果。也就是会出现脏读。 -
Read Committed(读提交)
大部分数据库采用的默认隔离级别。如oracle,写操作加写锁,读操作加读锁。
一个事务的更新操作结果只有在该事务提交之后,另一个事务才可以的读取到同一笔数据更新后的结果。
也就是一个事务开始读了,另一个事务开始写,读只能读到另个事务未提交之前的数据。 -
Repeatable Read(重复读)
mysql的默认级别。
整个事务过程中,对同一批数据的读取结果是相同的,不管其他事务是否在对共享数据进行更新,也不管更新提交与否。这里涉及到一个问题,例如mysql,那innoDB是怎么解决幻读的?
mysql默认级别RR下,innoDB使用MVCC和next-key locks解决幻读。MVCC解决的是普通读(快速读)的幻读,next-key locks解决的是当前读情况下的幻读。(1) 当前读:
所谓当前读,是指加锁的select,update,delete等语句。在RR事务隔离级别下,数据库会使用next-key locks来锁住本条记录及索引区间。
以上述幻读的例子来说,在RR情况下,假设使用的是当前读,加锁了读select * from table where id>3 ,锁住的就是id=3这条记录及id>3这条区间范围,锁住索引记录之间的范围,避免范围内插入记录,避免产生幻读行记录。(2) 普通读:
普通读是不加锁的读,解决幻读的手段是MVCC。
MVCC会给每行元组加一些辅助字段,记录创建版本号和删除版本号。
而每个事务在启动的时候,都有一个唯一的递增的版本号。开启新事务,事务版本号就会递增。- SELCT
读取创建版本小于或等于当前事务版本号,并且删除版本为空或大于当前事务版本号的记录。这样就可以保证在读取之前记录是存在的。 - INSERT
将当前事务的版本号保存至行的创建版本号 - UPDATE
新插入一行,并以当前事务的版本号作为新行的创建版本号,同时将原记录行的删除版本号设置为当前事务版本号。 - DELETE
将当前事务的版本号保存至行的删除版本号
具体参考链接:
https://mp.weixin.qq.com/s/GoJIYQqqiNBamFnwiFoRXw
https://www.cnblogs.com/wudanyang/p/10655180.html - SELCT
- Serializable(序列化)
最高隔离级别。所有事务操作依次顺序执行。注意这会导致并发度下降,性能最差。通常会用其他并发级别加上相应的并发锁机制来取代它。