文档章节

别使用嵌套事务

买个ZIPPO点蚊香
 买个ZIPPO点蚊香
发布于 2016/01/06 18:27
字数 1264
阅读 176
收藏 7

公司之前一直存在一个规范,就是禁止嵌套事务的使用,一直不太明白为什么,试了下应该是无法控制回滚,今天看大牛的博客发现,问题远远不只如此。

具体总结下来是以下3个问题

1、内层事务回滚,只能回滚全部事务,无法控制单一事务回滚

2、内层事务提交后,回滚外层事务,也会把内层提交了的事务一起回滚

3、因为2的原因,只要整个事务不完全提交,日志空间都无法被释放


    嵌套事务可不会像其语法表现的那样看起来允许事务嵌套。我真不知道为什么有人会这样写代码,我唯一能够想到的就是某个哥们对SQL Server社区嗤之以鼻然后写了这样的代码说:“玩玩你们”。

    让我更详细的解释一下,SQL Server允许你在一个事务中开启嵌套另一个事务,SQL Server允许你提交这个嵌套事务,也允许你回滚这个事务。

    但是,嵌套事务并不是真正的“嵌套”,对于嵌套事务来说SQL Server仅仅能够识别外层的事务。嵌套事务是日志不正常增长的罪魁祸首之一因为开发人员以为回滚了内层事务,仅仅是回滚内层事务。

    但实际上当回滚内层事务时,会回滚整个内层事务,而不是仅仅是内层。这也是为什么我说嵌套事务并不存在。

    所以作为开发人员来讲,永远不要对事务进行嵌套。事务嵌套是邪恶的。

    如果你不相信我说的,那么通过下面的例子就就会相信。创建完数据库和表之后,每一条记录都会导致日志增加8K。

CREATE DATABASE NestedXactsAreNotReal; 
GO 
USE NestedXactsAreNotReal; 
GO 
ALTER DATABASE NestedXactsAreNotReal SET RECOVERY SIMPLE; 
GO 
CREATE TABLE t1 (c1 INT IDENTITY, c2 CHAR (8000) DEFAULT 'a'); 
CREATE CLUSTERED INDEX t1c1 ON t1 (c1); 
GO 
SET NOCOUNT ON; 
GO

 

  测试 #1:回滚内部事务时仅仅回滚内部事务?

BEGIN TRAN OuterTran; 
GO

INSERT INTO t1 DEFAULT Values; 
GO 1000

BEGIN TRAN InnerTran; 
GO

INSERT INTO t1 DEFAULT Values; 
GO 1000

SELECT @@TRANCOUNT, COUNT (*) FROM t1; 
GO

 

    你可以看到得出的结果是2和2000,下面我来回滚内部的事务,按照我们的猜想应该只回滚1000条吧,但事实上你会得到如下结果:

ROLLBACK TRAN InnerTran; 
GO 

消息 6401,级别 16,状态 1,第 2 行 
无法回滚 InnerTran。找不到该名称的事务或保存点。

 

    好吧,由Books Online来看,我只能使用外部事务的名称或是将事务名称留空来进行回滚,代码如下:

ROLLBACK TRAN; 
GO

SELECT @@TRANCOUNT, COUNT (*) FROM t1; 
GO

 

    现在我得到结果是0和0。正如Books Online所言,这个回滚操作将外部事务进行了回滚并将全局变量@@TRANCOUNT设置为0。事务中所有的修改都被回滚,如果想部分回滚的话只能使用SAVE TRAN 和ROLLBACK TRAN。

   

测试 #2:嵌套事务中内部事务提交后会保存内部事务的修改吗?

BEGIN TRAN OuterTran; 
GO

BEGIN TRAN InnerTran; 
GO

INSERT INTO t1 DEFAULT Values; 
GO 1000

COMMIT TRAN InnerTran; 
GO

SELECT COUNT (*) FROM t1; 
GO

 

    正如我所期待,得到的结果是1000。这说明内部事务提交是会修改到磁盘的。但是如果这时外部事务回滚的话,那么不应该回滚内部事务…

ROLLBACK TRAN OuterTran; 
GO

SELECT COUNT (*) FROM t1; 
GO

 

    但运行上面查询后结果是0,这说明外部事务的回滚会影响内部事务。

 

测试 #3:提交嵌套的事务的内部事务至少可以让我清除日志吧。

   在开始这个测试之前我首先清除了日志,然后运行如下代码:

BEGIN TRAN OuterTran; 
GO

BEGIN TRAN InnerTran; 
GO

INSERT INTO t1 DEFAULT Values; 
GO 1000

DBCC SQLPERF ('LOGSPACE'); 
GO

   得到结果:

    2012-10-24_105050

  

    下面我将事务提交后运行CheckPoint(对于简单恢复模式的数据库将会截断日志),得到的结果:

COMMIT TRAN InnerTran; 
GO

CHECKPOINT; 
GO

DBCC SQLPERF ('LOGSPACE'); 
GO

 

    2

 

    我们发现日志的使用不减反赠,这是由于日志写入了CheckPoint记录(详情请看:How do checkpoints work and what gets logged)。提交内部事务不会导致日志被清除,这是由于外部事务回滚时也会连同内部事务一起回滚(译者注:所以这部分VLF在外部事务提交之前永远不会被标记位reusable)。所以这部分日志在外部事务提交之前永远不会被截断。为了证明这一点,我提交外部事务,然后再来看日志:

COMMIT TRAN OuterTran; 
GO

CHECKPOINT; 
GO

DBCC SQLPERF ('LOGSPACE'); 
GO

  3

 

    怎么样,日志使用百分比大幅下降了吧。

    对于嵌套事务来说---Just Say no。(这句话你可以当作来自SQLSkill.com的一个热心的家伙给的福利微笑)


转载地址: http://www.cnblogs.com/CareySon/archive/2013/01/22/2871204.html


本文转载自:http://www.cnblogs.com/CareySon/archive/2013/01/22/2871204.html

买个ZIPPO点蚊香
粉丝 0
博文 22
码字总数 1611
作品 0
程序员
私信 提问
Spring嵌套事务解惑(重点)

http://www.iteye.com/topic/35907 PROPAGATION_REQUIRED -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务...

码代码的小司机
2018/07/01
0
0
Spring 事务传播行为

如果对事务不了解的可以先看下,我的上一篇文章: 数据库事务详解 概述 一般SSH的项目都是使用三层架构即Controller、Services、DAO。 Spring 的事务一般都在Services定义,而Controller、D...

jijs
2018/01/07
0
0
脱离 Spring 实现复杂嵌套事务,之一(必要的概念)

写这篇博文的目的首先是与大家分享一下如何用更轻量化的办法去实现 Spring 那种完善的事务控制。 为什么需要嵌套事务? 我们知道,数据库事务是为了保证数据库操作原子性而设计的一种解决办法...

哈库纳
2014/02/13
0
14
关于PROPAGATION_REQUIRES_NEW和PROPAGATION_NESTED事务的比较

最近在用spring-data-jpa做事务处理时,由于两个事务都处理了同一个表,其中一个事务加锁,另外一个事务不加锁,于是在调用的另外一个事务中使用了嵌套的方式,但是运行却报 JpaDialect doe...

Barudisshu
2016/03/07
122
0
脱离 Spring 实现复杂嵌套事务,之四(NESTED - 嵌套事务)

本文是<实现 Spring 的事务控制>系列文章中一篇。本文假设读者已经阅读并理解《实现 Spring 的事务控制,之一(必要的概念)》文中所涉及的概念(当前连接、引用计数),以及数据库连接的(n...

哈库纳
2014/02/15
0
6

没有更多内容

加载失败,请刷新页面

加载更多

Jenkins系列_插件安装及报错处理

进入Jenkins之后我们可以进行插件的安装,插件管理位于以下模块: 发现上面报了一堆错误,是因为插件的依赖没有安装好,那么这一节,就先把这些错误解决掉吧。解决完成后,也就基本会使用插件...

shzwork
今天
2
0
mysql mysql的所有查询语句和聚合函数(整理一下,忘记了可以随时看看)

查询所有字段 select * from 表名; 查询自定字段 select 字段名 from 表名; 查询指定数据 select * from 表名 where 条件; 带关键字IN的查询 select * from 表名 where 条件 [not] in(元素...

edison_kwok
昨天
9
0
多线程同时加载缓存实现

import com.google.common.cache.Cache;import com.google.common.cache.CacheBuilder;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorServi......

暗中观察
昨天
3
0
利用VisualVM 内存查看

准备工作,建几个测试类。等下就是要查看这几个类里面的属性 package visualvm;public class MultiObject { private String str; private int i; MultiObject(String str...

冷基
昨天
2
0
组装一台工作游戏两用机

一、配置清单如下: 分类 项目 价格(元) 主板 华硕(ASUS)TUF Z370-PLUS GAMING II 电竞特工 Z370二代 支持9代CPU 1049 CPU 英特尔(Intel) i7 8700K 酷睿六核 盒装CPU处理器 2640 风扇 九...

mbzhong
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部