Oracle学习(十七)数据库锁在分布式系统里的应用(老板,乐观锁了解一下?~)
一、引言
随着业务量的增加,单机部署已经无法满足日常需求了,我们可能会把代码部署到多台服务器上去来进行服务的扩容,也就是负载均衡,那在这种场景下,怎么能实现锁的概念呢?
那么我们知道如果是一台主机部署的话,我们有很多方式可以实现锁的概念,比如利用synchronized关键字实现同步,或者使用reentrantLock可重入锁来在需要同步的场景,因为内存都是在一台机器上,可以很容易的实现对共享资源的锁定,但是多主机部署时,如果我们要锁定一个共享资源,显然这种方式无法满足我们的需求了,因为多主机部署时,我们每个主机要读取共享资源,可能会存在先后的顺序,那么在同时操纵一个数据时,就可能会存在一些很严重的问题了。
二、举个栗子
场景:2个用户同时对一个商品进行下单,商品库存只剩下1个,服务端会进行库存扣减。
期望结果:1个用户能下单成功,另一个用户提示库存不足
可能存在的问题:主机1和主机2同时去读取一个商品的库存,都发现是1,然后都进行了库存扣减,扣减完成后主机1进行了提交,库存被修改为0,这个时候主机2再进行提交,库存再次减1变为-1,那不就凉凉了么?
解决方案:数据库锁,分布式锁等(这篇文章只讨论数据库层面做的锁,分布式锁可以戳这里~)
三、代码实现
场景:商品库存为0 ,而此时进来了一个用户的购买线程
方式一 :update语句的原子性(行锁特性)实现乐观锁
乐观锁:乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测
-- 3、对商品id为1的商品进行库存扣减 UPDATE T_GDS_INFO SET GDS_STOCK = GDS_STOCK - 1 WHERE GDS_ID = 1 AND GDS_ID IN ( -- 2、这里要再封装一个结果集给外层的update语句进行条件过滤,否则一条语句直接操作同一张表,即进行select又进行update会报错 SELECT A.GDS_ID FROM ( -- 1、查询库存大于等于1的商品(带入需要扣减库存的商品id) SELECT GDS_ID FROM T_GDS_INFO WHERE GDS_ID = 1 AND GDS_STOCK >= 1 ) A );
上面的sql可能看着这比较绕,我们来一波简易的版本:
-- 1、当库存大于等于1时才进行库存扣减 UPDATE T_GDS_INFO SET GDS_STOCK = GDS_STOCK - 1 WHERE GDS_ID = 1 AND GDS_STOCK >= 1
方式二 :利用版本号实现乐观锁
思路:表里增加一个版本号的字段进行控制,每次成功更新版本号增加1,当需要进行更新操作时,先查询出版本号,然后再把查询出来的版本号作为update语句的条件带入,此时如果多个线程请求同一条数据时,查询到的版本号虽然一致,但是只有第一个提交的线程能更新成功。
-- 1、查询商品信息 得到版本号 SELECT * FROM T_GDS_INFO; -- 2、在语句里带入查询到的版本号,每次成功更新版本号+1 UPDATE T_GDS_INFO SET GDS_STOCK = GDS_STOCK - 1, VERSION = VERSION + 1 WHERE GDS_ID = 1 AND VERSION = 1 -- 3、如果同时2个订购都读取到版本号是1,然后进行库存扣减,那么第一条UPDATE语句会成功,把版本号增加变成2,而第二条UPDATE语句带入条件的版本号还是1,则会更新失败
PS:类似的还有用时间戳字段来进行版本控制的,思路类似,这里就不进行详细说明了