问题背景
在分布式系统中,应用会进行多实例部署,不同势力有各自的JVM,被负载均衡到不同实例上的用户请求通过JVM的锁机制无法互斥。例如,电商购物环节的减库存操作,有可能导致用户同时点击导致库存不一致的问题。
解决方案
- 使用MySQL乐观锁
- 分布式锁(暂不介绍)
MySQL乐观锁具体实现
相对于悲观锁,它只有在数据提交的时候进行冲突校验,如果产生数据冲突,返回错误信息,进行相应处理。
- 在数据库表中添加version版本号字段
CREATE TABLE `goods` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(64) NOT NULL DEFAULT '',
`remaining_number` int(11) NOT NULL,
`version` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
- 读取数据包括版本号
- 数据更新时对这条数据的version + 1
<update id="updateGoodCAS" parameterType="com.ztl.domain.Goods">
<![CDATA[
update goods
set `name`=#{name},
remaining_number=#{remainingNumber},
version=version+1
where id=#{id} and version=#{version}
]]>
</update>
- 判断数据库中要更新时的版本号和第一次取到数据的版本号是否一致,一致的话,说明更新,否则认为数据过期,返回错误信息到页面上。
@Override
public Boolean updateGoodCAS(Integer id, Integer decreaseNum) {
Goods good = goodsMapper.selectGoodById(id);
System.out.println(good);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
good.setRemainingNumber(good.getRemainingNumber() - decreaseNum);
int result = goodsMapper.updateGoodCAS(good);
System.out.println(result == 1 ? "success" : "fail");
return result == 1;
}
参考
https://juejin.im/post/5afec34df265da0b745269db