Java分布式锁之数据库实现

<div id="cnblogs\_post\_body" class="blogpost-body"><p>之前的文章<a href="http://www.cnblogs.com/garryfu/p/7978611.html" target="\_blank">《Java分布式锁实现》</a>中列举了分布式锁的3种实现方式,分别是基于数据库实现,基于缓存实现和基于zookeeper实现。三种实现方式各有可取之处,本篇文章就详细讲解一下Java分布式锁之基于数据库的实现方式,也是最简单最易理解的实现方式。</p> <p>首先,先来阐述下“锁”的概念,锁作为一种安全防御工具,既能上锁防止别人打开,又能让持有钥匙的人打开锁,这是锁的基本功能。那再来说一下“分布式锁”,分布式锁是在分布式系统(多个独立运行系统)内的锁,相对来说,这把锁的安全级别以及作用范围更大,所以从设计上就要考虑更多东西。</p> <p>现在来说,怎么基于数据库实现这把分布式锁。其实说白了就是,把锁作为数据资源存入数据库,当持有这把锁的访问者来决定是否开锁。以下详细讲解了数据库的交易同步锁和交易重试补偿锁的实现。</p> <h4>一、数据库的设计</h4> <p>数据库锁表的表结构如下:</p> <table style="height: 249px; width: 668px" border="1" align="center"><caption>&nbsp;</caption> <thead> <tr style="background-color: #9cd2d3" dir="ltr"> <td><span style="font-family: 楷体; font-size: 12px">field</span></td> <td><span style="font-family: 楷体; font-size: 12px">type</span></td> <td><span style="font-family: 楷体; font-size: 12px">comment</span></td> </tr> </thead> <tbody> <tr> <td><span style="font-family: 楷体; font-size: 12px">ID</span></td> <td><span style="font-family: 楷体; font-size: 12px">bigint</span></td> <td><span style="font-family: 楷体; font-size: 12px">主键</span></td> </tr> <tr> <td><span style="font-family: 楷体; font-size: 12px">OUTER\_SERIAL\_NO</span></td> <td><span style="font-family: 楷体; font-size: 12px">varchar</span></td> <td><span style="font-family: 楷体; font-size: 12px">流水号</span></td> </tr> <tr> <td><span style="font-family: 楷体; font-size: 12px">CUST\_NO</span></td> <td><span style="font-family: 楷体; font-size: 12px">char</span></td> <td><span style="font-family: 楷体; font-size: 12px">客户号</span></td> </tr> <tr> <td><span style="font-family: 楷体; font-size: 12px">SOURCE\_CODE</span></td> <td><span style="font-family: 楷体; font-size: 12px">varchar</span></td> <td><span style="font-family: 楷体; font-size: 12px">锁操作</span></td> </tr> <tr> <td><span style="font-family: 楷体; font-size: 12px">THREAD\_NO</span></td> <td><span style="font-family: 楷体; font-size: 12px">varchar</span></td> <td><span style="font-family: 楷体; font-size: 12px">线程号</span></td> </tr> <tr> <td><span style="font-family: 楷体; font-size: 12px">STATUS</span></td> <td><span style="font-family: 楷体; font-size: 12px">char</span></td> <td><span style="font-family: 楷体; font-size: 12px">锁状态</span></td> </tr> <tr> <td><span style="font-family: 楷体; font-size: 12px">REMARK</span></td> <td><span style="font-family: 楷体; font-size: 12px">varchar</span></td> <td><span style="font-family: 楷体; font-size: 12px">备注</span></td> </tr> <tr> <td><span style="font-family: 楷体; font-size: 12px">CREATED\_AT</span></td> <td><span style="font-family: 楷体; font-size: 12px">timestamp</span></td> <td><span style="font-family: 楷体; font-size: 12px">创建时间</span></td> </tr> <tr> <td><span style="font-family: 楷体; font-size: 12px">UPDATED\_AT</span></td> <td><span style="font-family: 楷体; font-size: 12px">timestamp</span></td> <td><span style="font-family: 楷体; font-size: 12px">更新时间</span></td> </tr> </tbody> </table> <p>作为锁的必要属性有5个:系统流水号,客户号,锁操作,线程号和锁状态,下面来解释一下每种属性</p> <p>流水号:锁的具体指向,比如可以是产品,可以是交易流水号(后面会说到交易同步锁、交易补偿锁的使用方式)</p> <p>客户号:客户的唯一标识</p> <p>锁操作:客户的某种操作,比如客户取现操作,取现补偿重试操作</p> <p>线程号:当前操作线程的线程号,比如取当前线程的uuid</p> <p>锁状态:P处理中,F失败,Y成功</p> <h4>二、代码设计</h4> <p>代码的目录结构如下:&nbsp;</p> <p><img src="https://images2017.cnblogs.com/blog/1289521/201712/1289521-20171212153515566-1626398048.png" alt="" width="178" height="231"></p> <p>主要贴一下锁操作的核心代码实现:</p> <p>锁接口定义:DbLockManager.java</p> <div class="cnblogs\_code"><img id="code\_img\_closed\_f2ef6495-555e-4615-bbd9-f8bf37e75ba2" class="code\_img\_closed" src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt="" style="display: inline;"><img id="code\_img\_opened\_f2ef6495-555e-4615-bbd9-f8bf37e75ba2" class="code\_img\_opened" style="display: none;" src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt=""> <div id="cnblogs\_code\_open\_f2ef6495-555e-4615-bbd9-f8bf37e75ba2" class="cnblogs\_code\_hide" style="display: none;"><div class="cnblogs\_code\_toolbar"><span class="cnblogs\_code\_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="https://my.oschina.net//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div> <pre><span style="color: #008000">/\*\*</span><span style="color: #008000"> \* 锁接口 &lt;br&gt; \* \* @Author fugaoyang \* </span><span style="color: #008000">\*/</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">interface</span><span style="color: #000000"> DbLockManager {
1</span><span style="color: #008000">/**</span><span style="color: #008000"> 2 * 加锁 3 </span><span style="color: #008000">*/</span> 4<span style="color: #0000ff">boolean</span><span style="color: #000000"> lock(String outerSerialNo, String custNo, LockSource source); 5 6</span><span style="color: #008000">/**</span><span style="color: #008000"> 7 * 解锁 8 </span><span style="color: #008000">*/</span> 9<span style="color: #0000ff">void</span><span style="color: #000000"> unLock(String outerSerialNo, String custNo, LockSource source, LockStatus targetStatus);

}</span></pre>

<div class="cnblogs\_code\_toolbar"><span class="cnblogs\_code\_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="https://my.oschina.net//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div></div> <span class="cnblogs\_code\_collapse" style="display: inline;">View Code</span></div> <p>锁接口实现类:DbLockManagerImpl.java</p> <div class="cnblogs\_code"><img id="code\_img\_closed\_e79c666f-0f72-4273-add4-9382bc8c369c" class="code\_img\_closed" src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><img id="code\_img\_opened\_e79c666f-0f72-4273-add4-9382bc8c369c" class="code\_img\_opened" style="display: none" src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt=""> <div id="cnblogs\_code\_open\_e79c666f-0f72-4273-add4-9382bc8c369c" class="cnblogs\_code\_hide"> <pre><span style="color: #008000">/\*\*</span><span style="color: #008000"> \* \* 数据库锁实现&lt;br&gt; \* \* </span><span style="color: #808080">@author</span><span style="color: #008000"> fugaoyang \* </span><span style="color: #008000">\*/</span><span style="color: #000000"> @Service </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> DbLockManagerImpl <span style="color: #0000ff">implements</span><span style="color: #000000"> DbLockManager {
1</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()); 2 3@Autowired 4</span><span style="color: #0000ff">private</span><span style="color: #000000"> DbSyncLockMapper lockMapper; 5 6@Transactional 7</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">boolean</span><span style="color: #000000"> lock(String outerSerialNo, String custNo, LockSource source) { 8 9 </span><span style="color: #0000ff">boolean</span> isLock = <span style="color: #0000ff">false</span><span style="color: #000000">; 10 TradeSyncLock lock </span>= <span style="color: #0000ff">null</span><span style="color: #000000">; 11 </span><span style="color: #0000ff">try</span><span style="color: #000000"> { 12 lock </span>=<span style="color: #000000"> lockMapper.find(outerSerialNo, custNo, source.getCode()); 13 14 </span><span style="color: #0000ff">if</span> (<span style="color: #0000ff">null</span> ==<span style="color: #000000"> lock) { 15 lock </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> TradeSyncLock(); 16 createLock(lock, outerSerialNo, custNo, source); 17 18 </span><span style="color: #0000ff">int</span> num =<span style="color: #000000"> lockMapper.insert(lock); 19 </span><span style="color: #0000ff">if</span> (num == 1<span style="color: #000000">) { 20 isLock </span>= <span style="color: #0000ff">true</span><span style="color: #000000">; 21 } 22 23 LOG.info(ThreadLogUtils.getLogPrefix() </span>+ "加入锁,客户号[{}],锁类型[{}]"<span style="color: #000000">, custNo, source.getCode()); 24 </span><span style="color: #0000ff">return</span><span style="color: #000000"> isLock; 25 } 26 27 </span><span style="color: #008000">//</span><span style="color: #008000"> 根据交易类型进行加锁</span> 28 isLock =<span style="color: #000000"> switchSynsLock(lock, source); 29 LOG.info(ThreadLogUtils.getLogPrefix() </span>+ "更新锁,客户号[{}],锁类型[{}]"<span style="color: #000000">, custNo, source.getCode()); 30 31 } </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (Exception e) { 32 LOG.error(ThreadLogUtils.getLogPrefix() </span>+ "交易加锁异常, 客户号:" +<span style="color: #000000"> custNo, e); 33 } 34 </span><span style="color: #0000ff">return</span><span style="color: #000000"> isLock; 35} 36 37@Transactional 38</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) { 39 40 </span><span style="color: #0000ff">try</span><span style="color: #000000"> { 41 TradeSyncLock lock </span>=<span style="color: #000000"> lockMapper.find(outerSerialNo, custNo, source.getCode()); 42 43 </span><span style="color: #0000ff">if</span> (<span style="color: #0000ff">null</span> !=<span style="color: #000000"> lock) { 44 lockMapper.update(lock.getId(), targetStatus.getName(), LockStatus.P.getName(), 45 ThreadLogUtils.getCurrThreadUuid(), ThreadLogUtils.getCurrThreadUuid()); 46 } 47 48 LOG.info(ThreadLogUtils.getLogPrefix() </span>+ "释放锁,客户号[{}],锁类型[{}]"<span style="color: #000000">, custNo, source.getCode()); 49 } </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (Exception e) { 50 LOG.error(ThreadLogUtils.getLogPrefix() </span>+ "释放锁异常, 客户号:{}"<span style="color: #000000">, custNo, e); 51 } 52} 53 54</span><span style="color: #008000">/**</span><span style="color: #008000"> 55 * 匹配加锁 56 </span><span style="color: #008000">*/</span> 57<span style="color: #0000ff">private</span> <span style="color: #0000ff">boolean</span><span style="color: #000000"> switchSynsLock(TradeSyncLock lock, LockSource source) { 58 </span><span style="color: #0000ff">boolean</span> isLock = <span style="color: #0000ff">false</span><span style="color: #000000">; 59 60 </span><span style="color: #0000ff">switch</span><span style="color: #000000"> (source) { 61 </span><span style="color: #0000ff">case</span><span style="color: #000000"> WITHDRAW: 62 ; 63 isLock </span>=<span style="color: #000000"> tradeSynsLock(lock); 64 </span><span style="color: #0000ff">break</span><span style="color: #000000">; 65 </span><span style="color: #0000ff">case</span><span style="color: #000000"> WITHDRAW_RETRY: 66 ; 67 isLock </span>=<span style="color: #000000"> retrySynsLock(lock); 68 </span><span style="color: #0000ff">break</span><span style="color: #000000">; 69 </span><span style="color: #0000ff">default</span><span style="color: #000000">: 70 ; 71 } 72 </span><span style="color: #0000ff">return</span><span style="color: #000000"> isLock; 73} 74 75</span><span style="color: #008000">/**</span><span style="color: #008000"> 76 * 交易同步锁 77 </span><span style="color: #008000">*/</span> 78<span style="color: #0000ff">private</span> <span style="color: #0000ff">boolean</span><span style="color: #000000"> tradeSynsLock(TradeSyncLock lock) { 79 </span><span style="color: #008000">//</span><span style="color: #008000"> 处理中的不加锁,即不执行交易操作</span> 80 <span style="color: #0000ff">if</span><span style="color: #000000"> (LockStatus.P.getName().equals(lock.getStatus())) { 81 </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">false</span><span style="color: #000000">; 82 } 83 84 </span><span style="color: #0000ff">int</span> num =<span style="color: #000000"> lockMapper.update(lock.getId(), LockStatus.P.getName(), LockStatus.S.getName(), 85 ThreadLogUtils.getCurrThreadUuid(), </span><span style="color: #0000ff">null</span><span style="color: #000000">); 86 </span><span style="color: #0000ff">if</span> (num == 1<span style="color: #000000">) { 87 </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">true</span><span style="color: #000000">; 88 } 89 </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">false</span><span style="color: #000000">; 90} 91 92</span><span style="color: #008000">/**</span><span style="color: #008000"> 93 * 补偿同步锁 94 </span><span style="color: #008000">*/</span> 95<span style="color: #0000ff">private</span> <span style="color: #0000ff">boolean</span><span style="color: #000000"> retrySynsLock(TradeSyncLock lock) { 96 </span><span style="color: #008000">//</span><span style="color: #008000"> 处理中或处理完成的不加锁,即不执行补偿操作</span> 97 <span style="color: #0000ff">if</span> (LockStatus.P.getName().equals(lock.getStatus()) ||<span style="color: #000000"> LockStatus.S.getName().equals(lock.getStatus())) { 98 </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">false</span><span style="color: #000000">; 99 } 100 101 </span><span style="color: #0000ff">int</span> num =<span style="color: #000000"> lockMapper.update(lock.getId(), LockStatus.P.getName(), LockStatus.F.getName(), 102 ThreadLogUtils.getCurrThreadUuid(), </span><span style="color: #0000ff">null</span><span style="color: #000000">); 103 </span><span style="color: #0000ff">if</span> (num == 1<span style="color: #000000">) { 104 </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">true</span><span style="color: #000000">; 105 } 106 </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">false</span><span style="color: #000000">; 107} 108 109</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) { 110 lock.setOuterSerialNo(outerSerialNo); 111 lock.setCustNo(custNo); 112 lock.setSourceCode(source.getCode()); 113 lock.setThreadNo(ThreadLogUtils.getCurrThreadUuid()); 114 lock.setStatus(LockStatus.P.getName()); 115 lock.setRemark(source.getDesc()); 116}

}</span></pre>

</div> <span class="cnblogs\_code\_collapse">View Code</span></div> <p>获取当前线程号以及打印uuid工具类ThreadLogUtils.Java</p> <div class="cnblogs\_code"><img id="code\_img\_closed\_25ad7b32-da05-4059-b8e6-077baabc33d8" class="code\_img\_closed" src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><img id="code\_img\_opened\_25ad7b32-da05-4059-b8e6-077baabc33d8" class="code\_img\_opened" style="display: none" src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt=""> <div id="cnblogs\_code\_open\_25ad7b32-da05-4059-b8e6-077baabc33d8" class="cnblogs\_code\_hide"> <pre><span style="color: #008000">/\*\*</span><span style="color: #008000"> \* \* 线程处理&lt;br&gt; \* \* </span><span style="color: #808080">@author</span><span style="color: #008000"> fugaoyang \* </span><span style="color: #008000">\*/</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span><span style="color: #000000"> ThreadLogUtils {
1</span><span style="color: #0000ff">private</span> <span style="color: #0000ff">static</span> ThreadLogUtils instance = <span style="color: #0000ff">null</span><span style="color: #000000">; 2 3</span><span style="color: #0000ff">private</span><span style="color: #000000"> ThreadLogUtils() { 4 setInstance(</span><span style="color: #0000ff">this</span><span style="color: #000000">); 5} 6 7</span><span style="color: #008000">//</span><span style="color: #008000"> 初始化标志</span> 8<span style="color: #0000ff">private</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">final</span> Object __noop = <span style="color: #0000ff">new</span><span style="color: #000000"> Object(); 9</span><span style="color: #0000ff">private</span> <span style="color: #0000ff">static</span> ThreadLocal&lt;Object&gt; __flag = <span style="color: #0000ff">new</span> InheritableThreadLocal&lt;Object&gt;<span style="color: #000000">() { 10 @Override 11 </span><span style="color: #0000ff">protected</span><span style="color: #000000"> Object initialValue() { 12 </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">null</span><span style="color: #000000">; 13 } 14}; 15 16</span><span style="color: #008000">//</span><span style="color: #008000"> 当前线程的UUID信息,主要用于打印日志;</span> 17<span style="color: #0000ff">private</span> <span style="color: #0000ff">static</span> ThreadLocal&lt;String&gt; currLogUuid = <span style="color: #0000ff">new</span> InheritableThreadLocal&lt;String&gt;<span style="color: #000000">() { 18 @Override 19 </span><span style="color: #0000ff">protected</span><span style="color: #000000"> String initialValue() { 20 </span><span style="color: #0000ff">return</span> UUID.randomUUID().toString()<span style="color: #008000">/*</span><span style="color: #008000"> .toUpperCase() </span><span style="color: #008000">*/</span><span style="color: #000000">; 21 } 22}; 23 24</span><span style="color: #0000ff">private</span> <span style="color: #0000ff">static</span> ThreadLocal&lt;String&gt; currThreadUuid = <span style="color: #0000ff">new</span> ThreadLocal&lt;String&gt;<span style="color: #000000">() { 25 @Override 26 </span><span style="color: #0000ff">protected</span><span style="color: #000000"> String initialValue() { 27 </span><span style="color: #0000ff">return</span><span style="color: #000000"> UUIDGenerator.getUuid(); 28 } 29}; 30 31</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span><span style="color: #000000"> clear(Boolean isNew) { 32 </span><span style="color: #0000ff">if</span><span style="color: #000000"> (isNew) { 33 34 currLogUuid.remove(); 35 36 __flag.remove(); 37 38 currThreadUuid.remove(); 39 40 } 41} 42 43</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span><span style="color: #000000"> String getCurrLogUuid() { 44 </span><span style="color: #0000ff">if</span> (!<span style="color: #000000">isInitialized()) { 45 </span><span style="color: #0000ff">throw</span> <span style="color: #0000ff">new</span> IllegalStateException("TLS未初始化"<span style="color: #000000">); 46 } 47 48 </span><span style="color: #0000ff">return</span><span style="color: #000000"> currLogUuid.get(); 49} 50 51</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span><span style="color: #000000"> String getCurrThreadUuid() { 52 </span><span style="color: #0000ff">return</span><span style="color: #000000"> currThreadUuid.get(); 53} 54 55</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span><span style="color: #000000"> clearCurrThreadUuid() { 56 currThreadUuid.remove(); 57} 58 59</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span><span style="color: #000000"> String getLogPrefix() { 60 </span><span style="color: #0000ff">if</span> (!<span style="color: #000000">isInitialized()) { 61 </span><span style="color: #0000ff">return</span> ""<span style="color: #000000">; 62 } 63 64 </span><span style="color: #0000ff">return</span> "&lt;uuid=" + getCurrLogUuid() + "&gt;"<span style="color: #000000">; 65} 66 67</span><span style="color: #0000ff">private</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">boolean</span><span style="color: #000000"> isInitialized() { 68 </span><span style="color: #0000ff">return</span> __flag.get() != <span style="color: #0000ff">null</span><span style="color: #000000">; 69} 70 71</span><span style="color: #008000">/**</span><span style="color: #008000"> 72 * 初始化上下文,如果已经初始化则返回false,否则返回true&lt;br/&gt; 73 * 74 * </span><span style="color: #808080">@return</span> 75 <span style="color: #008000">*/</span> 76<span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">boolean</span><span style="color: #000000"> initialize() { 77 </span><span style="color: #0000ff">if</span><span style="color: #000000"> (isInitialized()) { 78 </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">false</span><span style="color: #000000">; 79 } 80 81 __flag.set(__noop); 82 </span><span style="color: #0000ff">return</span> <span style="color: #0000ff">true</span><span style="color: #000000">; 83} 84 85</span><span style="color: #0000ff">private</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span><span style="color: #000000"> setInstance(ThreadLogUtils instance) { 86 ThreadLogUtils.instance </span>=<span style="color: #000000"> instance; 87} 88 89</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span><span style="color: #000000"> ThreadLogUtils getInstance() { 90 </span><span style="color: #0000ff">return</span><span style="color: #000000"> instance; 91}

}</span></pre>

</div> <span class="cnblogs\_code\_collapse">View Code</span></div> <p>两种锁的实现的大致思路如下:</p> <p><strong>1.交易同步锁</strong></p> <p>当一个客户来取现,第一次进入时,会插入一条当前线程,状态是P,操作是取现的锁,取现成功后根据当前线程号会更新成功;</p> <p>当一个客户同时多个取现操作时,只有一个取现操作会加锁成功,其它会加锁失败;</p> <p>当一个客户已经在取现中,这时数据库已经有一条状态P的锁,该客户同时又做了取现,这个取现动作会尝试加锁而退出;</p> <p><strong>2.交易重试补偿锁</strong></p> <p>1.当一个客户取现加锁成功,因调用第三方支付接口超时时,后台会对该笔交易重新发起重试打款操作,这时会新加一条当前交易流水号,当前线程号,状态是P,操作是取现重试的锁,重试的支付结果是成功的话,更新该条锁数据为Y状态,否则更新该条数据为F状态;</p> <p>2.当重试支付失败后,再去重试打款时,发现锁的状态是F,这时把F更新为P,继续重试,根据重试结果更新锁状态。</p> <p>上面实现的是一个最基本的数据库分布式锁,满足的并发量也是基于数据库所能扛得住的,性能基本可以满足普通的交易量。</p> <p><strong>后续可以优化的部分:</strong></p> <p>1.当一个用户同时多次获取lock时,因为目前是用的乐观锁,只会有一个加锁成功,可以优化成加入while(true)循环获取lock,当失败次数到达指定次数时退出,当前的操作结束。</p> <p>2.当锁表数据量随着时间增大时,可以考虑按用户对锁表进行分表分库,以减小数据库方面的压力。</p> <p>3.对锁的操作可以抽象出来,作为抽象实现,比如具体的取现操作只关心取现这个业务实现。</p> <p>&nbsp;</p> <p>因为时间有限,写的比较仓促,希望大家有问题可以提出,相互探讨~~</p> <p>完整示例代码后续会更新到github。</p> <p>&nbsp;</p> <style>p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco }</style> <style>p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco }</style> <style>p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco }</style></div>
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Redis分布式锁的正确实现方式

前言分布式锁一般有三种实现方式:1.数据库乐观锁;2.基于Redis的分布式锁;3.基于ZooKeeper的分布式锁。本篇博客将介绍第二种方式,基于Redis实现分布式锁。虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁。

Nginx + lua +[memcached,redis]

精品案例1、Nginxluamemcached,redis实现网站灰度发布2、分库分表/基于Leaf组件实现的全球唯一ID(非UUID)3、Redis独立数据监控,实现订单超时操作/MQ死信操作SelectPollEpollReactor模型4、分布式任务调试Quartz应用

Java分布式锁之数据库实现 - HelloWorld