本文转载请联系飞天小牛公众号。如上所述,“锁”是区分数据库系统和文件系统的关键特性。它的对象是事务,用于锁定数据库中的对象,如表、页和行。锁确实提高了并发性,但是不可避免地存在一些潜在的并发一致性问题。幸运的是,锁只会带来四种问题(丢失更新、脏读、不可重复读和幻读)。如果能避免这四种情况的发生,就不会出现并发异常。为此,ISO和ANISSQL标准制定了四种事务隔离级别标准,用于相应解决由锁引起的几种问题。锁引起的四种并发一致??性问题LostupdateLastToModifylostupdate非常好理解。简单的说就是一个事务的更新操作会被另一个事务的更新操作覆盖,导致数据不一致。例如:1)事务T1将行记录r更新为v1,但是事务T1没有提交2)同时,事务T2将行记录r更新为v2,事务T2没有提交3)事务T1提交了4)事务T2commit如下图,很明显,事务T1丢失了修改。但是,实际上,这并不完全发生。因为我们说过更新一行时,需要对该行或者其他粗粒度的对象加锁,所以当事务T1修改了行r但没有提交时,事务T2就会更新行r。阻塞直到事务T1提交释放锁。因此,从数据库层面来看,数据库本身可以帮助我们防止丢失更新问题的发生。然而在真实的开发环境中,我们经常会遇到逻辑意义上的丢失更新。例如:1)事务T1查询一行数据r,放入本地内存,显示给一个用户User12)事务T2也查询了这行数据,并将得到的数据显示给另一个用户User23)User1修改了rowrecordrisv1,updatethedatabaseandsubmit4)User2修改了rowrecordrisv2,updatedatabaseandsubmit显然,这条行记录的最终值为v2,User1的更新操作被User2覆盖,他的修改丢失了。也许它仍然在迷雾中,但让我给你一个更现实的例子。一个部门一起检查在线文档。员工A发现自己的性别信息有误,于是将“女”改成了“男”。这时HR也发现员工A的部门信息有误,于是将其由“测试”改为“开发”。然后员工A和HR同时点了submit,但是HR的网络有点慢。再次刷新,员工A会发现,哇,我的性别怎么还是“女”?脏读DirtyRead所谓脏读,就是一个事务读取了另一个事务中的“脏数据”。提交的数据如下图所示。在事务未提交的前提下,事务T1中的两次SELECT操作结果不同:注意,如果要重现脏读的情况,需要调整隔离级别为ReadUnCommitted(读未提交)。所以其实脏读基本不会发生,因为现在大部分数据库的隔离级别至少设置为READCOMMITTED不可重复读Unrepeatableread不可重复读是指在一个事务中多次读取同一个数据集。在本次事务结束之前,另一个事务也访问了同一个数据集,并进行了一些修改操作。因此,在第一个事务的两次读取之间,由于第二个事务的修改,第一个事务读取的数据可能不同。例如:事务T1读取一行数据r,T2将该行数据修改为v1。如果T1再次读取这行数据,此时读取的结果与第一次读取的结果不同。不可重复读和脏读的区别在于:脏读是读取未提交的数据,而不是读取的是已提交的数据,但是违反了事务一致性的要求。PhantomReadPhantomRead幻读本质上是不可重复读。不同的是,不可重复读主要是针对数据的更新(即事务的两次读取结果的值不同),而幻读主要是针对数据的增减(即事务的两次读取结果返回不同的数字)例如:事务T1读取了某个范围内的数据,事务T2在这个范围内插入了一些新的数据,然后T1再次读取数据读取这个范围内的数据。此时读取结果返回的记录数比第一次读取的结果多。四种事务隔离级别标准SQL标准定义了四种越来越严格的事务隔离级别,用于解决我们上面提到的四种事务的并发性和一致性问题。1)READUNCOMMITTEDreaduncommitted:事务中的修改,即使没有提交,对其他事务也是可见的。上面说了,数据库本身已经具备防止丢失更新的能力,即即使是最低的隔离级别也可以防止丢失更新。所以:这个隔离级别可以防止丢失更新2)READCOMMITTED读已提交:一个事务只能读取已提交事务所做的修改。换句话说,一个事务所做的更改在提交之前对其他事务不可见。这个隔离级别可以防止丢失更新+脏读3)REPEATABLEREAD可重复读(InnoDB存储引擎默认的隔离级别):保证同一个事务多次读取同一个数据的结果是一样的。这个隔离级别可以防止丢包更新+脏读+不可重复读4)SERIALIZABLSerializable:强制事务串行执行(需要使用锁机制来实现),让多个事务互不干扰,并且有不会出现并发一致性问题。这个隔离级别可以防止丢失更新+脏读+不可重复读+幻读。可以看到四个隔离级别可以防止越来越多的并发一致性问题,但是并不是说隔离级别越高越好,因为事务隔离级别越高,数据库的性能开销就越大。另外,多提一点,InnoDB存储引擎在REPEATABLEREAD事务隔离级别下使用了Next-KeyLock算法来避免幻读。也就是说,InnoDB存储引擎在其默认的REPEATABLEREAD事务隔离级别下完全可以保证事务的隔离要求,即达到了SQL标准的SERIALIZABLE隔离级别。
