在更新冲突概率低的场景,乐观锁比悲观锁性能更高。
本文讲一下mybatis的乐观锁实现,我们的测试表如下:
要做的事情是获取status为1的一条数据,并更新status状态为2。 在并发情况下,用事务或者将查询更新两个操作加锁执行效率都不是很高。
我们更希望用乐观锁解决并发问题。
一开始我的代码是这样写的:
Stock stock = mapper.findOneByStatus();
//do something
int affectedRows = 0;
do {
//这里我们希望返回的是MySQL执行时的影响行数
affectedRows = mapper.update(stock);
} while (affectedRows == 0);
update对应SQL如下:
<update id="update">
UPDATE
stock
SET status = 2
WHERE id = #{id}
</update>
压测时,发现update返回的影响行数始终是1,不符合预期,高并发下,应该会有很多结果返回是0的。
通过查资料,发现原来mybatis的update返回的并不是影响行数,而是matched的行数
因为每次通过id去update,所以每次matched为1,返回1。
踩坑后,决定添加version字段,利用version,基于matched来实现乐观锁。如下:
Stock stock = mapper.findOneByStatus();
//do something
int matchedRows = 0;
do {
matchedRows = mapper.update(stock);
} while (matchedRows == 0);
mapper的update
<update id="update">
UPDATE
stock
SET status = 2,version = version +1
<!-- version是否相等决定返回值matched是0还是1 -->
WHERE id = #{id} and version = ${version}
</update>
经过压测,上述通过version实现的乐观锁,很好的解决了并发的问题。