之前的文章《Java分布式锁实现》中列举了分布式锁的3种实现方式,分别是基于数据库实现,基于缓存实现和基于zookeeper实现。三种实现方式各有可取之处,本篇文章就详细讲解一下Java分布式锁之基于数据库的实现方式,也是最简单最易理解的实现方式。

首先,先来阐述下“锁”的概念,锁作为一种安全防御工具,既能上锁防止别人打开,又能让持有钥匙的人打开锁,这是锁的基本功能。那再来说一下“分布式锁”,分布式锁是在分布式系统(多个独立运行系统)内的锁,相对来说,这把锁的安全级别以及作用范围更大,所以从设计上就要考虑更多东西。

现在来说,怎么基于数据库实现这把分布式锁。其实说白了就是,把锁作为数据资源存入数据库,当持有这把锁的访问者来决定是否开锁。以下详细讲解了数据库的交易同步锁和交易重试补偿锁的实现。

一、数据库的设计

数据库锁表的表结构如下:

 
field type comment
ID bigint 主键
OUTER_SERIAL_NO varchar 流水号
CUST_NO char 客户号
SOURCE_CODE varchar 锁操作
THREAD_NO varchar 线程号
STATUS char 锁状态
REMARK varchar 备注
CREATED_AT timestamp 创建时间
UPDATED_AT timestamp 更新时间

作为锁的必要属性有5个:系统流水号,客户号,锁操作,线程号和锁状态,下面来解释一下每种属性

流水号:锁的具体指向,比如可以是产品,可以是交易流水号(后面会说到交易同步锁、交易补偿锁的使用方式)

客户号:客户的唯一标识

锁操作:客户的某种操作,比如客户取现操作,取现补偿重试操作

线程号:当前操作线程的线程号,比如取当前线程的uuid

锁状态:P处理中,F失败,Y成功

二、代码设计

代码的目录结构如下: 

Java分布式锁之数据库实现

主要贴一下锁操作的核心代码实现:

锁接口定义:DbLockManager.java

Java分布式锁之数据库实现
/**
 * 锁接口 <br>
 * 
 * @Author fugaoyang
 *
 */
public interface DbLockManager {
</span><span style="color: #008000">/**</span><span style="color: #008000">
 * 加锁
 </span><span style="color: #008000">*/</span>
<span style="color: #0000ff">boolean</span><span style="color: #000000"> lock(String outerSerialNo, String custNo, LockSource source);

</span><span style="color: #008000">/**</span><span style="color: #008000">
 * 解锁
 </span><span style="color: #008000">*/</span>
<span style="color: #0000ff">void</span><span style="color: #000000"> unLock(String outerSerialNo, String custNo, LockSource source, LockStatus targetStatus);

}

Java分布式锁之数据库实现
View Code

锁接口实现类:DbLockManagerImpl.java

/**
 * 
 * 数据库锁实现<br>
 * 
 * @author fugaoyang
 *
 */
@Service
public class DbLockManagerImpl implements DbLockManager {
</span><span style="color: #0000ff">private</span> <span style="color: #0000ff">final</span> Logger LOG = LoggerFactory.getLogger(<span style="color: #0000ff">this</span><span style="color: #000000">.getClass());

@Autowired
</span><span style="color: #0000ff">private</span><span style="color: #000000"> DbSyncLockMapper lockMapper;

@Transactional
</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">boolean</span><span style="color: #000000"> lock(String outerSerialNo, String custNo, LockSource source) {

    </span><span style="color: #0000ff">boolean</span> isLock = <span style="color: #0000ff">false</span><span style="color: #000000">;
    TradeSyncLock lock </span>= <span style="color: #0000ff">null</span><span style="color: #000000">;
    </span><span style="color: #0000ff">try</span><span style="color: #000000"> {
        lock </span>=<span style="color: #000000"> lockMapper.find(outerSerialNo, custNo, source.getCode());

        </span><span style="color: #0000ff">if</span> (<span style="color: #0000ff">null</span> ==<span style="color: #000000"> lock) {
            lock </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> TradeSyncLock();
            createLock(lock, outerSerialNo, custNo, source);

            </span><span style="color: #0000ff">int</span> num =<span style="color: #000000"> lockMapper.insert(lock);
            </span><span style="color: #0000ff">if</span> (num == 1<span style="color: #000000">) {
                isLock </span>= <span style="color: #0000ff">true</span><span style="color: #000000">;
            }

            LOG.info(ThreadLogUtils.getLogPrefix() </span>+ "加入锁,客户号[{}],锁类型[{}]"<span style="color: #000000">, custNo, source.getCode());
            </span><span style="color: #0000ff">return</span><span style="color: #000000"> isLock;
        }

        </span><span style="color: #008000">//</span><span style="color: #008000"> 根据交易类型进行加锁</span>
        isLock =<span style="color: #000000"> switchSynsLock(lock, source);
        LOG.info(ThreadLogUtils.getLogPrefix() </span>+ "更新锁,客户号[{}],锁类型[{}]"<span style="color: #000000">, custNo, source.getCode());

    } </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (Exception e) {
        LOG.error(ThreadLogUtils.getLogPrefix() </span>+ "交易加锁异常, 客户号:" +<span style="color: #000000"> custNo, e);
    }
    </span><span style="color: #0000ff">return</span><span style="color: #000000"> isLock;
}

@Transactional
</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> unLock(String outerSerialNo, String custNo, LockSource source, LockStatus targetStatus) {

    </span><span style="color: #0000ff">try</span><span style="color: #000000"> {
        TradeSyncLock lock </span>=<span style="color: #000000"> lockMapper.find(outerSerialNo, custNo, source.getCode());

        </span><span style="color: #0000ff">if</span> (<span style="color: #0000ff">null</span> !=<span style="color: #000000"> lock) {
            lockMapper.update(lock.getId(), targetStatus.getName(), LockStatus.P.getName(),
                    ThreadLogUtils.getCurrThreadUuid(), ThreadLogUtils.getCurrThreadUuid());
        }

        LOG.info(ThreadLogUtils.getLogPrefix() </span>+ "释放锁,客户号[{}],锁类型[{}]"<span style="color: #000000">, custNo, source.getCode());
    } </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (Exception e) {
        LOG.error(ThreadLogUtils.getLogPrefix() </span>+ "释放锁异常, 客户号:{}"<span style="color: #000000">, custNo, e);
    }
}

</span><span style="color: #008000">/**</span><span style="color: #008000">
 * 匹配加锁
 </span><span style="color: #008000">*/</span>
<span style="color: #0000ff">private</span> <span style="color: #0000ff">boolean</span><span style="color: #000000"> switchSynsLock(TradeSyncLock lock, LockSource source) {
    </span><span style="color: #0000ff">boolean</span> isLock = <span style="color: #0000ff">false</span><span style="color: #000000">;

    </span><span style="color: #0000ff">switch</span><span style="color: #000000"> (source) {
    </span><span style="color: #0000ff">case</span><span style="color: #000000"> WITHDRAW:
        ;
        isLock </span>=<span style="color: #000000"> tradeSynsLock(lock);
        </span><span style="color: #0000ff">break</span><span style="color: #000000">;
    </span><span style="color: #0000ff">case</span><span style="color: #000000"> WITHDRAW_RETRY:
        ;
        isLock </span>=<span style="color: #000000"> retrySynsLock(lock);
        </span><span style="color: #0000ff">break</span><span style="color: #000000">;
    </span><span style="color: #0000ff">default</span><span style="color: #000000">:
        ;
    }
    </span><span style="color: #0000ff">return</span><span style="color: #000000"> isLock;
}

</span><span style="color: #008000">/**</span><span style="color: #008000">
 * 交易同步锁
 </span><span style="color: #008000">*/</span>
<span style="color: #0000ff">private</span> <span style="color: #0000ff">boolean</span><span style="color: #000000"> tradeSynsLock(TradeSyncLock lock) {
    </span><span style="color: #008000">//</span><span style="color: #008000"> 处理中的不加锁,即不执行交易操作</span>
    <span style="color: #0000ff">if</span><span style="color: #000000"> (LockStatus.P.getName().equals(lock.getStatus())) {
        </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">false</span><span style="color: #000000">;
    }

    </span><span style="color: #0000ff">int</span> num =<span style="color: #000000"> lockMapper.update(lock.getId(), LockStatus.P.getName(), LockStatus.S.getName(),
            ThreadLogUtils.getCurrThreadUuid(), </span><span style="color: #0000ff">null</span><span style="color: #000000">);
    </span><span style="color: #0000ff">if</span> (num == 1<span style="color: #000000">) {
        </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">true</span><span style="color: #000000">;
    }
    </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">false</span><span style="color: #000000">;
}

</span><span style="color: #008000">/**</span><span style="color: #008000">
 * 补偿同步锁
 </span><span style="color: #008000">*/</span>
<span style="color: #0000ff">private</span> <span style="color: #0000ff">boolean</span><span style="color: #000000"> retrySynsLock(TradeSyncLock lock) {
    </span><span style="color: #008000">//</span><span style="color: #008000"> 处理中或处理完成的不加锁,即不执行补偿操作</span>
    <span style="color: #0000ff">if</span> (LockStatus.P.getName().equals(lock.getStatus()) ||<span style="color: #000000"> LockStatus.S.getName().equals(lock.getStatus())) {
        </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">false</span><span style="color: #000000">;
    }

    </span><span style="color: #0000ff">int</span> num =<span style="color: #000000"> lockMapper.update(lock.getId(), LockStatus.P.getName(), LockStatus.F.getName(),
            ThreadLogUtils.getCurrThreadUuid(), </span><span style="color: #0000ff">null</span><span style="color: #000000">);
    </span><span style="color: #0000ff">if</span> (num == 1<span style="color: #000000">) {
        </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">true</span><span style="color: #000000">;
    }
    </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">false</span><span style="color: #000000">;
}

</span><span style="color: #0000ff">private</span> <span style="color: #0000ff">void</span><span style="color: #000000"> createLock(TradeSyncLock lock, String outerSerialNo, String custNo, LockSource source) {
    lock.setOuterSerialNo(outerSerialNo);
    lock.setCustNo(custNo);
    lock.setSourceCode(source.getCode());
    lock.setThreadNo(ThreadLogUtils.getCurrThreadUuid());
    lock.setStatus(LockStatus.P.getName());
    lock.setRemark(source.getDesc());
}

}

View Code

相关文章:

  • 2021-10-12
  • 2021-12-11
  • 2022-01-25
  • 2021-06-06
  • 2021-12-22
  • 2022-12-23
  • 2021-05-29
  • 2021-11-30
猜你喜欢
  • 2021-07-31
  • 2022-12-23
  • 2021-04-27
  • 2022-12-23
  • 2022-12-23
  • 2022-01-05
  • 2021-09-19
相关资源
相似解决方案