数据库读脏、不可重复读、幻读

当多个事务并发执行时,在读取数据库数据时会遇到:数据库读脏、不可重复读、幻读。了解它们有助于理解各隔离级别的含义。

一、事务的隔离级别

事务:是指作为单个逻辑工作单元执行的一系列操作,要么全做,要么不做。 

事务的ACID特性:原子性、一致性、持久性、隔离性。

在数据库事务的ACID四个属性中,隔离性是一个最常放松的一个。可以在数据操作过程中利用数据库的锁机制或者多版本并发控制机制获取更高的隔离等级。但是,随着数据库隔离级别的提高,数据的并发能力也会有所下降。所以,如何在并发性和隔离性之间做一个很好的权衡就成了一个至关重要的问题。不同的隔离级别会导致和解决不同的读现象。

二、读脏

读脏,即读到了脏数据,无效的数据。

当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交(commit)到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。

举个例子,当我在本地修改某个对数据库操作类的代码后,还没有提交,这时,其他人用了我的代码,这就是读脏数据。因为我没有提交代码,随时可能修改代码,而其他人用刚才的代码获取的就是脏数据了。

这种情况下,多个开发者并发性很高,其他人随时可以读取我的代码,但是极容易读脏数据,所以我们代码的隔离性很差。

我们并不希望代码在未提交的情况下,被其他人读取,因此我们需要提高代码的隔离性。 

三、不可重复读

为了解决读脏数据的问题,我们通过提高代码的隔离性来实现。在我修改代码时,不允许其他人读取访问我的代码,只有在提交代码后才能访问,这样子就不会读脏数据。

这样子提高了代码之间的隔离性,并发性就降低了,因为需要等我把代码提交后其他人才能读取。但是会存在不可重复读问题。

再举个例子:张三和我同时访问某个类的代码,其中的一个常量被我改动并提交了,当张三再次访问这段代码时,得到的内容不一样,这就是不可重复读。

不可重复读。是指在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。这是由于查询时系统中其他事务修改的提交而引起的。比如事务T1读取某一数据,事务T2读取并修改了该数据,T1为了对读取值进行检验而再次读取该数据,便得到了不同的结果。

要解决不可重复读的问题,就需要在张三读取代码的时候,我不能对其进行访问,这样子就解决了不可重复读的问题,代码的隔离性又提高了,并发性降低了。

四、幻读

当张三在读某个类的代码时,我对这个类不进行修改,解决了不可重复读的问题,但是我增加了一些类,在提升了隔离性之后,虽然我不会修改张三正在读的类,张三也不会读我正在修改的类。但是我可能会增加或者删除几个类。这时候和张三之前读取到的类的总个数就有了变化。也就是说,张三之前读到的数据就不准确了。这就是幻读。

幻读。指同一个事务内多次查询返回的结果集不一样(比如增加了或者减少了行记录)。比如同一个事务A内第一次查询时候有n条记录,但是第二次同等条件下查询却又n+1条记录,这就好像产生了幻觉。

解决幻读就是当张三在读代码的时候,我不对代码进行任何操作,我们之间的隔离性最高,并发性最低。

要想解决脏读、不可重复读、幻读等读现象,那么就需要提高事务的隔离级别。但与此同时,事务的隔离级别越高,并发能力也就越低。所以,还需要读者根据业务需要进行权衡。

事务的隔离性上,从低到高可能产生的读现象分别是:脏读、不可重复读、幻读。

脏读指读到了未提交的数据。

不可重复读指一次事务内的多次相同查询,读取到了不同的结果。

幻读师不可重复读的特殊场景。一次事务内的多次范围查询得到了不同的结果。

通过在写的时候加锁,可以解决脏读。

通过在读的时候加锁,可以解决不可重复读。

通过串行化,可以解决幻读。

以上这几种解决方案其实是数据库的几种隔离级别。

猜你喜欢

转载自blog.csdn.net/vandet100/article/details/87775420