初始 select...for update...

达芬奇密码2018-07-12 10:06

一.业务背景

    目前,我有下面一个业务场景。多个用户访问页面,我会从数据库里面分配一个数据码给每个用户。这个数据码可以使用的前提是它的状态是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)

本文来自网易实践者社区,经作者田躲躲授权发布。