<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">还是秒杀.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">秒杀一般有几个场景</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">1.电商秒杀商品</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">2.抢红包</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">3.抢票</span></span><br />
<br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">假设一个场景如下:</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 某电商公司搞活动,一折秒杀,推出几种秒杀的商品,每种商品1000个,预计100w人抢购</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">要求:</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 不能超卖.绝对不可以卖多了.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 数据库要扣减库存,并且记录订单明细.</span></span><br />
<br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">难点分析</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">1.不能阻塞.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">海量的请求就像血栓一样,遍走周身,一旦遇到瓶颈,就会堵塞整个血管.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">所以一定要让海量的用户请求,尽快结束.</span></span><br />
<br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">2.数据库单行更新</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">大量的 update 库存表 set 剩余数量=剩余数量-1 where 商品ID=?</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">这种单行更新,有行锁,会阻塞其他事务,占用宝贵的数据库处理能力.</span></span><br />
<br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">针对这种场景,综合了很多资料</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">我觉得可以尝试几个关于秒杀的优化.</span></span><br />
<br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"><img src="/attachment/201509/15/29254281_14423216710949.png" width="599" height="781" alt="" /></span></span><br />
<br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">1.Web服务器集群层,卸载流量</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 海量的用户秒杀请求,本质上是一个排序,先到先得.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 但是如此之多的请求,完全响应,难度又很大.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 所以在Web服务器集群,可以考虑卸载流量.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 比如每十个请求,随机抛弃九个,只放行一个请求到后续处理环节.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 把秒杀的排序模式,变为随机抽奖的模式.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> </span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">2.Web服务器集群层,缩小锁范围.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 每次秒杀活动开始之前.先计算活动推出的商品数量,然后分配一个限额到每个Web服务器.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 比如一个活动推出秒杀商品</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 电视,手机,衣服各1000件,那么每台服务器的限额就是125件.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 将这个限额写入ZooKeeper,Web服务器监听到限额的变化,就会重新初始化各自的商品剩余数量.</span></span><br />
<br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">模拟示例:</span></span><br />
<div id="codeText" class="codeText" style="color:#003399;font-family:'Courier New';font-size:16px;">
<ol style="margin:0 1px 0 0;padding:5px 0pt;" start="1" class="dp-css none_number">
<li>
<span style="color:#000000;"><span style="color:#0000FF;"> <span style="font-family:Courier New;"> </span><span style="font-family:Courier New;">private</span></span> <span style="color:#0000FF;font-family:'Courier New';">static</span> <span style="color:#FF0000;font-family:'Courier New';">ConcurrentHashMap</span><span style="color:#0000CC;font-family:'Courier New';"><</span><span style="color:#FF0000;font-family:'Courier New';">String</span><span style="color:#0000CC;font-family:'Courier New';">,</span><span style="color:#FF0000;font-family:'Courier New';">Integer</span><span style="color:#0000CC;font-family:'Courier New';">></span> <span style="color:#FF0000;font-family:'Courier New';">map</span><span style="color:#0000CC;font-family:'Courier New';">=</span><span style="color:#0000FF;font-family:'Courier New';">new</span> <span style="color:#FF0000;font-family:'Courier New';">ConcurrentHashMap</span><span style="color:#0000CC;font-family:'Courier New';"><</span><span style="color:#FF0000;font-family:'Courier New';">String</span><span style="color:#0000CC;font-family:'Courier New';">,</span> <span style="color:#FF0000;font-family:'Courier New';">Integer</span><span style="color:#0000CC;font-family:'Courier New';">></span><span style="color:#0000CC;font-family:'Courier New';">(</span><span style="color:#0000CC;font-family:'Courier New';">)</span><span style="color:#0000CC;font-family:'Courier New';">;</span><br />
</span>
</li>
<li>
<span style="font-family:Courier New;"> </span><br />
</li>
<li>
<span style="font-family:Courier New;"> </span><span style="color:#0000FF;font-family:'Courier New';">private</span> <span style="color:#0000FF;font-family:'Courier New';">void</span><span style="font-family:Courier New;"> zooKeeperHandle</span><span style="color:#0000CC;font-family:'Courier New';">(</span><span style="color:#0000CC;font-family:'Courier New';">)</span><span style="color:#0000CC;font-family:'Courier New';">{</span><br />
</li>
<li>
<span style="font-family:Courier New;"> </span><span style="color:#FF9900;font-family:'Courier New';">//将ZooKeeper的变化,初始化到Web服务器全局容器</span><br />
</li>
<li>
<span style="font-family:Courier New;"> </span><span style="color:#FF0000;font-family:'Courier New';">map</span><span style="color:#0000CC;font-family:'Courier New';">.</span><span style="color:#FF0000;font-family:'Courier New';">put</span><span style="color:#0000CC;font-family:'Courier New';">(</span><span style="color:#FF00FF;font-family:'Courier New';">"电视机"</span><span style="color:#0000CC;font-family:'Courier New';">,</span><span style="font-family:Courier New;"> 125</span><span style="color:#0000CC;font-family:'Courier New';">)</span><span style="color:#0000CC;font-family:'Courier New';">;</span><br />
</li>
<li>
<span style="font-family:Courier New;"> </span><span style="color:#FF0000;font-family:'Courier New';">map</span><span style="color:#0000CC;font-family:'Courier New';">.</span><span style="color:#FF0000;font-family:'Courier New';">put</span><span style="color:#0000CC;font-family:'Courier New';">(</span><span style="color:#FF00FF;font-family:'Courier New';">"手机"</span><span style="color:#0000CC;font-family:'Courier New';">,</span><span style="font-family:Courier New;"> 125</span><span style="color:#0000CC;font-family:'Courier New';">)</span><span style="color:#0000CC;font-family:'Courier New';">;</span><br />
</li>
<li>
<span style="font-family:Courier New;"> </span><span style="color:#FF0000;font-family:'Courier New';">map</span><span style="color:#0000CC;font-family:'Courier New';">.</span><span style="color:#FF0000;font-family:'Courier New';">put</span><span style="color:#0000CC;font-family:'Courier New';">(</span><span style="color:#FF00FF;font-family:'Courier New';">"衣服"</span><span style="color:#0000CC;font-family:'Courier New';">,</span><span style="font-family:Courier New;"> 125</span><span style="color:#0000CC;font-family:'Courier New';">)</span><span style="color:#0000CC;font-family:'Courier New';">;</span><br />
</li>
<li>
<span style="font-family:Courier New;"> </span><span style="color:#0000CC;font-family:'Courier New';">}</span>
</li>
</ol>
</div>
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 假设用户请求秒杀电视机,它只是锁了</span></span><span style="color:#E53333;font-family:'Courier New';font-size:16px;">该Web服务器</span><span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">电视机的数量。(该Web服务器手机和衣服还可以</span></span><span style="color:#003399;font-family:'Courier New';font-size:16px;line-height:24px;white-space:normal;">继续</span><span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">并发处理,当然其他的Web服务器也在同时处理电视机的秒杀请求)</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 这样缩小了锁定的范围,增加了系统处理的吞吐量.</span></span><br />
<br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 如果这个剩余数量大于零,则将用户ID放入电视机购买队列,然后告知用户秒杀成功</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 如果这个剩余数量等于零,则告知用户秒杀失败.即便别的Web服务器还有电视机的剩余配额.</span></span><br />
<br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">3.ZooKeeper层,ZooKeeper变更库存信息</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 假设活动期间,需要修改库存信息。</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 两种可能,</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 第一种,该商品已经卖了500件,电商不想继续卖了.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 第二种,从仓库中又找到了一些积压库存..</span></span><br />
<br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 两种情况,都直接修改ZooKeeper中相应商品的配额.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> Web服务器会监听变化,并重新初始化全局容器.</span></span><br />
<br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">4.消息队列层,多消费者处理</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 消费者主要是从队列获取购买请求,发送至数据库</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 扣减数据库库存</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 写订单明细记录</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"><img src="/attachment/201509/15/29254281_1442324900V727.png" width="659" height="379" alt="" /></span></span><br />
<br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">5.数据库层,使用存储过程代替JDBC调用</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 由于使用了多消费者处理同一队列,增加吞吐量,避免队列堆积过大.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 但是多消费者,必然导致数据库出现单行更新问题.</span></span><br />
<br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 单行更新问题就是多个线程,并发修改同一条记录,导致事务相互阻塞.浪费了数据库宝贵的处理能力.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 考查下图.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 假设消费者到数据库的网络是1毫秒</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 那么相对于存储过程,使用JDBC的方式,每个事务将至少多持有行锁2毫秒.</span></span><br />
<br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 所以进一步优化,可以考虑用存储过程代替JDBC</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"><img src="/attachment/201509/15/29254281_1442325777bRvL.png" width="566" height="317" alt="" /></span></span><br />
<br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;">6.数据库层,库存单行更新,增加多个槽位.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 单行更新场景</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"><img src="/attachment/201509/15/29254281_1442326510Z6gw.png" width="531" height="266" alt="" /></span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 增加槽位的表结构</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"><img src="/attachment/201509/15/29254281_1442326800YvXX.png" width="244" height="307" alt="" /></span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> </span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 使用槽位分散行锁</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 每种商品的库存,由4个槽位组成.</span></span><br />
<span style="color:#003399;font-family:'Courier New';"><span style="font-size:16px;"> 事务开始,首先找到剩余数量最多的那个商品槽位.<br />
然后扣减该槽位的库存.<br />
这样一个行锁,可以变为4个行锁,系统吞吐量增加了4倍.<br />
(其实如果update的影响行数为0,表示该槽位已经没有库存.可以重复执行这个过程,再另选一个槽位)</span></span><br />
<div id="codeText" class="codeText">
<ol style="color:#003399;font-family:'Courier New';font-size:16px;margin:0px 1px 0px 0px;padding:5px 0pt;" start="1" class="dp-css none_number">
<li>
<span style="color:#000000;"><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">/</span><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">/开始事务</span><br />
</span>
</li>
<li>
<span style="color:#0000FF;font-family:'Courier New';font-size:18px;">select</span><span style="font-family:'Courier New';font-size:18px;"> 商品</span><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">,</span><span style="font-family:'Courier New';font-size:18px;">剩余数量</span><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">,</span><span style="color:#0000FF;font-family:'Courier New';font-size:18px;">@</span><span style="color:#008080;font-family:'Courier New';font-size:18px;">槽位</span><span style="color:#008080;font-family:'Courier New';font-size:18px;">:</span><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">=</span><span style="font-family:'Courier New';font-size:18px;">Slot </span><span style="color:#0000FF;font-family:'Courier New';font-size:18px;">from</span><span style="font-family:'Courier New';font-size:18px;"> 库存表 </span><br />
</li>
<li>
<span style="color:#0000FF;font-family:'Courier New';font-size:18px;">where</span> <br />
</li>
<li>
<span style="font-family:'Courier New';font-size:18px;">商品</span><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">=</span><span style="color:#FF00FF;font-family:'Courier New';font-size:18px;">'电视机'</span> <span style="color:#FF0000;font-family:'Courier New';font-size:18px;">and</span><span style="font-family:'Courier New';font-size:18px;"> 剩余数量</span><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">></span><span style="font-family:'Courier New';font-size:18px;">0 </span><span style="color:#FF0000;font-family:'Courier New';font-size:18px;">and</span> <br />
</li>
<li>
<span style="font-family:'Courier New';font-size:18px;">剩余数量</span><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">=</span><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">(</span><span style="color:#0000FF;font-family:'Courier New';font-size:18px;">select</span> <span style="color:#0000FF;font-family:'Courier New';font-size:18px;">max</span><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">(</span><span style="font-family:'Courier New';font-size:18px;">剩余数量</span><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">)</span> <span style="color:#0000FF;font-family:'Courier New';font-size:18px;">from</span><span style="font-family:'Courier New';font-size:18px;"> 库存表 </span><span style="color:#0000FF;font-family:'Courier New';font-size:18px;">where</span><span style="font-family:'Courier New';font-size:18px;"> 商品</span><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">=</span><span style="color:#FF00FF;font-family:'Courier New';font-size:18px;">'电视机'</span><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">)</span><br />
</li>
<li>
<span style="font-family:'Courier New';font-size:18px;">limit 1</span><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">;</span><br />
</li>
<li>
<br />
</li>
<li>
<span style="color:#0000FF;font-family:'Courier New';font-size:18px;">update</span><span style="font-family:'Courier New';font-size:18px;"> 库存表 </span><span style="color:#0000FF;font-family:'Courier New';font-size:18px;">set</span><span style="font-family:'Courier New';font-size:18px;"> 剩余数量</span><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">=</span><span style="font-family:'Courier New';font-size:18px;">剩余数量-1 </span><span style="color:#0000FF;font-family:'Courier New';font-size:18px;">where</span><span style="font-family:'Courier New';font-size:18px;"> 剩余数量</span><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">></span><span style="font-family:'Courier New';font-size:18px;">0 </span><span style="color:#FF0000;font-family:'Courier New';font-size:18px;">and</span><span style="font-family:'Courier New';font-size:18px;"> 商品</span><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">=</span><span style="color:#FF00FF;font-family:'Courier New';font-size:18px;">'电视机'</span> <span style="color:#FF0000;font-family:'Courier New';font-size:18px;">and</span><span style="font-family:'Courier New';font-size:18px;"> Slot</span><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">=</span><span style="color:#0000FF;font-family:'Courier New';font-size:18px;">@</span><span style="color:#008080;font-family:'Courier New';font-size:18px;">槽位</span><span style="color:#0000CC;font-family:'Courier New';font-size:18px;">;</span>
</li>
</ol>
<div>
<span style="color:#0000cc;font-family:'Courier New';"><span style="font-size:18px;line-height:23.4px;"><span style="color:#0000CC;letter-spacing:0.1px;white-space:normal;font-family:'Courier New';font-size:18px;background-color:#FFFFFF;"> /</span><span style="color:#0000CC;letter-spacing:0.1px;white-space:normal;font-family:'Courier New';font-size:18px;background-color:#FFFFFF;">/如果update的影响行数不为0,写订单明细表<br />
commit</span>;<br />
</span></span>
</div>
</div>
<span style="color:#003399;font-family:'Courier New';font-size:16px;line-height:24px;white-space:normal;"><br />
7.数据库层,冷热商品分开.<br />
某些热点商品,可以单独放置在一个数据库处理<br />
比如苹果手机新品,特卖打折 10w部<br />
这种注定会热的商品,应该使用单独的数据库处理<br />
避免和普通商品竞争,堵塞本次活动其他商品的处理.<br />
<br />
参考:<br />
http://blog.itpub.net/29254281/viewspace-1783043/<br />
</span><br />
<span style="color:#003399;font-family:'Courier New';font-size:16px;line-height:26px;white-space:normal;background-color:#FFFFFF;">http://jiagou.baijia.baidu.com/article/108134?qq-pf-to=pcqq.group</span><br style="color:#003399;font-family:'Courier New';font-size:16px;line-height:26px;white-space:normal;background-color:#FFFFFF;" />
<br />
<span style="color:#003399;font-family:'Courier New';font-size:16px;line-height:26px;white-space:normal;background-color:#FFFFFF;">http://www.infoq.com/cn/presentations/seckill-solution-based-sql</span><br style="color:#003399;font-family:'Courier New';font-size:16px;line-height:26px;white-space:normal;background-color:#FFFFFF;" />
<br />
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/30723338/viewspace-1962815/
,如需转载,请注明出处,否则将追究法律责任。