如何高效排查系统故障?一分钱引发的系统设计“踩坑”案例

转载自  如何高效排查系统故障?一分钱引发的系统设计“踩坑”案例

背景说明

某日,做产品X的开发接到客户公司电话,说是对账出了1分钱的差错,无法处理。本着“客户第一”的宗旨,开发立马上线查看情况。查完发现,按照产品X当日的年化收益率,正常情况下用户在转入57元后一共收益3分钱,合计是57.03元。但是该客户当日却有一笔消费57.04元,导致客户公司系统对多出的1分钱处理不了。再进一步分析,发现用户收益结转时多了1分钱的收益,并且已消费……

也就是说,本来用户只有3分钱收益,结果多发了1分钱给他,也就给公司造成1分钱的损失!用户在产品X里当天收益本应该是0.03元,怎么会变成0.04元呢?多出的1分钱收益从哪里来的呢?

数据库记录分析

带着上面的一系列疑问,开发人员首先排查了产品X收益的数据库记录。通过查询数据库发现,该用户收益结转在同一天内存在2笔交易记录。交易记录1创建时间为8:00:23,记录2创建时间为8:00:29,交易记录1和2的最后修改时间均为8:00:29,如图4-1所示。

图4-1  用户当日收益结转数据库记录分析 

正常情况下产品X收益每天只会结转一次,而这个用户当日有两笔收益结转记录。开发人员怀疑,很可能是出现了并发问题。

继续跟踪第一笔“TXID a”的记录,开发确认线上日志存在超时情况,失败原因是数据库链接数已满,线程等待提交。

分布式锁超时时间是5s,第一笔记录从创建到修改提交经历了6s,由此可见是在分布式锁失效之后,获得了数据库链接,进行提交成功。

有了以上三个排查思路后,我们可以开始逆推整个过程。

过程逆推

根据数据库记录逆推当时的运行情况,如图4-2所示。

(1)由于数据库连接数被占满,流水1创建的事务处于等待提交状态。

(2)系统A发现交易失败,重试次数不满8次的,立即发起重试,触发生成流水2的请求。

(3)5s以内数据均被分布式锁拦截,无法提交。

(4)经过5s后,系统B的分布式锁失效,此时事务仍在等待未提交。

(5)6s时,流水2成功越过数据库查询幂等校验发起事务,此时流水1拿到数据库连接,流水1和2两个事务同时提交。

(6)由于数据库未做唯一索引,且支付受理模块打穿下层幂等原则,生成2个TXID,导致两事务同时提交成功。

(7)收益结转重复记账,用户多了一笔收入。

 

图4-2  数据库分布式锁超时并发控制失效

深入分析

完成了整个问题的过程逆推后,开发人员进一步分析,发现问题真正的原因还是在系统设计上。如图4-3所示,系统A的事务允许一定时间的等待,而上层业务的重试时间又比这个等待的时间要短。这就存在一个问题:系统A的事务还在等待中,业务就又发起了重试。如果是在这个应用场景下(可能业务上对重试要求更高一些),那么对幂等控制的要求就更高了。而仅仅通过一个分布式锁来控制,如果分布式锁的超时时间设置的比事务允许等待的时间短,那么在锁失效之后就一定会同时提交两笔请求。

图4-3  分布式锁超时并发控制时间轴

继续对整个过程抽象化,开发人员得出一个结论:分布式锁在以下条件同时满足的情况下并发控制会被打穿。

(1)上层业务系统层面有重试机制。

(2)业务请求存在一定时间之后提交成功的情况,例如本例中第一次请求在事务等待6s后获得了数据库链接,提交数据库成功。

(3)下游系统缺乏其他有效的幂等控制手段。

思考

了解了问题的来龙去脉后,接下来要怎么解决这类问题呢?我们想了以下几个方案。

(1)调整B系统上的tr和分布式锁超时时间,tr超时调整为10s,分布式锁超时调整为30s。

(2)防止做收益结转产生并发控制幂等,调整了收益结转流水号的生成规则:前8位取X收益结转传入的交易号的前8位,第10位系统版本设置为“9”,最后8位seq取交易号的最后8位,降低问题出现几率。

方案一:调整超时时间

调整超时时间后,业务重试时间与分布式锁有效时间的分布时间轴如图4-4所示,即在事务允许等待后提交成功的时间之外,再进行重试,另外分布式锁在整个阶段均有效,防止提交。

图4-4  分布式锁超时并发控制时间轴

方案一验证有效。

方案二:增加幂等控制(推荐)

如图4-5所示,单纯靠分布式锁不是控制并发幂等的方式,最稳妥的方式还是在提交记录的时候通过数据库严格控制幂等。确保不论如何设置超时时间,都不会出现幂等控制的问题。

图4-5  分布式锁超时并发控制时间轴

方案二验证有效。

小结

资金安全无小事,而幂等控制又是资金安全中的重中之重。回顾本文案例,从问题分析定位,到整个逻辑的梳理清洗,其中涉及了三个时间轴的相互作用,再加上事务、分布式锁、重试等,整个问题发生的逻辑还是比较复杂的。因此,在系统并发幂等控制设计中,单纯的分布式锁并不具备严格控制并发幂等的作用,建议在系统设计时,将第三方唯一性的幂等控制作为幂等控制的兜底方案,控制好这道幂等防线,这样不论业务如何设计,就万变不离其宗了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/325577.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JWT(JSON web token)

1.什么是JWT JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally sign…

优秀学生专栏——李浩然

今天回访的同学是李浩然同学,李浩然同学不光长得帅(下面有照片哦),技术还过硬,今年5月份毕业的,目前从事教学工作,自从工作以来,企业曾多次向学校表扬李浩然同学,下面是对…

@ResponseBody导致的返回值中文乱码

新人学习springMVC开发框架,用到ajax 通过 ResponseBody 来获取返回值。 不得不说 ResponseBody的功能很强大,可以直接将返回类打包成json格式省却了很多事, 但是如果返回值是String类型的话,就会出现中文乱码问题,自…

一步步学习EF Core(1.DBFirst)

前言 很久没写博客了,因为真的很忙,终于空下来,打算学习一下EF Core顺便写个系列, 今天我们就来看看第一篇DBFirst. 本文环境:VS2017 Win7 .NET Core1.1 EF Core1.1.2 正文 这里我们不讨论是用DBFirst好,还是CodeFirst高端..各有各自的用处和适用场景.. 我们单纯的只是…

jsp数据交互(一)

一、jsp的内置对象: 1.out:out.print(“输出的内容”); 2.requset: (1)解决乱码:request.setCharacterEncoding(“utf-8”); (2)获取form表单里面的值: String name request.getParameter(“name”); 括号里面的参数是表单里面name的值。 (3…

JS中点击超链接但是不跳转

方式一 <td><a href"javascript:;">Delete</a></td>方式二 函数的返回值为false allA[i].onclick function(){var tr this.parentNode.parentNode;//var name tr.getElementsByTagName("td")[0].innerHTML;var name tr.child…

Greendao bean序列化出现的 问题!

报错&#xff1a; Found 1 problem(s) parsing "/home/zjs/Desktop/websocketTest/app/src/main/java/com/example/websockettest/dao/TerminalBean.java". First problem: Pb(96) The serializable class TerminalBean does not declare a static final serialVers…

ASP.NET Core改进了.NET Framework中的字符串处理

显然Microsoft开发人员和管理人员并没有表达清楚&#xff0c;事实上ASP.NET Core 2.0将会得到整个.NET Framework的支持。当前的更改只实现了在ASP.NET上提供.NET Core&#xff0c;这是为了便于开发而采取的一个临时步骤。对此&#xff0c;在ASP.NET Core预览发行声明中给出了如…

阿里P9谈程序员程序员的青春饭

转载自 阿里P9谈程序员程序员的"青春饭" 导读&#xff1a;你是否曾经认真思考过——毕业3-5年、10年&#xff0c;乃至更久后&#xff0c;我们希望成为什么样的人&#xff1f;作为一名技术人&#xff0c;我们要如何规划自己的职业发展生涯&#xff1f;网上热议的“…

优秀学生专栏——孙振涛

今天继续回访17级优秀毕业生&#xff0c;今天回访的同学是孙振涛同学&#xff0c;孙振涛在班内一直都是比较安稳守纪律&#xff0c;上学期间未违反过任何纪律&#xff0c;毕业之后自己创业合伙开了一个互联网公司&#xff0c;目前公司正在走上正轨&#xff0c;以下是对孙振涛同…

Android public class MyApplication extends MultiDexApplication使用

build.gradle(app)中设置 1. defaultConfig { multiDexEnabled true } 2. dependencies { compile ‘com.android.support:multidex:1.0.1’ } 3.使用 extends MultiDexApplication

不可思议黑科技,Xamarin移动开发新时代

黑科技&#xff01;新一代Xamarin竟然可以将.NET代码原生编译成&#xff1a;Jar包供Java原生调用、swift类库、obj-c类库、C类库 供目标平台传统代码直接调用 之前和很多朋友聊到Xamarin觉得确实不错&#xff0c;原生性能&#xff0c;研发效率提升2倍&#xff0c;研发成本降低5…

这可能是最生动的加密相关科普文章

转载自 这可能是最生动的加密相关科普文章 谁都不想在通信过程中被别人“窃取”小秘密。本文借助一对情侣与八卦女、猥琐男的斗智故事&#xff0c;为大家讲述科普密码学基础知识。既有料又有趣&#xff0c;深入浅出&#xff0c;相信你会喜欢。 一、背景 事情是这样的&#…

学习心得——王梦茹

18级青鸟1班王梦茹编程其实是一门既枯燥又简单有乐趣的一门技术&#xff0c;这要根据个人而言&#xff0c;也许每个人来这里学习编程的初衷都是不一样的&#xff0c;但是你可以去慢慢的发现编程中的乐趣&#xff0c;兴趣是最大的老师&#xff0c;对感兴趣的事学习起来往往是事半…

JS中函数的返回值

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>01_数据类型</title> </head> <body> <!-- 1. 分类(2大类)* 基本(值)类型* Number: 任意数值* String: 任意文本* Boolean: true…

System.err: java.net.UnknownServiceException: CLEARTEXT communication to 192.168.43.172 not permitte

解决方式 在res目录下创建一个xml文件夹&#xff0c;并创建文件network_security_config.xml&#xff0c;内容如下&#xff1a; <?xml version"1.0" encoding"utf-8"?> <network-security-config xmlns:android"http://schemas.android.c…

学习心得——李嫣然、逯广捷

18级青鸟1班李嫣然、逯广捷我觉得学习中最基本的一点&#xff0c;就是主动&#xff0c;学习是自己的事情&#xff0c;只有积极主动地学习才能感受到学习中的乐趣&#xff0c;要是越烦越不想学那肯定就是更学不会&#xff0c;这样周而复始最后就是什么都不会&#xff0c;所以说学…

一步步学习EF Core(2.事务与日志)

前言 上节我们留了一个问题,为什么EF Core中,我们加载班级,数据并不会出来 其实答案很简单,~ 因为在EF Core1.1.2 中我们在EF6.0中用到的的延迟加载功能并没有被加入,不过在EF Core 2.0中,这个功能将回归 而且这个功能是否需要被加入进去,社区也在激烈的讨论当中,有兴趣的可…

一个思维习惯,让你成为架构师

转载自 一个思维习惯&#xff0c;让你成为架构师 程序员的迷茫不仅仅是面对技术繁杂的无力感&#xff0c;更重要的是因为长期埋没于软件 世界的浩大的分工体系中&#xff0c;无法看清从业务到软件架构的价值链条&#xff0c;无法清楚定位自 己在分工体系的位置&#xff0c;处…

学习心得——高婕

18级青鸟1班高婕在这里和大家谈谈我学习的心得体会。我认为&#xff0c;要把学习当作是一种乐趣&#xff0c;不要当作一种负担。作为一名学生&#xff0c;我们要尊重老师的辛勤劳动&#xff0c;认真听好每一节课。其实在这里学习编程很简单&#xff0c;跟着老师的步调走&#x…