嵌套事务无法回滚,会造成数据库事务嵌套死锁吗

2357人阅读
首先必须执行con.setAutoCommit(false)方法,将JDBC事务设置为手动提交,否则手动提交mit()无效,手动回滚con.rollback()引发SQLException:AutoCommit 模式设置为“true”时,无法调用回滚操作。
我们执行con.setAutoCommit(false)方法,将JDBC事务设置为手动提交,然后分9种情况讨论JDBC事务和数据库事务。
描述:存储过程里面有一条INSERT语句在数据库事务管理下,而另一条INSERT语句在JDBC事务管理下,假定我们现在执行的语句均不违反约束。
情况一:JDBC事务和数据库事务都既不提交也不回滚
结果:未出现任何异常,但语句也并未提交。
情况二:JDBC事务既不提交也不回滚,数据库事务提交
结果:未出现任何异常,但两条语句均未提交。
情况三:JDBC事务既不提交也不回滚,数据库事务回滚
结果:未出现任何异常,但两条语句均未提交。
情况四:JDBC事务和数据库事务都回滚
结果:在执行con.rollback()的时候出现SQLException:服务器无法继续执行该事务,说明: 。当然,两条语句均未成功。
情况五:JDBC事务回滚,数据库事务提交
结果:虽然数据库事务提交,但是两条语句均未成功,JDBC事务的回滚影响到了数据库事务。
情况六:JDBC事务回滚,数据库事务既不提交也不回滚
结果:未出现任何异常,但两条语句均未提交。
情况七:JDBC事务提交,数据库事务回滚
结果:在执行con.rollback()的时候出现SQLException:服务器无法继续执行该事务,说明: 。当然,两条语句均未成功。
情况八:JDBC事务和数据库事务都提交
结果:执行一切顺利,两条语句都正确执行。
情况九:JDBC事务提交,数据库事务既不提交也不回滚
结果:未出现任何异常,但两条语句均未提交。
总结:成功提交的情况只有一种——JDBC事务和数据库事务都提交;若数据库事务提交或回滚,则mit()失效,而con.rollback()则会报异常。
推荐配置:若JDBC事务和数据库事务同时存在,数据库事务把提交和回滚的情况配完整,而JDBC事务只需执行mit()方法,它会根据数据库事务的情况自动判断是否真正提交。
&&相关文章推荐
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:155721次
积分:1405
积分:1405
排名:千里之外
原创:18篇
转载:32篇
评论:10条
(2)(1)(8)(6)(18)(7)(8)读书笔记 SQL 事务理解
来源:博客园
事务的ACID属性
Atomicity 原子性 
每个事务作为原子单元工作(即不可以再拆分),也就是说所有数据库变动事务,要么成功要么不成功。
SQL Server把每个DML或者 DDL命令都当做一个事务。不允许任何命令只是部分成功。 比如一个UPDATE语句更新500行,除非500行全部更新,否则有任何情况阻止该命令更新。SQL Server会终止该命令更新,并且回滚事务。
Consistency 一致性 
每个事务,不论成功或失败,数据库中定义的约束状态必须一致,否则会回滚。
比方说一个事务企图插入一个无效的外键,这会被SQL Server检测到违反约束,并且生成一个错误提示。
Isolation 隔离性 
事务的执行看上去是互不干扰的,隔离的程度基于隔离级别设置。比方说,两个事务要更改同一个数据,其中之一必须等另外一个完成以后才能去修改。
SQL Server用‘锁’来达到事务的隔离的目的。 通常有两种锁 Shared locks 共享锁 用作读取数据
Exclusive locks 排它锁 用作变更数据
Durability 持久性 
事务操作结果都会被保存下来(事务日志 database transaction log)。每个数据库变动(数据修改语句或者DDL语句)首先会把原始版本的数据(updates和deletes)写到事务日志,当事务提交,并且所有一致性检查都通过以后,事件成功提交的事实就会写入事务日志。如果数据库此之前意外当机,那么再次启动后,数据会回滚。
 
阻塞 Blocking
如果两个seesion在同样的资源上申请排它锁 ,当其中一个生成排它锁以后,另外一个必须等第一个释放后(commmit 或者 roll back)才能申请。也就是说同一时间内,两个会话无法写入同一个资源, 这样,一个写入阻塞了另外一个写入。这就叫阻塞
除了同一资源申请排它锁会造成堵塞之外,一个排他锁也会阻止其他事务读取同样的资源。因为排它锁和共享锁是不相容的。
 
死锁 Deadlocking
如果有两个或多个Session互相阻塞,这就会造成死锁。当SQL Server 检测到以后,会中断其中一个,然后返回错误信息 1205.
Session 1
Session 2

USE TSQL2012; BEGIN TRAN;




USE TSQL2012;
BEGIN TRAN;






UPDATE HR.Employees
SET Region = N'10004'
WHERE empid = 1



 




UPDATE Production.Suppliers
SET Fax = N'555-1212'
WHERE supplierid = 1






UPDATE Production.Suppliers
SET Fax = N'555-1212'
WHERE supplierid = 1



 


&blocked&

UPDATE HR.Employees
SET phone = N'555-9999'
WHERE empid = 1






&blocked&


发生死锁以后其中一个事务会完成,而另外一个会被中断,并且显示1205错误信息
Msg 1205, Level 13, State 51, Line 1
Transaction (Process ID 61) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
最后可以把成功执行事务回滚
IF @@TRANCOUNT & 0 ROLLBACK
IF @@TRANCOUNT & 0 ROLLBACK



@@TRANCOUNT 
用来返回在当前连接上执行的 BEGIN TRANSACTION 语句的数目。
如果返回0则表示当前不在一个事务里面, 1表示在一个事务里面,大于1则表示在一个嵌套的事务里面。

注意一个事务只能包含一个ROLLBACK 命令,他会回滚整个事务,然后重置 @@TRANCOUNT 为0

实际例子如下
1. COMMIT TRAN

USE TSQL2012;
SELECT @@TRANCOUNT; -- = 0
BEGIN TRAN;
SELECT @@TRANCOUNT; -- = 1
BEGIN TRAN;
SELECT @@TRANCOUNT; -- = 2
-- Issue data modification or DDL commands here
COMMIT
SELECT @@TRANCOUNT; -- = 1
COMMIT TRAN;
SELECT @@TRANCOUNT; -- = 0

2. ROLLBACK TRAN

USE TSQL2012;
SELECT @@TRANCOUNT; -- = 0
BEGIN TRAN;
SELECT @@TRANCOUNT; -- = 1
BEGIN TRAN;
SELECT @@TRANCOUNT; -- = 2
-- Issue data modification or DDL command here
ROLLBACK; -- rolls back the entire transaction at this point
SELECT @@TRANCOUNT; -- = 0

 
事务隔离级别
READ COMMITTED

这个是默认隔离级别,只有数据更改被提交以后才能被读取们所有SELECT语句会企图取得一个共享锁,修改数据的另外一个事务会话的排他锁会阻塞READ COMMITTED 会话。
在查询语句加入 WITH (NOLOCK) 或 WITH (READUNCOMMITTED)可以直接读取
注意现在 WITH (NOLOCK) 不推荐使用,在新的SQL 版本中 Update和Delete 语句里面不允许用这个选项了。去而代之的是 WITH (READUNCOMMITTED)

SELECT lastname, firstname
FROM HR.Employees WITH (READUNCOMMITTED);

例子: 写入阻塞写入
Session 1
Session 2

USE TSQL2012; BEGIN TRAN;




USE TSQL2012;




UPDATE HR.Employees
SET postalcode = N'10004'

WHERE empid = 1;





UPDATE HR.Employees
SET phone = N'555-9999'

WHERE empid = 1;







&more work&
&blocked&



COMMIT TRAN;
 




&results returned&



写入阻塞读取
Session 1
Session 2




USE TSQL2012;
BEGIN TRAN;




USE TSQL2012;




UPDATE HR.Employees
SET postalcode = N'10005'

WHERE empid = 1





SELECT lastname, firstname
FROM HR.Employees









&blocked&







COMMIT TRAN;
 




&results returned&



READ UNCOMMMITED 
这个隔离级别允许reader读取未提交的数据,这个设定使得SELECT 语句不用申请共享锁,不会被writer阻塞。然而被读取的数据在随后可能会回滚到原来的状态,这回导致脏读(reading dirty data)
例子
Session 1
Session 2




USE TSQL2012;
BEGIN TRAN;





USE TSQL2012;
SET TRANSACTION ISOLATION LEVEL READ

UNCOMMITTED;








UPDATE HR.Employees
SET region = N'1004'

WHERE empid = 1;




 





SELECT lastname, firstname, region
FROM HR.Employees








&results returned: region = 1004 for empid = 1&



ROLLBACK TRAN;
 




&region for empid = 1 rolled back to
original value&





SELECT lastname, firstname, region
FROM HR.E








&results returned: region = original value for empid = 1&



READ COMMITTED SNAPSHOT 这个实际上不是一个新的隔离级别,这是 READ COMMITTED的一个选项,该隔离级别有以下特性:
●使用tempdb来存储被修改数据的原始版本。 这样当reader 读取数据的时候读取的是原始版本 ,不需要共享锁,也不会被writer阻塞。
即读取 (原始)提交数据。
●READ COMMITTED SNAPSHOT 选项可以针对每个数据库设置
●RCSI(READ COMMITTED SNAPSHOT ) 不是独立的隔离级别,与READ COMMITTED的区别仅仅是防止writer阻塞reader。
●RCSI 是Windows Azure SQL Database的默认隔离级别
REPEATABLE READ
在事务结束之前,该事务中的每个读取操作都是可重复的。所有被读取的数据都会被共享锁锁定,读取过程中其他更新或删除的事务无法对这些数据做更改。 不过事务读取完成以后,可能会有新的数据行加入进来,导致幻读(phantom read)
SNAPSHOT 该隔离级别同样使用tempdb来空置行的版本控制,事务只能识别在其开始之前提交的数据修改。 在当前事务中执行的语句将看不到在当前事务开始以后由其他事务所做的数据修改。所以不存在幻读(phantom read) ,事务中的语句所获取的已提交数据快照对应于该数据在事务开始时的状态。SNAPSHOT 隔离级别不需要共享锁
SERIALIZABLE
最强的隔离。在一个SELECT事务完成之前,其他事务无法向表中插入新行。
 
事务模式 Transaction Modes
Autocommit
单个数据修改或者DDL 被执行以后,如果执行成功会自动提交。
Implicit transaction
隐式声明,先要开启隐式声明,然后每个语句执行以后不会自动提交或者回滚,需要手动处理。例子如下:

USE TSQL2012;
SET IMPLICIT_TRANSACTIONS ON;
SELECT @@TRANCOUNT; -- 0
SET IDENTITY_INSERT Production.Products ON;
-- Issue DML or DDL command here
INSERT INTO Production.Products(productid, productname, supplierid, categoryid,
unitprice, discontinued)
VALUES(101, N'Test2: Bad categoryid', 1, 1, 18.00, 0);
SELECT @@TRANCOUNT; -- 1
COMMIT TRAN;
SET IDENTITY_INSERT Production.Products OFF;
SET IMPLICIT_TRANSACTIONS OFF;
-- Remove the inserted row
DELETE FROM Production.Products WHERE productid = 101; -- Note the row is deleted

Explicit transaction mode
显式声明,就是用BEGIN TRANSACTION 或者 BEGIN TRAN 命令开始一个事务。例子如下:

USE TSQL2012;
SELECT @@TRANCOUNT; -- 0
BEGIN TRAN;
SELECT @@TRANCOUNT; -- 1
SET IDENTITY_INSERT Production.Products ON;
INSERT INTO Production.Products(productid, productname, supplierid, categoryid,
unitprice, discontinued)
VALUES(101, N'Test2: Bad categoryid', 1, 1, 18.00, 0);
SELECT @@TRANCOUNT; -- 1
SET IDENTITY_INSERT Production.Products OFF;
COMMIT TRAN;


参考文档
sys.dm_tran_database_transactions

Write-Ahead Transaction Log

@@TRANCOUNT

Detecting and Ending Deadlocks

事务隔离级别

SET TRANSACTION ISOLATION LEVEL (Transact-SQL)
/zh-cn/library/ms173763.aspx
免责声明:本站部分内容、图片、文字、视频等来自于互联网,仅供大家学习与交流。相关内容如涉嫌侵犯您的知识产权或其他合法权益,请向本站发送有效通知,我们会及时处理。反馈邮箱&&&&。
学生服务号
在线咨询,奖学金返现,名师点评,等你来互动}

我要回帖

更多关于 嵌套事务 回滚 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信