Transaction弱隔离之读提交的介绍和实现

我们在上文《Transactions的基本概念和介绍》中提到,transaction的一个作用就是可以做到隔离。比如说当一个translation在写一个数据的同时,如果有另外一个transaction在同时读或写,就有可能会有问题。而且这种问题很难发现和debug,所以一般来说这种transaction级别的隔离都是由数据库本身来保证的,理论上来说这样应用的开发者就不需要来操心具体的细节了。可惜理想很丰满,现实却很骨感。很多时候数据库本身因为各种原因(性能等因素),它提供的不是严格的隔离,而是一种弱隔离,简单来说就是能保证一些同时读写的操作,但是有一些却不能保证。这样一来开发者就有点懵了,我究竟该何去何从呢?本文就来详细给大家介绍一下常见的transaction弱隔离,相信了解了这些之后,你再去开发应用就会游刃有余了。

读提交(Read Committed)

所谓的读提交其实是最基本的一种transaction隔离,它做到了以下两点保证:

  1. 当读数据库的时候,只会读到已经提交的数据。(没有脏读)
  2. 当写数据库的时候,只会覆盖已经写了的数据。(没有脏写)

没有脏读

所谓没有脏读就是当有一个transaction写一个数据到数据库的时候,只要它还没有提交,那么别人就不能读到它。

如下图所示,user1的transaction做了两件事,一个是把x写成3,一个是把y写成3。整个transaction只会在这两件事都完成的时候才会提交。现在我们来看,当user1把x写成3的时候,user2正好来读,因为User1的整个transaction还没有提交,所以这个时候User2读到的x的值还是2而不是3,这就是没有脏读。

很显然,没有脏读可以有两个好处,一个是在整个transaction的过程中,别的用户不会看到不一致的两个状态。另外一个好处就是假如这个transaction在后续的过程中出问题了,那么前面已经完成写操作会被roll back,如果缺少了“没有脏读”的保证,那么就可能出现有用户看到这个被roll back的数据了。

没有脏写

这个保证主要是用来出来两个同时发生的写的。一般来说,我们可以说有两个写同时发生,我们希望后面发生的写会覆盖前面发生的写。但是当有transaction存在的情况就有点不同,假如一个transaction有多个操作,在这个transaction还没有提交的过程中,又同时发生了一个新的写,是否还要覆盖一个没有提交的写呢?假如覆盖了,那么就是一个脏写。一般来说都是通过让第二个写延迟到第一个transaction提交之后再执行。

我们来看一个例子,我们假设这是一个购车系统,要买id为1234这辆车需要做两件事情,一件是更新它的buyer名字,一个是产生一个发票。我们需要这两件事在一个transaction里面发生。假如缺少了“没有脏写”的保证就会发生下图所示的问题,listings中的数据在中间被改成了bob,而发票则被写成了Alice,从而产生问题。

读提交的实现

我们理解了读提交的概念之后,再来看看一般我们怎么才能做到上面这些保证呢?

对没有脏写来说,通常的实现方法是使用一个锁:当一个transaction要写一个object的时候,首先会获取这个锁,直到它整个transaction完成或者终止才会释放这个锁。这样一来别的想要写的操作就需要等待这个锁,从而可以避免脏写。

那么如何避免脏读呢?第一想法可能也比较简单,就是读的时候也需要去获取上面写抓住的锁,从而就可以保证不会读到脏的数据。但是这种方法一个最大的问题就是假如有一个transaction在写一个object,所有的读都需要在那边等待,直至写完成,这显然是一个消耗很大性能很低的实现。

那一般如何来避免脏读了,其实很简单就是我们同时保存一份旧的数据和新的数据,在写transaction没有完成的时候,任何读不会被block,但是它读到的时候transaction更新之前的旧的数据。只有当整个transaction完成之后,才会读到新的数据。

总结

本文介绍了Transaction弱隔离中的读提交的基本概念和实现方法。

You may also like...

2 Responses

  1. June 8, 2021

    […] 我们在前文《Transaction弱隔离之读提交的介绍和实现》和《Snapshot的隔离和Repeatable的读》中介绍了read commit和snapshot隔离。这两篇文章其实更多地讨论的是当有写发生的时候如何进行读,除了脏写之外没有更多地讨论如何处理两个写同时发生的情况。其实当有两个写同时发生的时候还会有很多问题需要处理,其中最常见的就是两个写有冲突,这个时候就会发生更新丢失的问题。 […]

  2. June 10, 2021

    […] 我们在前面的《Transaction弱隔离之读提交的介绍和实现》和《Transaction弱隔离之更新的丢失》中分别介绍了脏写和更新丢失。他们都是有两个写同时发生,从而产生了冲突。那么对于这种情况,我们必然需要进行保护和处理,可以是数据库来自动处理也可以是手动的加一些保护比如锁或者原子写操作等。然而上面提到的两种写冲突就是全部了吗?现实世界显然更加残酷,本文就来具体看看别的冲突的例子。 […]

Leave a Reply

Your email address will not be published. Required fields are marked *