锁的三种问题(脏读,幻读,丢失)

1、简介

  经过锁机制能够实现事务的隔离性要求,使得事务能够并发地工做。锁提升了并发,可是却带来了问题。不过好在由于事务隔离性的要求,锁只会带来三种问题,若是能够防止这三种状况的发生,那将不会产生并发异常。算法

2、脏读

  页和脏数据的区别:脏页是指在缓冲池中已经被修改的页,可是尚未刷新到磁盘中,即数据库实例内存中的页和磁盘中的页的数据不一致,固然在刷新到磁盘以前,日志都已近被写入到重作日志文件中。脏数据是指事务对缓冲池中行记录的修改,而且尚未被提交(commit)。数据库

  脏页是由于数据库实例内存和磁盘的异步形成的,这并不影响数据的一致性(或者说二者最终会达到一致性,即当脏页都刷回到磁盘中)。而且由于脏页的刷新是异步的,不影响数据库的可用性,所以能够带来性能的提升。markdown

  脏数据则不一样,若是读到了脏数据,即一个事务能够读到另外一个事务中未提交的数据,则显然违反了数据库的隔离性。并发

  脏读指的就是在不一样的事务下,当前事务能够读到另外事务未提交的数据异步

  以下显示了一个脏读的例子:性能

  

  上述例子中可发现,在会话A中,在事务没有提交的前提下,会话B中的两次SELECT操做取得了不一样的结果,而且2这条记录是在会话A中并未提交的数据,即产生了脏读,违反了事务的隔离性spa

  脏读现象在生产环境中并不常发生,脏读发生的条件是须要事务的隔离级别为READ UNCOMMITTED。InnoDB为READ REPEATABLE,SQL server为READ COMMITTED,Oracle为READ COMMITTED日志

3、幻读(不可重复读)

  不可重复读是指在一个事务内屡次读取同一数据集合。在这个事务还没结束时,另一个事务也访问该同一数据集合,并作了一些DML操做。所以,在第一个事务中的两次读数据之间,因为第二个事务的修改,那么第一个事务两次读到的数据可能不一致。这样就发生了在一个事务内两次读到的数据不同,这种状况称为不可重复读。code

  不可重复度和脏读的区别是:脏读读到未提交的数据,而不可重复读读到的是已经提交的数据,但其违反了数据库事务一致性。以下例子:orm

 

 

  通常来讲,不可重复读的问题是能够接受的,由于读到是已经提交的数据。

  InnoDB中,经过使用Next-Key Lock算法来避免不可重复读的问题。在这种算法下,对于索引的扫描,不只是锁住扫描到的索引,并且还锁住这些索引覆盖的范围(gap)。所以这个范围内的插入都是不容许的,这样来避免不可重复读的现象。

4、丢失更新 

  丢失更新是另外一个锁致使的问题,就是一个事务的更新操做会被另外一个事务的更新操做锁覆盖,从而致使数据的不一致。例如:

  1)事务T1将行记录R更新为V1,事务T1并未提交。

  2)同时,事务T2将行记录R更新为V2,事务T2未提交。

  3)T1提交

  4)T2提交

  在当前数据库的任何隔离级别下,都不会致使数据库理论上的丢失更新问题。这是由于,即便是READ UNCOMMITTED的事务隔离级别,对于行的DML操做,须要对行或者其余粗粒度级别的对象加锁。所以在上述2)步骤中,事务T2并不会对行记录R进行更新操做,由于其会阻塞,直至事务T1提交。

  虽然数据库能阻止丢失更新问题,但在生产应用中确有逻辑意义上的丢失更新问题,而致使问题的并非由于数据库自己的问题。以下状况就会发生丢失更新:

  1)事务T1查询一行数据,放入本地内存,并显示给一个终端用户User1

  2)事务T2也查询该行数据,并将取得的数据显示给用户User2

  3)User1修改这行记录,更新数据库并未提交。

  4)User2修改这行记录,更新数据库并未提交。

  要避免丢失更新发生,须要让事务在这种状况下的操做变成串行化,而不是并行的操做。即在1)中,对用户读取的记录加上排它锁X。一样,在2)中,也加上一个排它锁X。经过这种方式,2)就必须等待1)和3)完成,最后完成4)。如图:

  

相关文章
相关标签/搜索