C#中分布式事务的超时处理问题
事务是个很精妙的存在,我们在数据层、服务层、业务逻辑层等多处地方都会使用到。
在这里我只说下TransactionScope这个微软推荐使用的隐式事务。它是从Framework 2.0开始引入的一个事务管理类,在使用隐式事务时,事务完成前 程序应调用TransactionScope的Complete()方法,将事务提交,然后利用Dispose()释放事务对象。若执行期间出现错误,事务将自动回滚。
比如:
using (ransactionScope scope = new TransactionScope())
{
//to do something
scope.Complete();
}
在这里个人建议用using来创建,因为using实现了IDispose接口,它会隐式的调用TransactionScope对象的Dispose方法,即使发生异常时也是如此,能确保在事务结束或者异常的时候也能正确的释放资源。其实我们反编译一下,它的内部实现就是一个try...finally代码块,这样也就不难理解using的作用了。
说主题,在某地市的某库升级中,为避免程序运行中产生脏数据以及数据更新不一致导致的重复同步情况,在可能产生上述问题的考虑下,我用这个TransactionScope来对上述的操作进行事务处理。在本机的测试环境中,运行结果是正常的,当然这个运行正常的前提是数据量较小的情况下,我每次只对一条或者十几条数据的不同表进行insert和update。然而部署到生产环境针对真实数据运行之后,发现这个事务总是回滚,一直无法正常提交。程序也就没法正常跑起来。因为生产环境中的数据有60W左右,insert一次、update一次,最后再insert一条同步语句,前2个操作都是比较耗时的。我切换回测试环境调试了一下,逐行运行,发现当执行完第一个insert之后,执行第二个update时发生异常了。这个异常由TransactionScope抛出,异常提示是:事务已中止。这个错误,在数据量小的情况下不会发生,数据量大一些就出现了,这个是不是和事务处理的时间长短有关呢?因为我明显感觉到在这次调试的时候,执行的时间比之前数据量只有一条的时候长了很多,至少花费1分钟以上。于是google一下,验证了我的想法。
TransactionScope有些重载函数是可以接受TimeSpan类型的值,这个就是事务的超时时间了。当事务实现隔离的时候,事务范围内的资源将会被锁定,如果一些事务长期占有资源,那将很容易造成死锁,为了避免这个问题,TransactionScope事务会定义一个超时限制,这个超时默认值为60秒。如果事务超过此时间,即使没有发生异常,也会自动中止。上面问题的原因算是找到了,知道了原因,那么也就很好解决了。我们可以在Web.Config 中配置:
<configuration>
<system.transactions>
<defaultSettings timeout="00:05:00" />
</system.transactions>
</configuration>
或者在using的时候就定义好超时时间:
new TimeSpan(0, 5, 0)))
或者先初始化事物行为的附加信息,然后定义超时时间:
TransactionOptions tOpt = new TransactionOptions();
tOpt.IsolationLevel = IsolationLevel.ReadCommitted; //设置TransactionOptions模式
tOpt.Timeout = new TimeSpan(0, 5, 0); // 设置超时时间为5分钟
using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required, tOpt))
我这里定义的是5分钟,其实整个过程处理起来也就第一次处理历史数据需要1到2分钟时间,以后每天只需处理几十条数据,这个时间基本是秒级别的。
这里说明下, 超时时间如果设置为0时表示超时无限长。无限长的设置主要对调试有用,调试过程中可能要逐步通过代码来隔离业务逻辑中的问题,并且在尝试确定问题期间不希望所调试的事务超时。在所有其他情况下使用无限长的超时时一定要格外小心,因为它会覆盖防止事务死锁的保护。[这段说明摘抄至MSDN]
1、SQL事务
优点:执行效率最佳
限制:事务上下文仅在数据库中调用,难以实现复杂的业务逻辑。
- CREATE PROCEDURE Tran1
- as
- begin tran
- set xact_abort on
- Insert Into trantest (id,test)values(1,'test')
- Insert Into trantest (id,test)values(2,'test')
- commit tran
- GO
- --set xact_abort on 表示遇到错误立即回滚
- --当然你也可以这么写
- CREATE PROCEDURE tran1
- as
- begin tran
- insert into trantest(id,test)values(1,'test')
- if(@@error<>0)
- rollback tran
- else
- begin
- insert into trantest(id,test)values(2,'test')
- if(@@error<>0)
- rollback tran
- else
- commit tran
- end
- GO
- CREATE PROCEDURE Tran1
- as
- begin tran
- set xact_abort on
- Insert Into trantest (id,test)values(1,'test')
- Insert Into trantest (id,test)values(2,'test')
- commit tran
- GO
- --set xact_abort on 表示遇到错误立即回滚
- --当然你也可以这么写
- CREATE PROCEDURE tran1
- as
- begin tran
- insert into trantest(id,test)values(1,'test')
- if(@@error<>0)
- rollback tran
- else
- begin
- insert into trantest(id,test)values(2,'test')
- if(@@error<>0)
- rollback tran
- else
- commit tran
- end
- GO
2、ADO.NET 事务
在ADO.NET 中,可以使用Connection 和Transaction 对象来控制事务。若要执行事务,请执行下列操作:
调用Connection 对象的BeginTransaction 方法来标记事务的开始。
将Transaction 对象分配给要执行的Command的Transaction 属性。
执行所需的命令。
调用Transaction 对象的Commit 方法来完成事务,或调用Rollback 方法来取消事务。
优点:简单,效率和数据库事务差不多快。
缺点:事务执行在数据库连接层上,所以你需要在事务过程中手动的维护一个连接。
- SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Test"].ConnectionString);
- conn.Open();
- SqlTransaction tx = conn.BeginTransaction(IsolationLevel.ReadCommitted);
- SqlCommand cmd = new SqlCommand();
- cmd.Connection = conn;
- cmd.Transaction = tx;
- try
- {
- cmd.CommandText = "INSERT INTO [Test]([Name],[Value]) VALUES ('测试1','1')";
- cmd.ExecuteNonQuery();
- cmd.CommandText = "INSERT INTO [Test]([Name],[Value]) VALUES ('测试2','2')";
- cmd.ExecuteNonQuery();
- tx.Commit();
- }
- catch (Exception ex)
- {
- tx.Rollback();
- throw new Exception(ex.Message, ex);
- }
- finally
- {
- conn.Close();
- }
- SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Test"].ConnectionString);
- conn.Open();
- SqlTransaction tx = conn.BeginTransaction(IsolationLevel.ReadCommitted);
- SqlCommand cmd = new SqlCommand();
- cmd.Connection = conn;
- cmd.Transaction = tx;
- try
- {
- cmd.CommandText = "INSERT INTO [Test]([Name],[Value]) VALUES ('测试1','1')";
- cmd.ExecuteNonQuery();
- cmd.CommandText = "INSERT INTO [Test]([Name],[Value]) VALUES ('测试2','2')";
- cmd.ExecuteNonQuery();
- tx.Commit();
- }
- catch (Exception ex)
- {
- tx.Rollback();
- throw new Exception(ex.Message, ex);
- }
- finally
- {
- conn.Close();
- }
3、TransactionScope事务
在.NET 2.0中新添加了一个名为System.Transactions的命名空间,其提供了一个“轻量级”的、易于使用的事务框架,通过这个框架可以大大简化事务的操作。
这个框架提供了如下优点:
(1)在简单(不涉及分布式)事务中也可以使用声明式的事务处理方法,而不必使用Com+容器和目录注册。
(2)用户根本不需要考虑是简单事务还是分布式事务。它实现一种所谓自动提升事务机制(Promotable Transaction),会自动根据事务中涉及的对象资源判断使用何种事务管理器。
TransactionScope事务类,它可以使代码块成为事务性代码。并自动提升为分布式事务
优点:实现简单,同时能够自动提升为分布式事务
- TransactionOptions option = new TransactionOptions();
- option.IsolationLevel = IsolationLevel.ReadCommitted;
- using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required,option))
- {
- using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Test"].ConnectionString))
- {
- conn.Open();
- SqlCommand cmd = new SqlCommand(conn);
- cmd.CommandText = "INSERT INTO [Test]([Name],[Value]) VALUES ('测试1','1')";
- cmd.ExecuteNonQuery();
- cmd.CommandText = "INSERT INTO [Test]([Name],[Value]) VALUES ('测试2','2')";
- cmd.ExecuteNonQuery();
- }
- ts.Complete();
- }
- TransactionOptions option = new TransactionOptions();
- option.IsolationLevel = IsolationLevel.ReadCommitted;
- using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required,option))
- {
- using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Test"].ConnectionString))
- {
- conn.Open();
- SqlCommand cmd = new SqlCommand(conn);
- cmd.CommandText = "INSERT INTO [Test]([Name],[Value]) VALUES ('测试1','1')";
- cmd.ExecuteNonQuery();
- cmd.CommandText = "INSERT INTO [Test]([Name],[Value]) VALUES ('测试2','2')";
- cmd.ExecuteNonQuery();
- }
- ts.Complete();
- }
4、EnterpriseServices实现事务
就是利用com+实现自动处理事务。
添加引用System.EnterpriseServices.dll
using System.EnterpriseServices;
使用方法参考:http://support.microsoft.com/default.aspx?scid=kb;zh-cn;816141
随便建立一个按钮,在按钮中进行如下操作:
- try
- {
- work1();
- work2();
- ContextUtil.SetComplete();
- }
- catch(System.Exception except)
- {
- ContextUtil.SetAbort();
- Response.Write(except.Message);
- }
- try
- {
- work1();
- work2();
- ContextUtil.SetComplete();
- }
- catch(System.Exception except)
- {
- ContextUtil.SetAbort();
- Response.Write(except.Message);
- }
然后在页面中添加2个操作,模拟一下在逻辑层调用不同类中的操作的情况 :
- private void work1()
- {
- SqlConnection conn=new SqlConnection(System.Configuration.ConfigurationSettings.AppSettings["conn"]);
- SqlCommand cmd1=new SqlCommand("Insert Into trantest (id,test)values(1,'test')",conn);
- conn.Open();
- cmd1.ExecuteNonQuery();
- conn.Close();
- }
- private void work2()
- {
- SqlConnection conn=new SqlConnection(System.Configuration.ConfigurationSettings.AppSettings["conn"]);
- SqlCommand cmd2=new SqlCommand("Insert Into trantest (id,test)values(2,'test')",conn);
- conn.Open();
- cmd2.ExecuteNonQuery();
- conn.Close();
- }
- 修改前台页面在<%Page后面添加 Transaction="Required" 即可
C#中分布式事务的超时处理问题的更多相关文章
- Google关于Spanner的论文中分布式事务的实现
Google关于Spanner的论文中分布式事务的实现 Google在Spanner相关的论文中详细的解释了Percolator分布式事务的实现方式, 而且用简洁的伪代码示例怎么实现分布式事务; Pe ...
- Java生鲜电商平台-SpringCloud微服务架构中分布式事务解决方案
Java生鲜电商平台-SpringCloud微服务架构中分布式事务解决方案 说明:Java生鲜电商平台中由于采用了微服务架构进行业务的处理,买家,卖家,配送,销售,供应商等进行服务化,但是不可避免存在 ...
- ASP.NET中分布式事务的使用
之前发表了一篇事务的存储过程,最近在做项目的时候遇到分布式事务,所有总结一下,跟大家分享和交流一下经验.首先说明为什么要分布式事务呢?先说说我在项目的哪里遇到分布式事务吧,我是在做网站后台开发的时候, ...
- j2ee中spring的分布式事务实现及解决方案
1 java事务类型 Java事务的类型有三种:JDBC事务.JTA(Java Transaction API)事务.容器事务. 常见的容器事务如Spring事务,容器事务主要是J2EE应用服务器提供 ...
- MySQL 中基于 XA 实现的分布式事务
1 XA协议 首先我们来简要看下分布式事务处理的XA规范可知XA规范中分布式事务有AP,RM,TM组成: 其中应用程序(Application Program ,简称AP):AP定义事务边界(定义事务 ...
- Mycat 分布式事务的实现
引言:Mycat已经成为了一个强大的开源分布式数据库中间件产品.面对企业应用的海量数据事务处理,是目前最好的开源解决方案.但是如果想让多台机器中的数据保存一致,比较常规的解决方法是引入"协调 ...
- 微服务分布式事务之LCN、TCC
在亿级流量架构之分布式事务解决方案对比中, 已经简单阐明了从本机事务到分布式事务的演变过程, 文章的最后简单说明了TCC事务, 这儿将会深入了解TCC事务是原理, 以及理论支持, 最后会用Demo举例 ...
- 分布式事务与Seate框架(1)——分布式事务理论
前言 虽然在实际工作中,由于公司与项目规模限制,实际上所谓的微服务分布式事务都不会涉及,更别提单独部署构建Seata集群.但是作为需要不断向前看的我,还是有必要记录下相关的分布式事务理论与Seate框 ...
- oracle分布式事务总结-转载
基本概念 Local Coordinator:在分布事务中,必须参考其它节点上的数据才能完成自己这部分操作的站点. Global Coordinator:分布事务的发起者,负责协调这个分布事务. Co ...
随机推荐
- abp(net core)+easyui+efcore实现仓储管理系统——入库管理之六(四十二)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...
- Nginx | CentOS 8 安装Nginx详细教程
Nginx是一个web服务器也可以用来做负载均衡及反向代理使用, 目前使用最多的就是负载均衡,这篇文章主要介绍了centos8 安装 nginx Nginx是一种开源的高性能HTTP和反向代理服务器, ...
- 【bzoj2049】[Sdoi2008]Cave 洞穴勘测——线段树上bfs求可撤销并查集
题面 2049: [Sdoi2008]Cave 洞穴勘测 Time Limit: 10 Sec Memory Limit: 259 MB Submit: 12030 Solved: 6024 Desc ...
- Linux基础篇学习——常见系统命令:ls,pwd,cd,date,hwclock,passwd,su,clear,who,w,uname,uptime,last,dmesg,free,ps,top
ls 显示指定目录中的内容 ls [OPTION]... [FILE]... OPTION -a --all,显示所有文件包括隐藏文件 -l 列出长属性,显示出文件的属性与权限等数据信息 -i 列出 ...
- Hive支持的文件格式和压缩格式及各自特点
Hive中的文件格式 1-TEXTFILE 文本格式,Hive的默认格式,数据不压缩,磁盘开销大.数据解析开销大. 对应的hive API为:org.apache.hadoop.mapred.Text ...
- vue封装axios
一.安装axios npm install --save axios 二.在src下面创建文件夹api=>api.js(接口集合)+http.js(封装的请求) 三.在main.js中引用api ...
- 面试刷题25:jvm的垃圾收集算法?
垃圾收集是java语言的亮点,大大提高了开发人员的效率. 垃圾收集即GC,当内存不足的时候触发,不同的jvm版本算法和机制都有差别. 我是李福春,我在准备面试,今天的问题是: jvm的垃圾回收算法有哪 ...
- CodeForces - 1249E 楼梯和电梯
题意:第一行输入n和c,表示有n层楼,电梯来到需要时间c 输入两行数,每行n-1个,表示从一楼到二楼,二楼到三楼.....n-1楼到n楼,a[ ] 走楼梯和 b[ ] 乘电梯花费的时间 思路:动态规划 ...
- Web Scraper 性能测试 (-_-)
刚在研究 Python 爬虫的时候,看到了个小白工具,叫 Web Scraper,于是来测试下好不好用. Web Scraper 是什么? 它是一个谷歌浏览器的插件, 用于批量抓取网页信息, 主要特点 ...
- LFU五种实现方式,从简单到复杂
前言 最近刷力扣题,对于我这种 0 基础来说,真的是脑壳疼啊.这个月我估计都是中等和困难题,没有简单题了. 幸好,力扣上有各种大牛给写题解.看着他们行云流水的代码,真的是羡慕不已.让我印象最深刻的就是 ...