日期:2010-08-30 浏览次数:20689 次
ACID性质是数据库理论中的奠基石,它定义了一个理论上可靠数据库所必须具备的四特性质:原子性,分歧性,隔离性和持久性。虽然这四特性质都很重要,但是隔离性最为灵活。大部分数据库都提供了一些可供选择的隔离级别,且如今许多库都添加了附加层来创建颗粒度更细的隔离。隔离级别使用范围如此之广次要是由于放宽隔离约束往往会使得可扩展性和功用提高几个数量级。
串行分歧性是可用的最陈旧最高的隔离级别之一,它之所以倍受青睐是由于其提供的简单编程模型,即每次仅能有一个事务对给定的资源进行操作,这就避免了很多潜在的资源问题。虽然如此,大部分使用程序(尤其是Web使用程序)都不采用这种级别非常高的隔离,由于从终端用户的角度来看这是不切实际的-任何一个拥有大量用户群的使用程序在访问共享资源时都将会有几分钟的延迟,而这会使得用户量迅速减少。弱分歧性和最终分歧性在大规模分布式数据源中,例如Web中,随处可见。好几个成功的大型Web使用(例如,eBay和Amazon)都显示出乐观的(optimistic)弱分歧性要比传统悲观的(pessimistic)机制在扩展性方面好得多。本文将一窥八种不同的隔离级别。学会适当的放宽数据分歧性的约束,你可以在本人的使用程序中使用这八种隔离级别来获得更好的功用和可扩展性。
并发控制的次要目标是为了确保事务被隔离且不会影响到其他事务。要达到高级别的隔离需以牺牲功用为代价。并发控制可以用悲观或者乐观的机制来实现。大部分关系型数据库都使用了悲观机制来实现写入优化。悲观机制采用了锁,通过使用锁它可以阻塞一些操作或者进行某些方式的冲突检测。当一个表格,页面或是行被修正后,悲观机制中的锁可以用来阻塞其他潜在的访问修正资源的事务。然而,乐观机制并不采用任何锁,它仅仅依赖于冲突检测来维护事务隔离。乐观机制采用的冲突检测可以允许所有的读操作,并在事务结束时检验其分歧性。如果检测到冲突,那么事务会进行回滚或重做。大部分web服务器都是读入优化,因此使用了乐观机制。通过允许所有的读入操作,乐观机制既可以保证很高的读写吞吐量,也可以在资源不是不断改变的情况下保证数据的分歧性。
下面列出的隔离级别是用来协助Web开发人员更好的理解他们编程模型中放置的约束,协助系统架构师和开发人员共同讨论如何在保持必要的数据完整性的同时选择最无效的隔离级别。它们按照最少隔离(未提交读)到最多隔离(串行化)的顺序列出。
未提交读隔离级别需求事务间很少的隔离。每一个读操作都能看到事务中等待的写操作(脏读)。然而曾经提交的写操作必需要有一个串行顺序来防止脏写。悲观机制会阻塞有冲突的写操作直到其他写操作曾经被提交或曾经回滚。乐观机制不会锁住这些操作,它会允许所有的操作都通过。如果一个连接进行了回滚,那么接下来修正同一块数据的其他操作也会被回滚。在这种级别中,共享缓冲可以不加验证的进行使用。这种隔离级别最好在不需求事务(比如只读的数据集),或者事务只在独占数据库时才修正的情况下使用。
例子:一个只在离线情况下更新的档案数据库,或者不在事务中使用的审核/登陆(audit/logging)表。
已提交读可以读取系统中任何曾经提交的形状,并且可以不加验证(混合形状)的进行缓冲,只需当前连接中发生的改变能够反映到结果中即可。悲观机制将其实现为单调视图。乐观事务则隔离存储所有的改动,使得它们直到提交后才可用。读已提交使用一个非常乐观的机制,它推迟写入所有的变化直到事务被提交为止。这种方式的乐观隔离可以在不阻塞读操作的情况下实现复杂的写入操作,并且它没有验证模式。共享缓冲只能在已提交的形状中使用。这种隔离级别最好在结果可以使用旧值,且事务只能用于写入操作的情况下使用。
例子:一个不必显示当前最新帖子的在线论坛,且它的帖子间数据不相冲突。
单调视图是对读已提交的一个扩展,它其中的事务在执行时会观察数据库中一个单调上升的形状。在这种级别中,如果有明显的写入事务,那么悲观事务会在读入操作中被阻塞。乐观事务会像在读已提交中一样操作,隔离保存所有的改动,并且会验证它们的缓冲以确保其仍然合法。这种级别可以定期地同步数据库副本,且最好在不需求事务或者仅存在写操作事务的情况下使用。
例子:一个仅能由一团体来修正的用户偏好表。
快照读取扩展了单调视图,它可以保证查询结果都能反映到数据库分歧的快照中。悲观机制会在读操作时妨碍其他影响结果的写入操作。乐观机制则允许其他的写入操作,并通知读取事务某部分曾经发生改变并进行回滚。想要实现一个乐观机制,必须在读操作结束之前验证能否有什么并行的写入操作修正了结果,如果有的话,那么结果可能会重做或回滚。这个检验过程可能只是简单的检查同一张表中能否出现了写入操作,或者只是检查改动的查询结果。乐观隔离级别可以很轻松地检测出冲突,并且在允许并发读入操作的过程中,支持写入操作。这种级别只需能够读取到快照,便可以定期地同步数据库副本。最好在写入操作很少,不想与读入操作冲突,且查询结果需求分歧性的时候使用这种隔离级别。
例子::一个查询比修正频繁,且只保留最新值的货币换位表或者查询表。
游标稳定性隔离扩展了读已提交,并且是许多关系型数据默认的隔离级别。在这种隔离级别中,悲观事务如果在一个单独的语句中执行的话,必须得指定它将修正的记录。这通常可以在"SELECT"查询后附加“FOR UPDATE”关键字来实现。在这种情况下,其他冲突的读写悲观事务都将被阻塞直到该事务结束为止。乐观事务会跟踪提交时被验证的所有修正记录/实体的版本号。这是一种很流行的乐观隔离级别,因此被所有的主流对象关系映射库支持。在Java持久性API中,可以使用FLUSH_ON_COMMIT(虽然查询可能不影响本地改动)来接近达到这种级别,且如果检测到冲突的话,可以抛出OptimisticLockException 异常。这种隔离也同样可以用在HTTP头域的If-Match或者 If-Unmodified-Since中,它可以用来在更新前对比上一个资源的版本或者时间戳。这种级别最好在实体由外部信息(不从数据库中读取)更改,或者改动不会彼此覆盖的情况下使用。
例子:一个共享的公司目录或者一个wiki。
可反复读取级别扩展了游标稳定性,它保证事务内的任何数据在事务过程中都不会被修正或者移除。悲观事务需求读取所有记录上的锁,并阻塞其他服务来修正这些记录。乐观事务则会跟踪所有的记录或者实体,并检查它们能否在提交时被修正过。这种级别最好在实体形状能够影响其他实体,或者事务由读写操作构成的情况下使用。
例子:一个订单跟踪数据库,它从一个实体中读取值并用它来计算其他的实体值。
快照隔离扩展了快照读取和可反复读取,它保证事务中所有进行的读操作都能看到数据库中分歧的快照。事务执行的的任何读操作都会有相反的结果,而不管它们在事务中执行的早晚。这和可反复读取不同,由于快照隔离能够防止幻读(查询结果不断变化)。许多关系型数据库采用多版本并发控制(也可以叫做 SERIALIZABLE)来支持这种级别,实现方法是通过锁和冲突检测的组合。在这种级别中,考虑到它可能与悲观机制或者乐观机制相冲突,因此事务一定要做好回滚的预备。悲观机制会通过锁住资源来尝试减少冲突的机会,但是必须在事务提交后将这些改动合并。乐观机制也会使用多版本并发控制,但是它不会阻塞其他可能产生潜在冲突操作的事务,反而是将冲突的事务进行回滚。这种级别的隔离最好在事务可以读取和修正多个记录的情况下使用。
例子:一个基于系统形状规则的任务流系统。
串行性是快照隔离的扩展,它要求所有的事务都必须一个接着一个的出现,就好比它们被串行化过一样。悲观机制需求锁住所有评估过的查询,以防止写入操作影响这些结果。而乐观机制则跟踪所有评估过的查询,并在事务结束时使用一个后向验证或前向验证的模式来检查能否有