一.业务背景
目前,我有下面一个业务场景。多个用户访问页面,我会从数据库里面分配一个数据码给每个用户。这个数据码可以使用的前提是它的状态是0.我的sql是这样写的:
select * from tb_status where status=0 order by id asc limit 1;
我会找到当前状态可用,id最小的这条数据。业务逻辑是获取这条数据后,这条记录的status变成1,不可用。
二.正常场景
现在,我们来看一下这个业务的简易流程图。
当访问间隔时间很大,看上去没有任务问题,数据正常!
三.高并发场景
试想下下面这种情况:
当用户1和用户2访问间隔时间很短,或者说用户1还没有更新完status,用户2恰好执行了这条sql,那么用户1和用户2就获取到了同样的code,这显然不是我想要的结果。
四.解决方案:
理论:如果我这样做,会不会解决问题?
select * from tb_status where status=0 order by id asc limit 1 for update;
五.深入探析
1.我先初始化两条数据:
2.for update 功能1:
首先打开两个查询窗口,窗口1开启事物,执行sql,但是事物不提交,我们看看窗口2的sql执行情况。。。
窗口1
窗口2
可以看到for update 语句如果没有释放锁的话,其他请求是不会得到数据的。。
3.for update 功能2:
下面我修改下sql,看看执行的结果如何:
窗口1
窗口2
这里我们获取到的数据就是id为2的code了。。。
六.实际开发应用
1.如果我们用的是mybatis,那么sql语句这样写就OK了
select <include refid="allColumns" />
from <include refid="table"/>
<![CDATA[
where gameId=#{gameId} and status=1
]]>
order by id desc limit 1;
2.事物管理
我们只需要把执行查找和更新放在同一个事物下面管理就OK了
@Transactional(rollbackFor = {Exception.class }, propagation = Propagation.REQUIRED)
本文来自网易实践者社区,经作者田躲躲授权发布。