时间:2021-07-01 10:21:17 帮助过:39人阅读
如图所示,如果更新操作如第一个图中一样顺序执行,则数据的版本号会依次递增,不会有冲突出现。但是像第二个图中一样,不同的用户操作读取到数据的同一个版本,再分别对数据进行更新操作,则用户的A的更新操作可以成功,用户B更新时,数据的版本号已经变化,所以更新失败。
我们对某个商品减库存时,具体操作分为以下3个步骤:
查询出商品的具体信息
根据具体的减库存数量,生成相应的更新对象
修改商品的库存数量
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;
* 商品名字
*/
private String name;
/**
* 库存数量
*/
private Integer remainingNumber;
/**
* 版本号
*/
private Integer version;
@Override
public String toString() {
return "Goods{" +
"id=" + id +
", name=‘" + name + ‘\‘‘ +
", remainingNumber=" + remainingNumber +
", version=" + version +
‘}‘;
}
}
public interface GoodsMapper {
Integer updateGoodCAS(Goods good);
}
<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>
public interface GoodsService {
@Transactional
Boolean updateGoodCAS(Integer id, Integer decreaseNum);
}
@Service
public class GoodsServiceImpl implements GoodsService {
@Autowired
private GoodsMapper goodsMapper;
@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;
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class GoodsServiceImplTest {
@Autowired
private GoodsService goodsService;
@Test
public void updateGoodCASTest() {
final Integer id = 1;
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
goodsService.updateGoodCAS(id, 1); //用户1的请求
}
});
thread.start();
goodsService.updateGoodCAS(id, 2); //用户2的请求
System.out.println(goodsService.selectGoodById(id));
}
}
输出结果:
Goods{id=1, name=‘手机‘, remainingNumber=10, version=9}
Goods{id=1, name=‘手机‘, remainingNumber=10, version=9}
success
fail
Goods{id=1, name=‘手机‘, remainingNumber=8, version=10}
代码说明:
在updateGoodCASTest()的测试方法中,用户1和用户2同时查出id=1的商品的同一个版本信息,然后分别对商品进行库存减1和减2的操作。从输出的结果可以看出用户2的减库存操作成功了,商品库存成功减去2;而用户1提交减库存操作时,数据版本号已经改变,所以数据变更失败。
这样,我们就可以通过MySQL的乐观锁机制保证在分布式场景下的数据一致性。
以上。
https://segmentfault.com/a/11...
【转】MySQL乐观锁在分布式场景下的实践
标签:int enum innodb app where 后端服务 情况下 varchar 部署