众所周知,SQL Server事务隔离级别是为了保证在并发事务处理环境下的数据完整性,准确性,一致性的一种机制。在SQL Server 2005 中一共有五种事务隔离级别,分别为:READ UNCOMMITTED,READ COMMITTED,REPEATABLE READ,SNAPSHOT,SERIALIZABLE.正确的在事务中应用隔离界别可以保证数据的准确,本文的意图不在于如何正确的将事务隔离界别应用到不同的需求逻辑实现中,而是想说明一些有关使用事务隔离级别时应该考虑的性能因素从而构建具有良好性能的事务体系。
一 事务隔离级别介绍
SQL Server 2005通过锁机制和行版本控制来实现事务隔离级别机制。下面简单介绍一下各种不同的事务隔离级别以及将他们应用到事务中时SQL Server的内部实现。
READ UNCOMMITED (未提交读)
READ UNCOMMITED级别是限制最低的隔离级别,当READ UNCOMMITTED被应用到事务时,事务中的查询将不会在数据库对象上应用任何锁(包括共享锁)相当于使用了No Lock提示的Select语句。所以当使用这个隔离级别的时候,将不会阻止其他的事务读或者写当前事务请求的数据。由于这种特性,导致应用READ COMMITTED的事务存在读取其他事务修改但并未提交的数据(脏读)。
READ COMMITTED (已提交读)
READ CMMITTED级别是SQL Server的默认事务隔离级别。它可以避免事务中的查询读取被修改但并未提交的数据(避免脏读)。READ COMMITTED有两种实现方式,这取决于READ_COMMITTED_SNAPSHOT数据库选项的设置情况。当READ_COMMITTED_SNAPSHOT为ON时,数据库引擎将会通过行版本控制为事务中的每一个查询建立一个事务级的数据快照。数据快照包含在执行查询前的所有符合查询条件的数据。使用这种方法将不会对数据库对象应用任何数据库锁(当然,可以使用READCOMMITTEDLOCK提示强制查询语句使用锁)。如果READ_COMMITTED_SNAPSHOT为OFF时,数据库引擎将在执行查询时,对数据库对象应用共享锁来避免其他事务修改当前事务中正在执行的查询语句所访问的对象。
因为在事务中各查询的间隔期间,其他事务可以修改数据库对象,所以使用READ COMMITTED级别时有可能出现不可重复读或幻象读的
情况。
REPEATABLE READ (可重复读)
REPEATABLE READ在READ COMMITTED的基础上,延长了执行查询时对访问的数据应用的共享锁的周期。在每次执行查询时,都要对数据库对象应用共享锁并且在整个事务结束前不会释放共享锁,从而避免了在事务间隔期间其他事务修改当前事务访问的数据对象(避免不可重复读)。但是其他事务可以执行Insert语句对当前事务访问的对象执行插入操作,因此幻象读的情况仍然不可避免。
SERIALIZABLE (可序列化)
SERIALIZABLE是最严格的隔离级别,与REPEATABLE相比,应用这个隔离级别的事务将会在访问数据上加Range锁,其他事务将不能在数据库对象上修改或者插入任何数据。Range锁也会被一直占用直到整个事务结束(相当于Select语句的HOLDLOCK查询提示)。使用这个隔离级别可以避免幻象数据。
SNAPSHOT (隔离级别 快照)
SNAPSHOT隔离级别会为事务中的所有查询语句建立事务级的数据快照,数据快照包含开始事务前的所有已经提交的数据。这种模式同样利用了行版本控制机制。注意,使用SNAPSHOT隔离级别时虽然不会阻塞其他事务执行,但是有一个例外,当ROLLBack操作执行时,如果事务请求被其他事务正在ROLLBACK的数据时锁住的数据,当前事务会被阻塞知道其他事务释放他们所占用的锁。
二 事务隔离级别的系统开销
由于数据库锁机制和行版本控制都要占用系统资源,并且数据库锁的占用和释放将会影响到并发事务处理的响应速度和数据库死锁,所以在使用事务隔离级别时要考虑到不同的应用和各种不同隔离级别的系统开学情况。
READ UNCOMMITTED (未提交读)
可以提供最好的系统性能。原因在于使用READ COMMITTED隔离界别的事务将不对为查询请求任何的数据库锁,数据库锁管理占用的资源几乎为0,同时当前被执行的事务不会影响到其他事务的正确执行,可以确保并发事务的相应时间,从而大大提高系统的整体性能。提供最好的性能必然有最大的弊端,脏读,不可重复读,幻想数据等情况都会在这种情况下发生。
READ COMMITTED (已提交读)
如果使用所机制实现READ COMMITTED隔离,将会对事务中查询的目标数据库对象加共享锁。如果使用行版本控制模式,则对系统主要影响在于数据库引擎需要提供额外的资源来管理数据快照。所有的数据快照将被保存在数据库的TempDB中,所以当访问大量数据时,需要考虑IO子系统的吞吐量以及TempDB是否有充分的空间来维护数据库实例中所有数据库的行版本请求,已经其他涉及TempDB的操作。此外,数据库服务器的处理器系统和快速存储系统也将承受更大的负载。
REPEATABLE (可重复读)
在事务执行过程中一直占用数据库对象的锁资源将会导致其他事务在当前事务执行过程中被永久性阻塞。如果当前事务中存在长时间的操作且没有系统没有超时处理机制,将会严重影响事务处理的响应速度,甚至出现数据库死锁。由于在REPEATABLE事务隔离级别下,其他事务可以对当前事务访问的数据库对象执行插入操作,将不会影响并发的插入操作(但需要在幻象数据与性能之间进行权衡)
SERIALIZABLE (可序列化)
尽量避免使用的隔离级别,应用该隔离级别的事务将完全阻塞其他需要访问当前事务正在访问的数据库对象的事务。在并发操作数量庞大时,即使事务处理响应速度较快,也会大大降低系统整体响应速度。
三 应用
事务隔离级别的应用是在应用与性能之前的权衡,一下是我本人对于不同类型的应用与不同事务隔离级别结合的一些观点,仅供参考
READ UNCOMMITTED
适合在大型数据仓库系统OLTP与OLAP之间的数据转换(ETL)时使用,由于数据仓库中保存的数据是一段时间内的数据,并且大量的数据用来对趋势作分析,所以对于数据的精确性要求比较低。在ETL执行过程中,可能会遍历大量的OLTP系统数据,查询将会消耗很长的时间,如果长时间占用数据库对象并阻塞其他事务的更新或插入操作,将对OLTP系统造成巨大影响。如果ETL执行时间在系统离线状态下进行,则无须考虑。
READ COMMITTED/SNAPSHOT
可以满足大部分一般应用,但仍然需要尽量减小事务中的查询响应时间。同时还应考虑在使用行版本控制模式下,查询大量数据对系统资源的影响。
REPEATABLE
在一些修改操作比较频繁的系统中,当事务需要对相同的结果集进行多次不连续的处理时,需要应用REPEATABLE隔离级别。要尽量将事务中访问的数据库对象降低到最少,以缓解阻塞其他事务而造成的压力。
SERIALIZABLE
谨慎使用!特别是并发数量很大的系统,切忌使用该事务隔离级别。
四 结论
提高事务执行的性能可以有很多方法,比如优化查询,建立适当的索引等等。将性能作为一个因素加入合理使用事务隔离级别的范畴内并不是必要的,但是如果大家能在实践中尽量去考虑这些因素,那么将构建出更优质的系统。