首页 > 代码库 > 数据库的快照隔离级别(Snapshot Isolation)
数据库的快照隔离级别(Snapshot Isolation)
隔离级别定义事务操作资源和更新数据的隔离程度,在SQL Server中,隔离级别只会影响读操作申请的共享锁,而不会影响写操作申请的互斥锁。隔离级别控制事务在执行读操作时:
- 在读数据时是否使用共享锁,申请何种类型的隔离级别;
- 事务持有读锁的时间
- 读操作引用其他事务更新的数据行时,控制读操作的行为:
- 被阻塞,等待其他事务释放互斥锁;
- 读取事务提交后的版本,该数据行在事务开始时存在;Retrieves the committed version of the row that existed at the time the statement or transaction started.
- 读没有提交的数据;
在执行写操作时,事务持有互斥锁,直到事务结束才释放,互斥锁不受事务隔离级别的影响。隔离性和并发性是此消彼长的关系。在SQL Server中,互斥锁和任意锁都不兼容,在同一时间,同一个数据行上,只能有一个事务持有互斥锁,就是说,写操作是顺序进行的,不能并发。
Choosing a transaction isolation level does not affect the locks acquired to protect data modifications. A transaction always gets an exclusive lock on any data it modifies, and holds that lock until the transaction completes, regardless of the isolation level set for that transaction.
事务的隔离级别共有5个,使用SET命令修改Session-Level的隔离级别,使用DBCC UserOptions 查看当前Session的隔离级别:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SNAPSHOT | SERIALIZABLEDBCC UserOptions
一,事务的隔离级别
SQL Server 默认的隔离级别是Read Committed,用户不能修改Database-Level默认的隔离级别,但能够修改Session-Level默认的隔离级别。Session-Level默认的隔离级别是Read Committed,该隔离级别受到数据库选项 READ_COMMITTED_SNAPSHOT 的影响,决定Read Committed隔离级别是否使用行版本控制事务的读操作,在Read Committed隔离级别下:
- 如果设置选项READ_COMMITTED_SNAPSHOT为OFF,那么事务在执行读操作时申请共享锁,阻塞其他事务的写操作;
- 如果设置选项READ_COMMITTED_SNAPSHOT为ON,那么事务在执行读操作时使用Row Versioning,不会申请共享锁,不会阻塞其他事务的写操作;
在任何隔离级别下,事务在执行写操作时都申请互斥锁(exclusive lock),持有互斥锁直到事务结束,互斥锁不受隔离级别的控制;而共享锁(Shared Lock)受到隔离级别的控制,隔离级别影响Shared Lock的申请和释放:
- 在 Read Uncommitted隔离级别下,读操作不会申请Shared Lock;
- 在 Read Committed(不使用row-versioning),Repeatable Read 和 Serializable隔离级别下,都会申请Shared Lock;
- 在 Read Committed(不使用row-versioning) 隔离级别下,在读操作执行时,申请和持有Share Lock;一旦读操作完成,释放Shared Lock;
- 在 Repeatable Read 和 Serializable隔离级别下,事务会持有Shared Lock,直到事务结束(提交或回滚);
SQL Server支持使用Row Versioning的隔离级别,事务的读操作只申请SCH-S 表级锁,不会申请Page 锁和Row 锁:
- 当数据库选项 READ_COMMITTED_SNAPSHOT设置为ON,Read Committed隔离级别使用Row Versioning提供语句级别(Statement-Level)的读一致性;
- When a transaction runs at the read committed isolation level, all statements see a snapshot of data as it exists at the start of the statement.
- Snapshot隔离级别使用Row Versioning 提供事务级别(Transaction-Level)的读一致性。当读取被其他事务修改的数据行时,获取在当前事务开始时的行版本数据,使用Snapshot隔离级别时,必须设置数据库选项ALLOW_SNAPSHOT_ISOLATION为ON;
- When reading rows modified by another transaction, they retrieve the version of the row that existed when the transaction started.
- 注意语句级别的读一致性和事务级别的读一致性是snapshot 和 read committed snpshot 最大的区别:
- 事务级别的读一致性是指:在事务开始,到事务提交期间,该事务持有表数据的一个快照。如果在该事务活动期间,其他事务更新表数据,该事务只会读取快照数据,不会读取到被其他事务更新的数据值;
- 语句级别的读一致性是指:事务持有的数据快照,在语句结束时,立即释放;在事务活动期间,能够读取到其他事务提交更新的数据值;
二,使用Row Versioning的隔离级别
在默认的隔离级别Read Commited下,在执行读操作时,事务申请shared lock,读写操作相互阻塞。在隔离级别Read Uncommitted下,事务不会申请shared lock,因此读操作不会阻塞写操作,但是读操作可能会读到脏数据。脏数据是指被其它尚未提交的事务修改之后的数据值,不是指更新之前的数据值。
SQL Server 提供Snapshot隔离级别,用于读取修改之前的数据值。在Snapshot隔离级别下,事务在修改任何数据之前,先将修改前的数据复制到tempdb,写操作创建数据行的一个原始版本(Row Version),注意,SQL Server只会复制被修改的数据行,对于未修改的数据行,不会保存行版本数据。后续其他事务的一切读操作都去读这个复制的行版本。在Snapshot隔离级别下,读写操作不会互相阻塞。使用行版本控制提高事务的并发性,但是有一个明显的缺点,虽然用户读到的不是脏数据,但是数据可能正在被修改,很快就要过期。如果根据这个过期的数据做数据修改,可能会产生逻辑错误。
1,启用Snapshot隔离级别
设置数据库选项 ALLOW_SNAPSHOT_ISOLATION 为 ON,没有改变Session-Level的事务隔离级别,需要修改Session-Level的事务隔离级别为SNAPSHOT,才能使用行版本数据
alter database currentset allow_snapshot_isolation on;
要想使用snapshot隔离级别,必须将当前Session的隔离级别修改Snapshot,这样当前的事务才能访问Row Versioning数据:
SET TRANSACTION ISOLATION LEVEL SNAPSHOT
2,数据库选项READ_COMMITTED_SNAPSHOT(简称RCS)
使用Row-Versioning数据时,数据库选项ALLOW_SNAPSHOT_ISOLATION必须设置为ON,将数据库选项READ_COMMITTED_SNAPSHOT设置为ON,在默认的隔离级别Read Committed下,事务能够访问Row Versioning数据:
alter database currentset allow_snapshot_isolation on;alter database current set read_committed_snapshot on;
前提是必须设置数据库选项ALLOW_SNAPSHOT_ISOLATION为ON,一旦启用RCS选项,在默认的Read Committed 隔离级别中,事务使用行版本(row versioning)代替加锁,事务能够访问版本化的数据行。Read Committed Snapshot隔离级别保证语句级别的事务一致性,TSQL语句只能读取在该语句开始时已经提交的数据。当前事务的读操作不会阻塞其他事务的写操作,当前事务读取的是其他事务已提交修改的数据值。
When a transaction runs at the read committed isolation level, all statements see a snapshot of data as it exists at the start of the statement. Setting the READ_COMMITTED_SNAPSHOT ON option allows access to versioned rows under the default READ COMMITTED isolation level.
3,READ COMMITTED Snapshot隔离级别
在Read Committed 隔离级别下,事务不能读取被其他事务修改,但尚未提交的数据,即只能读取已提交更新的数据,READ COMMITTED隔离级别的行为受到数据库选项:READ_COMMITTED_SNAPSHOT的影响:
- 如果设置RCS选项为OFF(默认设置),数据库引擎使用Shared Lock阻止其他事务修改当前事务正在读取的数据;当读取被其他事务修改,但尚未提交更新的数据行时,该读操作将被阻塞;
- If READ_COMMITTED_SNAPSHOT is set to OFF (the default), the Database Engine uses shared locks to prevent other transactions from modifying rows while the current transaction is running a read operation. The shared locks also block the statement from reading rows modified by other transactions until the other transaction is completed.
- 如果设置RCS选项为ON,数据库引擎使用行版本化(Row Versioning)的数据实现语句级别的一致性,不会阻塞其他事务的写操作,但只能读取已提交更新的数据
- If READ_COMMITTED_SNAPSHOT is set to ON, the Database Engine uses row versioning to present each statement with a transactionally consistent snapshot of the data as it existed at the start of the statement. Locks are not used to protect the data from updates by other transactions.
4,快照隔离级别
SNAPSHOT 隔离级别指定在一个事务中读取的数据是一致性的数据版本。在事务开始时,在表级别创建数据快照,只能识别其他事务已提交的数据更新。在事务开始之后,当前事务不会识别其他事务执行的数据更新。Sanpshot隔离级别实现事务级别的数据一致性。SQL Server 使用tempdb来存储行版本化(row versioning)的数据,如果数据更新较多,存储的行版本太多,会导致tempdb成为系统瓶颈。
Specifies that data read by any statement in a transaction will be the transactionally consistent version of the data that existed at the start of the transaction. The transaction can only recognize data modifications that were committed before the start of the transaction. Data modifications made by other transactions after the start of the current transaction are not visible to statements executing in the current transaction. The effect is as if the statements in a transaction get a snapshot of the committed data as it existed at the start of the transaction.
三,启用快照隔离级别
1,使用snapshot 隔离级别
step1,设置数据库选项
ALTER DATABASE CURRENT SET SINGLE_USER
WITH ROLLBACK IMMEDIATE; ALTER DATABASE CURRENT SET ALLOW_SNAPSHOT_ISOLATION ON; --ALTER DATABASE CURRENT SET READ_COMMITTED_SNAPSHOT OFF; ALTER DATABASE CURRENT SET MULTI_USER;
step2,修改Session-Level的隔离级别为snapshot
set transaction isolation level snapshot
2,使用Read_Committed_Snapshot隔离级别
ALTER DATABASE CURRENT SET SINGLE_USER WITH ROLLBACK IMMEDIATE; ALTER DATABASE CURRENT SET ALLOW_SNAPSHOT_ISOLATION ON; ALTER DATABASE CURRENT SET READ_COMMITTED_SNAPSHOT ON; ALTER DATABASE CURRENT SET MULTI_USER;
四,引用徐海蔚老师的例子,测试隔离级别的行为
snapshot隔离级别不会阻塞其他事务的写操作,该隔离级别忽略数据的修改操作,只读取row versioning的数据,就是说,读取到的是数据修改之前的版本,当snapshot事务尝试修改由其他事务修改的数据时,产生更新冲突,写操作异常终止。
read committed snapshot隔离级别,读取行版本化的已提交数据:
- 当其他事务未提交更新时,读取行版本化的数据,即读取修改之前的数据值;
- 当其他事务提交数据更新后,读取修改后数据值;
- 由于该隔离级别不会申请共享锁,因此不会阻塞其他事务的更新操作;
- 能够更新由其他事务修改的数据;
参考文档:
Isolation Levels in the Database Engine
SQL SERVER – Difference Between Read Committed Snapshot and Snapshot Isolation Level
数据库的快照隔离级别(Snapshot Isolation)