Summary:
·
Read
Committed uses locks. The concurrency is low. Transaction 1 (T1) has to wait
for T2 completes the modification to read the updates, but you get the newest
consistent data.
·
Read_Committed_Snapshot uses row-version snapshot of the data just before
modification statement in T2. T1 will read the snapshot when T2 is doing the
update. If T2 commits the modification, T1 will be able to read the updated
value. T1 also can modify the same row by T2 after T2 commits the transaction.
The drawbacks are: (1) inconsistent data comparing with read committed, (2)
maybe much more snapshots needed in temp table depending on how many
modification statements in other transactions, comparing to SNAPSHOT.
·
SNAPSHOT
transactions isolation level also does not use locks and uses snapshot as Read_Committed_Snapshot. SQL Server creates a snapshot of
the data just before the T2 wants to modify the data T1 is reading. Thus, the
number of snapshots is generally less than that for Read_Committed_Snapshot.
T1 only reads data from the SNAPSHOT while its transaction is open, even after
T2 commits the changes. T1 does not see it. Also T1 cannot modify the same rows
committed by T2. Thus, the SNAPSHOT transactions isolation level has the
largest lag on data consistency. But it keep the data consistent at the
transaction level.
·
So
the call is primarily upon the business requirement. Then, test and compare the
options in your technology environment.
1. Read_Committed_Snapshot and SNAPSHOT are turned on in different ways:
-- For READ_COMMITTED_SNAPSHOT, default is
OFF
ALTER DATABASE AdventureWorks2008R2
SET READ_COMMITTED_SNAPSHOT ON;
--for SNAPSHOT isolation
SET TRANSACTION ISOLATION LEVEL
SNAPSHOT
--But before that, you may need to make the
DB allow SNAPSHOT isolation
ALTER DATABASE AdventureWorks2012
SET ALLOW_SNAPSHOT_ISOLATION ON;
--------------
Background Information--------------------------------
· SQL Server has another option to allow/disallow to set the transaction isolation level to SNAPSHOT.
· The default for a user database is OFF. If it is OFF, you cannot set SNAPSHOIOT as the T. I. L for a transaction.
· You have to turn it ON for a database if you want to use SNAPSHOIOT as the T. I. L for transactions.
/*When ON is
specified, transactions can specify the SNAPSHOT transaction isolation level.
When a
transaction runs at the SNAPSHOT isolation level, all statements see a snapshot
of
data as it exists at the start of the transaction.
When OFF is
specified, transactions cannot specify the SNAPSHOT transaction isolation
level.
*/
ALTER DATABASE AdventureWorks2012
SET ALLOW_SNAPSHOT_ISOLATION ON;
--Let's test it.
--First, find the current value
of snapshot_isolation_state. It is supposed to be OFF
as the default.
SELECT name, snapshot_isolation_state,
snapshot_isolation_state_desc AS description
FROM sys.databases
WHERE name = N'AdventureWorks2012';
GO
/*** It is OFF initially. More
information on the value of snapshot_isolation_state
name
snapshot_isolation_state description
AdventureWorks2012
0
OFF
snapshot_isolation_state = State of snapshot-isolation
transactions being allowed, as set by the ALLOW_SNAPSHOT_ISOLATION option:
0 = Snapshot
isolation state is OFF (default). Snapshot isolation is disallowed.
1 = Snapshot
isolation state ON. Snapshot isolation is allowed.
2 = Snapshot
isolation state is in transition to OFF state. All transactions have their
modifications versioned. Cannot start new transactions
using snapshot isolation. The database remains in the transition to OFF state
until all transactions that were active when ALTER DATABASE was run can be completed.
3 = Snapshot
isolation state is in transition to ON state. New transactions have their
modifications versioned.
Transactions cannot use snapshot isolation until the snapshot isolation state
becomes 1 (ON).
The database remains in the transition to ON state until all update
transactions that were active when
ALTER DATABASE was run can be completed.
*/
--Let’s first
make the T.I.L to the default
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
--Let's check the
current transaction isolation level
--Option 1
SELECT CASE
WHEN transaction_isolation_level = 1
THEN 'READ
UNCOMMITTED'
WHEN transaction_isolation_level = 2
AND is_read_committed_snapshot_on = 1
THEN 'READ
COMMITTED SNAPSHOT'
WHEN transaction_isolation_level = 2
AND is_read_committed_snapshot_on = 0 THEN 'READ
COMMITTED'
WHEN transaction_isolation_level = 3
THEN 'REPEATABLE
READ'
WHEN transaction_isolation_level = 4
THEN 'SERIALIZABLE'
WHEN transaction_isolation_level = 5
THEN 'SNAPSHOT'
ELSE NULL
END AS TRANSACTION_ISOLATION_LEVEL
FROM sys.dm_exec_sessions AS s
CROSS JOIN sys.databases AS d
WHERE session_id = @@SPID
AND d.database_id = DB_ID();
--Option 2: use DBCC USEROPTIONS - Returns the
SET options active (set) for the current connection.
DBCC USEROPTIONS
--isolation level: read committed
--Option 3
SELECT transaction_isolation_level FROM sys.dm_exec_sessions
WHERE session_id=58 --the current session ID
--make sure the snapshot_isolation_state
is still OFF
SELECT name, snapshot_isolation_state,
snapshot_isolation_state_desc AS description
FROM sys.databases
WHERE name = N'AdventureWorks2012';
GO
/**** It is still
OFF
name
snapshot_isolation_state description
AdventureWorks2012
0
OFF
*/
--Can we set the
transaction isolation level to SNAPSHOT? Yes, you can!!!???.
--No error or
warning message!!!
SET TRANSACTION ISOLATION LEVEL SNAPSHOT
--Moreover, it
seems you have changed it to SNAPSHOT.
DBCC USEROPTIONS
--isolation level:
snapshot!!!
--Is the snapshot_isolation_state still OFF? Yes, it is OFF.
SELECT name, snapshot_isolation_state,
snapshot_isolation_state_desc AS description
FROM sys.databases
WHERE name = N'AdventureWorks2012';
GO
/******* It is
still OFF
name
snapshot_isolation_state description
AdventureWorks2012
0
OFF
*/
--What happened?
Well, you have to test it with a transaction! Run code below
BEGIN TRAN
SELECT [BusinessEntityID],[VacationHours] FROM [HumanResources].[Employee] WHERE [BusinessEntityID]=4
/*
Msg
3952, Level 16, State 1, Line 4
Snapshot isolation
transaction failed accessing database 'AdventureWorks2012' because snapshot
isolation is not allowed in this database. Use ALTER DATABASE to allow snapshot
isolation.
*/
2. Control
the data consistency at different level
1) READ_COMMITTED_SNAPSHOT – at the statement level.
2) SNAPSHOT – at the transaction level
3. Demonstrate
the different between READ COMMITTED, SNAPSHOT, and READ_COMMITTED_SNAPSHOT
1) Read Committed
A read committed
transaction read the data, and another transaction modifies the same data.
The update must
wait. After the read is finished, the update can start. While the update is on,
other reads will be blocked until the update is committed or rolled back.
--A 普通已提交事务
--在此示例中,一个普通read committed事务将读取数据,然后由另一事务修改此数据。执行
--完的读操作不阻塞由其他事务执行的更新操作。但是,在其他事务已经做了更新操作后,读
--操作会被阻塞住,直到更新操作事务提交为止
*******************************************************************************************/
--==========================In
Window 1 ===================================--
USE [AdventureWorks2012]
GO
--Make sure it is
Read Committed. If not, set it to be. You may need to close all of the
--other
connections, and use ALTER DATABASE AdventureWorks2012 SET READ_COMMITTED_SNAPSHOT
OFF
SELECT CASE WHEN transaction_isolation_level = 1 THEN 'READ
UNCOMMITTED'
WHEN transaction_isolation_level = 2
AND is_read_committed_snapshot_on = 1
THEN 'READ
COMMITTED SNAPSHOT'
WHEN transaction_isolation_level = 2
AND is_read_committed_snapshot_on = 0 THEN 'READ
COMMITTED'
WHEN transaction_isolation_level = 3 THEN 'REPEATABLE
READ'
WHEN transaction_isolation_level = 4 THEN 'SERIALIZABLE'
WHEN transaction_isolation_level = 5 THEN 'SNAPSHOT'
ELSE NULL
END AS TRANSACTION_ISOLATION_LEVEL
FROM sys.dm_exec_sessions AS s
CROSS JOIN sys.databases AS d
WHERE session_id = @@SPID
AND d.database_id = DB_ID();
BEGIN TRAN
--Query 1
--Return the initial 48 vacation hours for
this employee.
SELECT [BusinessEntityID] ,
[VacationHours]
FROM [HumanResources].[Employee]
WHERE [BusinessEntityID] = 4
--*********************In
Window 2 *****************************************--
USE [AdventureWorks2012]
GO
BEGIN TRAN
--Update 1
--Update will not be blocked as the S lock is
released after read is done
--Please note that the S lock in Read
Committed is at the statement level, not at the transaction level
--So although Transaction 1 is still open,
the S lock is released after the read.
UPDATE [HumanResources].[Employee]
SET [VacationHours] = [VacationHours] - 8
WHERE [BusinessEntityID] = 4
--make sure the update is effective in
transaction 2 = 40 hours
SELECT [BusinessEntityID] ,
[VacationHours]
FROM [HumanResources].[Employee]
WHERE [BusinessEntityID] = 4
--========================In
Window 1 ===================================--
--However, the
query in transaction 1 will be blocked as the update in T2 is not committed or rolledback.
SELECT [BusinessEntityID] ,
[VacationHours]
FROM [HumanResources].[Employee]
WHERE [BusinessEntityID] = 4
--*************************In
Window 2 *****************************************--
COMMIT
GO
--============================In
Window 1 ===================================--
--Now the query
returns 40 hours after the change in Window 2 is committed
SELECT [BusinessEntityID] ,
[VacationHours]
FROM [HumanResources].[Employee]
WHERE [BusinessEntityID] = 4
--If you update
the same table on another column, it will be OK as the update in T2 does not
affect it.
--But it will not be OK in the SNAPSHOT
transaction isolation level as demonstrated below.
BEGIN TRAN
UPDATE [HumanResources].[Employee]
SET [SickLeaveHours] = [SickLeaveHours] - 8
WHERE [BusinessEntityID] = 4
--It should RETURN 80-8=72
SELECT [SickLeaveHours]
FROM [HumanResources].[Employee]
WHERE [BusinessEntityID] = 4
--Rollback the update in T1. The update
in T2 has been committed, and not affected.
ROLLBACK TRAN
2) SNAPSHOT ISOLATION
In Snapshot, one
transaction read the data, another transaction tries to modify the data.
SQL Server will
take a snapshot of the data in the beginning of T2, T1 will read the data
from the row-version snapshot. T2 can update the real table.
So the data being acquired is
that just before the modification. Moreover, when T1 tries
to modify the same row being updated by T2, conflicts occur, and the
modifications in T1 are rolled back
*******************************************************************************************/
--B 使用快照隔离
--此示例中,在快照隔离下运行的事务将读取数据,然后由另一个事务修改此数据。快照事务
--不阻塞由其他事务执行的更新操作,他忽略数据的修改继续从版本化的行读取数据。也就是
--说,读取到的是数据修改前的版本。但是,当快照事务尝试修改已由其他事务修改的数据时,
--他将生成错误并终止
--============================In
Window 1 ===================================--
USE [AdventureWorks2012]
GO
--First, let's put the data back to the original
value
UPDATE [HumanResources].[Employee]
SET [VacationHours] = 48
WHERE [BusinessEntityID] = 4
--Allows the SNAPSHOT isolation for the
database
ALTER DATABASE [AdventureWorks2012] SET ALLOW_SNAPSHOT_ISOLATION ON
GO
--Now set
the TRANSACTION ISOLATION LEVEL to SNAPSHOT
SET TRANSACTION ISOLATION LEVEL SNAPSHOT
GO
BEGIN TRAN
--make sure the initial value is 48 hours
SELECT [BusinessEntityID] ,
[VacationHours]
FROM [HumanResources].[Employee]
WHERE [BusinessEntityID] = 4
--*************************In
Window 2 *****************************************--
USE [AdventureWorks2012]
GO
BEGIN TRAN
--Update 1
--Update will not be blocked by T1
UPDATE [HumanResources].[Employee]
SET [VacationHours] = [VacationHours] - 8
WHERE [BusinessEntityID] = 4
--Make sure the data in the table has
decreased to 40 hours
SELECT [VacationHours]
FROM [HumanResources].[Employee]
WHERE [BusinessEntityID] = 4
--============================In
Window 1 ===================================--
--Run the query again in T1
--It should return the original 48 hours
before the update as T1 read data from the snapshot before T2
SELECT [BusinessEntityID] ,
[VacationHours]
FROM [HumanResources].[Employee]
WHERE [BusinessEntityID] = 4
--******************************In
Window 2 *****************************************--
--提交事务
COMMIT TRAN
GO
--==============================In
Window 1 ===================================--
--Query it again
after T2 is committed.
--It should be still
48 as it read the data from the snapshot before transaction 2.
SELECT [BusinessEntityID] ,
[VacationHours]
FROM [HumanResources].[Employee]
WHERE [BusinessEntityID] = 4
--Update 2. As this row has been modified T2, any
updates on the row by T1 will be aborted and rolled back.
--Error
3960: Msg 0, Level 16, State 2, Line 1
/*
Snapshot isolation transaction aborted due to update conflict. You cannot use
snapshot isolation to access table 'HumanResources.Employee'
directly or indirectly in database 'AdventureWorks2012' to update, delete, or
insert the row that has been modified or deleted by another transaction.
Retry the transaction or change the isolation level for the update/delete
statement.
*/
UPDATE [HumanResources].[Employee]
SET [SickLeaveHours] = [SickLeaveHours] - 8
WHERE [BusinessEntityID] = 4
--The update is automatically rolledback, still 80 hours
SELECT [SickLeaveHours]
FROM [HumanResources].[Employee]
WHERE [BusinessEntityID] = 4
--But
modification in T2 will not be rolled back. The data value in the table should
still be 40 hours.
--Roll back the tran in T1 if it is still open
ROLLBACK TRAN
GO
3) READ_COMMITTED_SNAPSHOT ON
When
READ_COMMITTED_SNAPSHOT is ON, T1 read the data. If T2 tries to modify the
data, T1 will read the data from the row-version snapshot just before the
modification statement of T2 (Not the transaction!!!), so T2
can modify the data. Different from the SNAPSHOT isolation, after T2 finishes
the update, T1 can get the updated value of the table. Additionally, T1 can
modify the same row after T2 has committed.
--C 使用行版本控制的已提交读
--在此示例中,使用行版本控制的已提交读事务与其他事务并发运行。已提交读事务的行为与快照事务的行为
--有所不同。与快照事务相同的是,即使其他事务修改了数据,已提交读事务也将读取版本化的行。
--与快照事务不同的是,已提交读将执行下列操作:
--(1)在其他事务提交数据更改之后,读取修改的数据
--(2)能够更新由其他事务修改的数据,而快照事务不能
***************************************************************************************/
--==============================In
Window 1 ===================================--
USE [AdventureWorks2012]
GO
--First, let's put the data back to the
original value
UPDATE [HumanResources].[Employee]
SET [VacationHours] = 48
WHERE [BusinessEntityID] = 4
--Set TRANSACTION ISOLATION LEVEL to READ
COMMITTED
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
GO
--SET READ_COMMITTED_SNAPSHOT ON
--You have to close all of the other
connections while executing it. If necessary, use sp_who
and kill
ALTER DATABASE [AdventureWorks2012] SET READ_COMMITTED_SNAPSHOT ON
GO
--Let’s double check it is
READ_COMMITTED_SNAPSHOT ON
SELECT CASE WHEN transaction_isolation_level = 1 THEN 'READ
UNCOMMITTED'
WHEN transaction_isolation_level = 2
AND is_read_committed_snapshot_on = 1
THEN 'READ
COMMITTED SNAPSHOT'
WHEN transaction_isolation_level = 2
AND is_read_committed_snapshot_on = 0 THEN 'READ
COMMITTED'
WHEN transaction_isolation_level = 3 THEN 'REPEATABLE
READ'
WHEN transaction_isolation_level = 4 THEN 'SERIALIZABLE'
WHEN transaction_isolation_level = 5 THEN 'SNAPSHOT'
ELSE NULL
END AS TRANSACTION_ISOLATION_LEVEL
FROM sys.dm_exec_sessions AS s
CROSS JOIN sys.databases AS d
WHERE session_id = @@SPID
AND d.database_id = DB_ID();
--Make sure the initial value is 48 hours
BEGIN TRAN
SELECT BusinessEntityID ,
[VacationHours]
FROM [HumanResources].[Employee]
WHERE BusinessEntityID = 4
--*************************************In
Window 2 *****************************--
USE [AdventureWorks2012]
GO
BEGIN TRAN
--Update 1
--The update should not be locked by the open
tran in T1
UPDATE [HumanResources].[Employee]
SET [VacationHours] = [VacationHours] - 8
WHERE BusinessEntityID = 4
--It should be 40
SELECT [VacationHours]
FROM [HumanResources].[Employee]
WHERE [BusinessEntityID] = 4
--============================In
Window 1 ===================================--
--Run it again in T1
--It should still be 48 as T2 has not
committed the transaction yet. T1 still get the data
--from the snapshot before T2 takes place.
SELECT [BusinessEntityID] ,
[VacationHours]
FROM [HumanResources].[Employee]
WHERE [BusinessEntityID] = 4
--****************************In
Window 2 *****************************************--
--提交事务
COMMIT TRAN
GO
--================================In
Window 1 ===================================--
--Now T1 should get the updated value 40 after
T2 has committed the transaction.
SELECT [BusinessEntityID] ,
[VacationHours]
FROM [HumanResources].[Employee]
WHERE [BusinessEntityID] = 4
--Also the update on the same row is allowed for
T1 after T2 has committed the change. No 3960 error.
BEGIN TRAN
UPDATE [HumanResources].[Employee]
SET [SickLeaveHours] = [SickLeaveHours] - 8
WHERE [BusinessEntityID] = 4
--We can rollback T1 for the update.
--The modification by T2 has committed and
become permanent.
ROLLBACK TRAN
GO
4. Summary
of Differences in Read Committed, SNAPSHOT, and Read_Committed_Snapshot(使用行版本控制隔离级别后的不同)
Transaction (T1) |
T2 |
Result |
||
A. Read
Committed |
B. Snapshot |
C. Read_Committed_Snapshot |
||
BEGIN TRAN Query1 |
48 |
|||
BEGIN TRAN Update1 |
OK |
|||
Query1 |
40 |
|||
Query2 |
Blocked |
48 |
||
COMMIT TRAN |
Q2 in
T1returns 40 |
|||
Query3 |
40 |
48 |
40 |
|
Update2 ROLLBACK TRAN |
OK |
Failed |
OK |