支付系统转账过程中并发交易引起的分布式死锁问题

死锁

死锁的规范定义:集合中的每一个进程都在等待只能由本集合中的其他进程才能引发的事件,那么该组进程是死锁的。从广义上讲,这里的进程指的是一个执行单元。

问题描述

假设【账户A】给 【账户B】转账 500 元,在程序中的转账逻辑如下:
1. 开启事务
2. 【账户A】的余额减去 500 元
3. 【账户B】的余额增加 500 元
4. 提交事务

在集群环境下,在 server1、server2 服务上都部署了支付模块,在该环境下可能会出现如下的场景:

  1. 在 server1 上执行【账户A】给【账户B】转账 500 元;
  2. 在 server2 上执行【账户B】给【账户A】转账 500 元;

以上两个转账行为同时执行,那么在执行到转账逻辑的第三步的时候,server1 会请求对【账户B】进行加锁,server2 会请求对【账户A】进行加锁,由于此时的【账户A】已由 server1进行锁定,【账户B】已由 server2进行锁定,此时就会产生死锁问题。

解决方案

在并发编程中,我们经常遇到三个问题原子性问题、可见性问题、有序性问题。见Java编程:并发编程的3个特性

通过上面的问题描述我们可以发现,在一个转账的过程中【账户A】、【账户B】的执行是有序的,但是在多个转账过程中【账户A】、【账户B】的执行是无序的。该问题类似并发编程的有序性问题,我们可以通过如下方式解决改问题:

在系统中对所有的账户进行排序:
【账户A】、【账户B】、【账户C】、【账户D】、【账户E】、【账户F】

在 server1 上执行【账户A】给【账户B】转账 500 元的逻辑:
1. 开启事务
2. 【账户A】的余额减去 500 元
3. 【账户B】的余额增加 500 元
4. 提交事务

在 server2 上执行【账户B】给【账户A】转账 500 元的逻辑:
1. 开启事务
2. 【账户A】的余额增加 500 元
3. 【账户B】的余额减去 500 元
4. 提交事务

将账户进行排序,保证转账过程中【账户A】始终先于【账户B】,这样就可以避免产生死锁问题。

解决方案有好多中,这里只提出我认为相对简单的一种方案,而且也是也在一线互联网公司广泛使用的一种方案。如有不正之处,欢迎批评指正。

你可能感兴趣的:(架构设计,Java编程)