事务

事务隔离级别

1.READ UNCOMMITTED(未提交读,脏读),相当于(NOLOCK)

2.READ COMMITTED(已提交读,默认级别 )

3.REPEATABLE READ(可以重复读)

4.SERIALIZABLE(可序列化 Hold Table Lock)相当于(HOLDLOCK)

5.SNAPSHOT(快照) (类似Serializable  读取时不加共享锁)

6.READ COMMITTED SNAPSHOT(已经提交读隔离) (类似Read Committed 读取不加共享锁)

说明:

SNAPSHOT 在SNAPSHOT隔离级别下,当读取数据时可以保证操作读取的行是事务开始时可用的最后提交版本
同时SNAPSHOT隔离级别也满足前面的已提交读,可重复读,不幻读;该隔离级别实用的不是共享锁,而是行版本控制
使用SNAPSHOT隔离级别首先需要在数据库级别上设置相关选项
READ COMMITTED SNAPSHOT(已经提交读隔离) (类似Read Committed 读取不加共享锁)

在打开的所有查询窗口中执行以下操作

ALTER DATABASE TEST SET ALLOW_SNAPSHOT_ISOLATION ON;
重置测试数据
IF OBJECT_ID('Orders','U') IS NOT NULL DROP TABLE Orders 
GO
CREATE TABLE Orders
(ID INT NOT NULL PRIMARY KEY,
Price FLOAT NOT NULL,
type INT NOT NULL
);
INSERT INTO Orders VALUES(10,10.00,1),(11,11.00,1),(12,12.00,1),(13,13.00,1),(14,14.00,1);
GO
在回话1中打开事务,将订单10的价格加1,并查询跟新后的价格
BEGIN TRANSACTION
UPDATE Orders 
SET Price=Price+1
WHERE ID=10

SELECT ID,Price,type FROM Orders
WHERE ID=10
---查询到更新后的价格为11

---在回话2中将隔离级别设置为SNAPSHOT,并打开事务(此时查询也不会因为回话1的排他锁而等待,依然可以查询到数据)
SET TRANSACTION ISOLATION LEVEL SNAPSHOT
BEGIN TRANSACTION
SELECT ID,Price,type FROM Orders
WHERE ID=10

---查询到的结果还是回话1修改前的价格,由于回话1在默认的READ COMMITTED隔离级别下运行,SQL SERVER必须在更新前把行的一个副本复制到TEMPDB数据库中
--在SNAPSHOT级别启动事务会请求行版本

---现在在回话1中执行提交事务,此时订单10的价格为11
COMMIT TRANSACTION

---再次在回话二中查询订单10的价格并提交事务,结果还是10,因为事务要保证两次查询的结果相同

SELECT ID,Price,type FROM Orders
WHERE ID=10

COMMIT TRANSACTION

---此时如果在回话2中重新打开一个事务,查询到的订单10的价格则是11
BEGIN TRANSACTION
SELECT ID,Price,type FROM Orders
WHERE ID=10

COMMIT TRANSACTION

/*SNAPSHOT隔离级别保证操作读取的行是事务开始时可用的最后已提交版本,由于回话1的事务未提交,所以订单10的最后提交版本还是修改前的价格10,所以回话2读取到的价格是回话2事务开始前的已提交版本价格10,当回话1提交事务后,回话2重新新建一个事务此时事务开启前的价格已经是11了,所以查询到的价格是11,同时SNAPSHOT隔离级别还能保证SERIALIZABLE的隔离级别*/
READ COMMITTED SNAPSHOT
READ COMMITTED SNAPSHOT也是基于行版本控制,但是READ COMMITTED SNAPSHOT的隔离级别是读操作之前的最后已提交版本,而不是事务前的已提交版本,有点类似前面的READ COMMITTED能保证已提交读,但是不能保证可重复读,不能避免幻读,但是又比 READ COMMITTED隔离级别多出了不需要获取共享锁就可以读取数据
要启用READ COMMITTED SNAPSHOT隔离级别同样需要修改数据库选项,在回话1,回话2中执行以下操作(执行下面的操作当前连接必须是数据库的唯一连接[不能打开多个SQL窗口],可以通过查询已连接当前数据库的进程,然后KILL掉那些进程,然后再执行该操作,否则可能无法执行成功)
ALTER DATABASE TEST SET READ_COMMITTED_SNAPSHOT ON

IF OBJECT_ID('Orders','U') IS NOT NULL DROP TABLE Orders 
GO
CREATE TABLE Orders
(ID INT NOT NULL PRIMARY KEY,
Price FLOAT NOT NULL,
type INT NOT NULL
);
INSERT INTO Orders VALUES(10,10.00,1),(11,11.00,1),(12,12.00,1),(13,13.00,1),(14,14.00,1);
GO

-----在回话1中打开事务,将订单10的价格加1,并查询跟新后的价格,并保持事务一直处于打开状态
BEGIN TRANSACTION
UPDATE Orders 
SET Price=Price+1
WHERE ID=10

--查询到的价格是11
SELECT ID,Price,type FROM Orders
WHERE ID=10

---在回话2中打开事务查询订单10并一直保持事务处于打开状态(此时由于回话1还未提交事务,所以回话2中查询到的还是回话1执行事务之前保存的行版本)
BEGIN TRANSACTION
SELECT ID,Price,type FROM Orders
WHERE ID=10
--查询到的价格还是10

---在回话1中提交事务
COMMIT TRANSACTION 

---在回话2中再次执行查询订单10的价格,并提交事务
SELECT ID,Price,type FROM Orders
WHERE ID=10
COMMIT TRANSACTION 
--此时的价格为回话1修改后的价格11,而不是事务之前已提交版本的价格,也就是READ COMMITTED SNAPSHOT隔离级别在同一事务中两次查询的结果不一致.

SET TRAN ISOLATION LEVEL READ COMMITTED;

ALTER DATABASE TEST SET ALLOW_SNAPSHOT_ISOLATION ON;
ALTER DATABASE TEST SET ALLOW_SNAPSHOT_ISOLATION OFF;

ALTER DATABASE TEST SET READ_COMMITTED_SNAPSHOT ON;
ALTER DATABASE TEST SET READ_COMMITTED_SNAPSHOT OFF;


TransactionScopeOption

Root事务提交才会提交

TransactionScope timeout

When a scope joins an ambient transaction but specifies a smaller timeout than the one the ambient transaction is set to, the new, shorter timeout is enforced on the TransactionScope object, and the scope must end within the nested time specified, or the transaction is automatically aborted. If the nested scope's timeout is more than that of the ambient transaction, it has no effect.

嵌套的内部事务比外部事务超时时间短时,执行内部短的超时时间。

嵌套的内部事务比外部事务超时时间长时,内部事务超时时间无效。

 TransactionScope isolation level

加入环境事务时不能指定和环境事务不同的隔离级别

参考:编写事务应用程序 

https://www.cnblogs.com/artech/tag/CommittableTransaction/

原文地址:https://www.cnblogs.com/fmys/p/9906376.html