解决的办法同样是做好隔离操作,在一个写入完成之前,禁止其他事务的读入。事实上更新丢失是并发场景下最容易出现的错误,而且如果设计不合理,出现了错误也会非常难排查。数据库解决隔离性问题的办法就是设置不同的隔离级别,不同的隔离级别对应不同的隔离策略,可以保证不同级别下的隔离性。不同的隔离级别意味着使用不同级别的锁,显然隔离级别越高意味着性能越差。所以这就需要数据库管理员(DBA)对于当前的应用场景,以及并发量和数据风险有一个非常清楚的认知。能够在性能和安全性之间做一个权衡。这里,我们不多做具体的探究,观察一下下图,简单了解一下即可:
从上到下以此是四种隔离级别,越往下隔离级别越高,能够解决的隔离性问题也就越多。同样的,用到的锁也就越多,系统的性能也就越差。最上面未提交读是最低的隔离级别,在读取的时候并不会判断是否可能会读取到没有提交的数据。所以它的隔离性最差,连最简单的脏读都无法解决。已提交读则是通过锁限制了只会读取已经提交的数据,读数据的时候使用的共享锁,在读取完成之后立即释放。这种隔离级别只能够解决最常见的脏读问题,它也是SQL server数据库的默认隔离级别。可重复读的读取过程和已提交级别一样,但是在读取的时候会保持共享锁,一直到事务结束。也就是说只要一个事务没有结束,锁就不会释放。其他的事务无法更新数据,保证了不会出现不可重复读的情况。最后是可串行读,它是在可重复读的基础上进一步加强了隔离性。在事务进行当中,不仅会锁定受影响的数据本身,而且还会锁定整个范围。这就阻止了其他事务影响整体的情况出现。在这个隔离级别下,保证了事务之间不会有任何踩踏。到这里,数据库事务四大原则当中的三个就介绍完了,内容看起来不少,但其实还没有结束,关于隔离的实现会牵扯到锁的使用,这块深挖下去,又会牵扯许多内容。不过对于我们算法从业者而言,能够了解到这一层,也差不多够了。四原则当中还剩下一个一致性原则,一致性这个单词在很多地方都出现过,比如分布式存储系统、多副本的一致性等等。但是这些概念的意思并不相同,不可以简单地理解成同一回事。数据库的一致性表示数据的状态是正确的,在转移的时候,是从一个正确的状态转移到了另一个正确的状态。正确的状态其实就是指不出错的状态,也就是和程序员预期一致的状态。之前在介绍隔离性时谈到的种种问题,总结起来都是数据和程序员的预期不一致。也就是说如果和程序员的预期一致,就可以认为满足了一致性。虽然一致性是数据库的四原则之一,但数据库系统当中并没有专门针对一致性的部分。其实在数据库眼中,满足了其他三原则,那么自然也就达成了一致性。一致性是目的,并不是手段。举个例子,还是以刚刚转账的情景距离。A向B转账100,我们都知道,前提条件是A的账户里的金额大于等于100,如果A账户里小于100,我们开发的时候没有做校验还强行转账成功。那么这个结果显然是错误的,也是和我们预期不一致的,但是这个问题发生的原因并不是因为数据库没有做好一致性,而是开发人员忽略了限制条件。所以数据库的教材上才会写着“Ensuring the consistency is the responsibility of user, not DBMS.", "DBMS assumes that consistency holds for each transaction”。“保证一致性是开发的责任,而不是数据库的,数据库假设每一个事务都符合 一致性。”到这里,数据库事务的四原则就介绍完了,衷心祝大家,日拱一卒,每天都有收获。