“读现象”是多个事务并发执行时,在读取数据方面可能碰到的状况,了解它们有助于咱们理解各隔离级别的含义,其中包括脏读、不可重复读和幻读。
脏读
脏读又称无效数据的读出,是指在数据库访问中,事务T1
将某一值修改,然后事务T2
读取该值,此后T1
因为某种原因撤销对该值的修改,这就导致了T2
所读取到的数据是无效的。
说白了,脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据就是脏数据,根据脏数据所做的操作可能是不正确的。
举例说明:
在下面的例子中,事务T2
修改了一行,但是没有提交,事务T1
读了这个没有提交的数据。现在如果事务T2
回滚了刚才的修改或者做了另外的修改的话,那么事务T1
中查到的数据就是不正确的啦!
T1 | T2 |
---|---|
SELECT age FROM users WHERE id = 1; /* will read 20 */ | |
UPDATE users SET age = 21 WHERE id = 1; /* No commit here */ | |
SELECT age FROM users WHERE id = 1; /* will read 21 */ | |
ROLLBACK; /* lock-based DIRTY READ */ |
在这个例子中,事务T2
回滚后就没有id
是1
、age
是21
的这条数据了,所以事务T1
就读到了一条脏数据。
不可重复读
不可重复读,是指在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。这是由于查询时系统中其他事务修改的提交而引起的。比如事务T1
读取某一数据,事务T2
读取并修改了该数据,T1
为了对读取值进行检验而再次读取该数据,便得到了不同的结果。
一种更易理解的说法是:在一个事务内,多次读同一个数据。在这个事务还没有结束时,另一个事务也访问该同一数据。那么,在第一个事务的两次读数据之间。由于第二个事务的修改,那么第一个事务读到的数据可能不一样,这样就发生了在一个事务内两次读到的数据是不一样的,因此称为不可重复读,即原始读取不可重复。
举例说明:
在基于锁的并发控制中“不可重复读(non-repeatable read)”现象发生在当执行SELECT
操作时没有获得读锁(read locks)或者SELECT
操作执行完后马上释放了读锁;在多版本并发控制中,当没有要求一个提交冲突的事务回滚的时候,也会发生“不可重复读(non-repeatable read)”现象。
T1 | T2 |
---|---|
SELECT * FROM users WHERE id = 1; | |
UPDATE users SET age = 21 WHERE id = 1; COMMIT; | |
SELECT * FROM users WHERE id = 1; COMMIT; |
在这个例子中,事务T2
提交成功,因此他对id
为1
的记录修改就对其他事务可见了,但是事务T1
在之前已经从这条记录读到了另外一个age
的值啦!
幻读
幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的“全部数据行”。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入“一行新数据”。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。一般解决幻读的方法是增加范围锁 RangeS,锁定检锁范围为只读,这样就可以避免幻读啦!
“幻读(phantom read)”是“不可重复读(Non-repeatable reads)”的一种特殊场景:当事务没有获取范围锁的情况下,执行SELECT XXX WHERE XXX
操作就可能会发生“幻读(phantom read)”。
举例说明:
当事务T1
两次执行SELECT XXX WHERE XXX
检索一定范围内数据的操作中间,事务T2
在这个表中创建了一行新数据,这条新数据正好满足事务T1
的WHERE
子句。
T1 | T2 |
---|---|
SELECT * FROM users WHERE age BETWEEN 10 AND 30; | |
INSERT INTO users VALUES ( 3, 'Bob', 27 ); COMMIT; | |
SELECT * FROM users WHERE age BETWEEN 10 AND 30; |
在这个例子中,事务T1
执行了两次相同的查询操作,但是两次操作中间事务T2
向数据库中增加了一条符合事务T1
的查询条件的数据,导致幻读。
解决方案
要想解决脏读、不可重复读和幻读等读现象,那么就需要提高事务的隔离级别。但与此同时,事务的隔离级别越高,并发能力也就越低。所以,还需要大家根据业务需要进行权衡。
转载声明:本文转自「Hollis」,数据库的读现象浅析。