RCA:分布式锁bug导致支付网关拿到同一个锁

Posted 云纵达摩院

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RCA:分布式锁bug导致支付网关拿到同一个锁相关的知识,希望对你有一定的参考价值。

研发中心 于增智 201803


背景:

dlock 是2013年我司自行开发的基于 Redis 的分布式锁 Java 实现。


问题现象:

支付网关昨天出现队列写库唯一索引冲突的异常,目前该问题不会影响支付业务,只会输出异常日志。


问题原因:

由于最近也没有上线,随怀疑是分布式锁没有锁住的问题。于是排查了下 dlock 的日志和代码,发现问题。

08:16:26,781 [resin-port-10340-52] ERROR D.LOG [//T1803070……00/] - [{"handleCost":0,"errorMsg":"can not get resource!","_serialId":1,"_method":"RedisConnectionFactory.getConnection()#1520……37#","url":"redis1-1.y……01"}]

 

dlock 可以配置多个 redis 实例,通过 failover 机制,当访问失败时自动切换主池子实例,这个机制在主 redis 实例完全挂掉的情况下没有问题,但是主 redis 实例没有挂掉,而只是业务服务的某台机器访问出现问题的情况下,就会出现类似于“脑裂”的现象。


    比如支付网关两台节点A和B,两台 redis 实例分别为R1和R2,正常情况下,AB均访问主实例R1,

    A->Dlock->主R1

    B->Dlock->主R1

    当B服务从R1获取 redis 连接失败时,会自动切换到可用的节点R2,并设置R2为当前实例,此时访问情况就变成了

    A->Dlock->主R1

    B->Dlock->主R2

    由于 dlock 加锁是先通过主实例 setnx 判断并写入键值,然后再写入其他实例,这两步操作并非原子级操作,所以就会出现同时拿到锁的情况。


解决办法:

需要修复 dlock 的 failover 机制。

假如真的发生了脑裂,支付网关通过两条措施确保不会重复执行(幂等性):

    1、队列唯一索引:通过交易号+队列类型+key+业务标识等进行 md5 计算出来的值,防止重复执行。

    2、在写队列前,都会加分布式锁。


经验教训:

让客户端判断远端集群哪一个节点不可用,就会导致脑裂。当年 Cobar 就出现过这个问题。


-EOF-

欢迎关注云纵达摩院


不管什么行业,IT 技术已经渐渐成为支撑许多公司产品的骨架,但多数这些公司的领导对于 IT 人才的重视依然不够,没有技术积累。这类公司终究会遭受到短视的后果,最明显的是三点:1. 技术滞后拖累产品创新的步伐 2. 系统运营成本居高不下,侵蚀公司获利 3. 系统不稳甚至故障频发,为公司带来灾难。

——蔡学镛

以上是关于RCA:分布式锁bug导致支付网关拿到同一个锁的主要内容,如果未能解决你的问题,请参考以下文章

踩到一个关于分布式锁的非比寻常的BUG!

redis分布式锁

Redis 分布式锁没这么简单,网上大多数都有 bug

分布式锁解决方案

springboot+redis实现分布式锁

从一个bug看Redisson分布式锁的设计