本文主要讨论数据库事务隔离级别及原理,然后从以下四点展开讨论:事务隔离的概念以葫芦娃为例,帮助理解脏读、不可重复读和幻读数据库常见的四种隔离级别“快照读”概念事务隔离概念任何支持事务的数据库都必须具备四个特性,即:原子性(Atomicity)一致性(Consistency)隔离以上四点隔离和持久性也称为事务ACID。数据库具有以上特点,保证了交易中数据的正确性。其中,事务的隔离是指事务之间相互独立,不会相互影响,即一个事务内使用的操作和数据与其他并发事务隔离。没有事务隔离会出现什么问题?如果没有事务隔离,可能会出现脏读、不可重复读、幻读等。为了帮助和加深理解,我们以大家耳熟能详的“葫芦娃”为例。起初,藤上有七个葫芦。每当有葫芦娃出生,爷爷就会将自己的信息录入数据库表中,如下:有一天,第四个娃出生了。事务A(爷爷)访问数据库,将刚出生的葫芦娃的信息添加到数据库中,但是事务还没有提交。insertintoTvalues(4,'四个宝宝','喷水');这时又来了一个事务B(蛇精),她进行了查询操作,想查询所有出生的葫芦娃的信息select*fromT;//这时候,如果事务之间没有有效隔离,蛇精查询数据库返回的结果中会出现Siwa的信息,是脏读的。(1)脏读:指读取自己事务中未提交的数据第二天,事务A(蛇精)一大早就查询数据库中关于siwa的信息select*fromTwhereID=4;//名称:siwa能力:喷水这时候交易B(爷爷)来了,因为爷爷发现Siwa其实可以喷火而不是喷水,所以更新更改Siwa的喷火能力,然后提交事务。updateTsetAbility='喷火'whereID=4;接下来,蛇精(事务A)想要再次查看确认Siwa的信息,于是执行了select*fromTwhereID=4;//Name:SiwaAbility:Spray这时,她惊奇的发现,第四个的技能宝宝的信息被读了两遍!这是不可重复的阅读。(2)不可重复读:指在自己的事务中读取两次,前后数据不同。第三天,事务A(蛇精)访问了数据库。她想看看到底生了哪些葫芦娃。于是她执行了select*fromT;//此时一共找出了四个葫芦宝宝的信息,因为第五个宝宝出生了,所以事务B(爷爷)打开数据库,将第五个宝宝的信息输入到insertintoTvalues(5,'五','喷水');此时事务A(蛇精)想要查询所有已经出生的葫芦娃的所有信息进行确认,于是再次执行查询select*fromT;//一共找出5个葫芦娃信息this到时候,事件A(蛇精)可能就上当了,以为自己出现了幻觉。这种情况称为“幻读”。(3)幻读:指自己事务中两次连续查找之间的并发修改事务修改了查询数据集,导致两次查询返回不同的结果(注:不可重复读与幻读很相似。)不可重复读的重点是修改,而幻读的重点是增删)数据库的隔离级别为了避免上述情况,我们可以设置数据库的隔离级别(组合选择最合适的级别以实际场景为准。)一般的数据库包括以下四种隔离级别:ReadUncommittedReadCommittedRepeatedReadSerializable这里以MySQL为例,在MySQL中,事务隔离级别分为以下四种:Level0:TRANSACTION_READ_UNCOMMITTED一切皆有可能发生Level1:TRANSACTION_READ_COMMITTED--不能发生脏读,可以发生不可重复读和幻读Level2:TRANSACTION_REPEATABLE_READ--NoDirtyreadsandrepeatedreadscanbeoccur,andphantomreadscanoccurLevel3:TRANSACTION_SERIALIZABLE--都不能发生(1)Readuncommitted(TRANSACTION_READ_UNCOMMITTED)Readuncommitted,表示可以读取未提交的内容。由于该隔离级别下的查询不会被锁定,因此可能会出现“脏读”、“不可重复读”和“幻读”。在实际开发中,除非有特殊情况,基本不会用到这个隔离级别。(2)Readcommit(TRANSACTION_READ_COMMITTED)Readcommit表示只能读取提交的内容。这是最常用的隔离级别,也是Oracle和SQLServer的默认级别,可以有效避免脏读。(注意:除非显示共享锁、排他锁等锁,否则查询默认是不加锁的。与“readuncommitted”不同,“readcommitted”因为使用了“snapshotread”可以避免脏读)(3)Repeatableread(TRANSACTION_REPEATABLE_READ)可重复读,这个级别可以有效避免“不可重复读”,也是MySQL数据库innodb的默认级别。在这一层,普通查询也使用“快照读”,但与“读提交”不同的是,事务开始时不允许进行Update操作,而“不可重复读”是由于两次读之间的数据修改造成的获取。因此,“可重复读”可以有效避免“不可重复读”,但“幻读”却无法避免,因为“插入或删除操作”会产生幻读。(4)序列化(TRANSACTION_SERIALIZABLE)序列化是数据库的最高隔离级别。在这个层次上,事务序列化按照队列顺序一个一个执行,可以避免脏读、不可重复读和幻读。安全性高对应执行效率低,性能开销最大,因此在实际开发中很少使用。快照读数据库读分为:一致非锁读、锁读,上面说的“快照读”是非锁读。可以简单理解为执行SELECT语句时会生成一个快照。注意:不同事务隔离级别下的快照读取是有差异的:在READCOMMITTED隔离级别下,事务中的每一次读取都会重新生成一个快照,所以每个快照都是最新的。因此,每次在事务中执行SELECT时,也可以看到其他已提交事务所做的更改。因为是读取快照,所以有效避免了脏读。并且假设如果没有使用“锁读”的“快照读(一致非锁读)”,那么当一个更新事务没有提交时,另一个查询更新数据的事务会因为查询不到而被阻塞。这种情况下,并发能力和效率都比较差。在REPEATEDREAD隔离级别下,快照会在事务中执行第一条SELECT语句时产生,只有在本次事务中数据发生变化时才会更新快照。因此,只能看到在第一个SELECT之前其他提交的事务所做的更改。总结事务的隔离性就是事务之间相互独立,不会相互影响,即一个事务内使用的操作和数据与其他并发事务隔离。不做事务隔离可能会出现脏读、幻读、不可重复读。结合实际情况设置合理的隔离级别,可以有效避免上述问题。数据库中常见的隔离级别有四种:readuncommitted、readcommitted、repeatableread和serialization,其中readcommitted在实际开发中用得比较多。其中,引入了一个“读取快照”的概念。需要注意的是,不同隔离级别下的“读快照”是有区别的。通过使用“读取快照”,提高并发操作的效率。
