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 ...
随机推荐
- 常见排序算法总结与分析之交换排序与插入排序-C#实现
前言 每每遇到关于排序算法的问题总是不能很好的解决,对一些概念,思想以及具体实现的认识也是模棱两可.归根结底,还是掌握不够熟练.以前只是看别人写,看了就忘.现在打算自己写,写些自己的东西,做个总结.本 ...
- 一个使用fasttext训练的新闻文本分类器/模型
fastext是什么? Facebook AI Research Lab 发布的一个用于快速进行文本分类和单词表示的库.优点是很快,可以进行分钟级训练,这意味着你可以在几分钟时间内就训练好一个分类模型 ...
- [二分] Codefoces Anton and Making Potions
Anton and Making Potions time limit per test 4 seconds memory limit per test 256 megabytes input sta ...
- matplotlib命令与格式:参数配置文件与参数配置
转自 https://my.oschina.net/swuly302/blog/94805 自定义matplotlib Created Saturday 08 December 2012 5.1 ma ...
- iPhone连接到Mac上叮叮叮断断续续响个不停的解决办法
一.推荐方式 1.让iPhone和Mac通过数据线连接(对,就是连着) 2.打开终端,执行如下命令: sudo killall -STOP -c usbd 3.一分钟内,iPhone即可连上Mac 二 ...
- kerberos系列之flink认证配置
大数据安全系列的其它文章 https://www.cnblogs.com/bainianminguo/p/12548076.html-----------安装kerberos https://www. ...
- AngularJS插件使用---angular-cookies.js
首先在项目中引入angular-cookies.js angular模块中添加依赖:‘ngCookies’ 在控制器中依赖注入$cookies,使用$cookies操作cookie $cookiesP ...
- Mob 之 短信验证集成 SMSSDK
开相关发中总会遇到短信验证这些操作,这周没有来得及写新的东西,借此分享一篇以前学习短信验证的笔记,本文使用的是 Mob 提供的 SMSSDK . 下载 SMSSDK 官网下载地址:SMSSDK 集成 ...
- 【线段树基础】NKOJ 1321 数列操作
时间限制 : 10000 MS 空间限制 : 165536 KB 问题描述 假设有一列数{Ai}(1≤i≤n),支持如下两种操作:将Ak的值加D.(k, D是输入的数)输出As+As+1+…+At ...
- Vertica的这些事(三)——Vertica中实现Oracle中的ws_concat功能
vertica中没有类似Oracle中的ws_concat函数功能,需要开发UDF,自己对C++不熟悉,所有只有想其他方法解决了. 上代码: SELECT node_state, MAX(DECODE ...