排队下单生成自增排序号码的问题场景分析
今天和同事去地铁口下面的一个面店吃饭,大家桌面扫码后下单,发现自己会有一个取餐号,我的是39,同事的是40多。
这当然很容易想到,这个取餐号码是自增的,这种场景再熟悉不过了,在以往我们去饭店吃饭拿到的号因为是在柜台口头下单,服务员扫码支付,所以小票机器打出来的单号就很容易是看的出来自增的,这种序号就+1就行了,只有一个地方在点单,这样的逻辑当然没问题。
现在是桌面扫码了,就出现了我们开发最常见也最头疼的问题,并发问题。如果现在的最大取餐号是30,两人同时下单,或者同时支付,怎么保证两人取到的一个是31 一个是32 ,而不是都是31?
假设现在订单表如下:
表格内容如下:
这是 百变机兽之洛洛历险记 的主要角色和人物,我们采用这些人物角色带入他们在餐厅点单的场景。
1号洛洛和2号晶晶已经先下单,拿到游戏,开始体验,我们看到我们应该自动分配出 0001 给洛洛 , 0002 给晶晶,然后其余的机车族战士和猛兽族战士也纷纷下单获得技能卡,各自得到取餐号,现在最大取餐号是0012,下一个应该是0013。
现在金铁兽也要下单,该如何正确的分到这个0013的取餐号码呢?
方案1:
如果按最简单的开发思路,我们获取当前最大的 get_order_number ,将其取出来,然后 +1 ,之后再插入记录,那么程序设计如下:
可以看到我们的代码先是取出最大number,然后对其做00前缀的补齐,之后我们做一个拼接SQL,然后插入数据库,当然实际业务肯定比这个复杂很多,但我们这里简化了模型就用来讲解现在的自增序号问题。
这是最容易想到的实现方案,但是这个方案必然会有数据并发问题,假如实际情况是现在金铁兽和银铁兽都要下单,那他们都要买极光神风爪技能卡,会不会他们都分到了0013这个编号呢?
答案是显而易见的啊,非常有可能都分到这个号码,那么该如何避免这个问题呢?我们想到是不是可以设定加锁来控制写入呢,由此我们推导出方案2。
方案2
通过加锁(分布式锁 这里以redis为例),能够让代码顺序的执行,我们可以在代码执行之前设定一个变量来加锁判断一下,比如这样:
这里我们对这个当前点单的1号店铺进行设置key并加锁判断,只有拿到锁的人才能进行点单,才能走下一步,那么显然问题出现了,这样别人就得等了,这就是阻塞住了,等于程序上降低了应用可并发的数量。这样也还是有点问题,有没有更好的方法呢?这里我们介绍方案3.
方案3
我们直接使用 redis incr 方法能对一个数进行自增且返回这个自增后的结果,那不就是我们要的了吗?大道至简啊!