热门搜索:  

并发初体验,解决小规模并发下单问题

最近解决了一个小规模并发下单问题,来跟大家分享一下。

场景描述

现在有这么一个业务场景,线上通过手机app下单买祈福灯,支付成功后,线下寺庙点亮。存在多个 用户同时选择同一个灯的情况出现,如下图。此时,正常情况应为一个用户下单成功,其余显示灯已被选。由于,支付和下单是单独分开的,只要focus on下单就ok了。

简而言之,就是一个并发现单的问题。

分析过程

我们可以想到的正常下单的流程,应该是这样的:

1. 选择祈福灯时,先查询灯是否可用。
2. 选择祈福灯,例如图中的“D0000065”。
3. 下单业务逻辑,再次查询灯是否可用。
if(灯可用){
    该祈福灯状态设为已购买
    生成订单记录
    相关日志记录...
}

在没有并发问题发生时,上面的流程近乎完美(really?),可是,多人下单时,同时去数据库中查询灯的状态时,结果都是可用的,接下来,emmmm,你懂的。

那么,判断灯是否可用再下单,这样的逻辑是存在问题的。解决并发下单的常规思路不外乎两种,一是加锁,二是利用队列。这里,我主要是通过对数据库加锁的方式来解决这个问题的。

乐观锁与悲观锁

在此之前,需要了解一些关于的概念。在本科的数据库原理课上。我们接触到两个概念——共享锁和排它锁,现在又需要两个新的概念——乐观锁和悲观锁。

  • 乐观锁(Optimistic Lock),想法乐观,认为自己在操作数据库时不会发生冲突,取数据时不加锁,更新数据是加锁,进行判断。

  • 悲观锁(Pessimistic Lock),想法悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

显然乐观锁的相应速度快,悲观锁的时间消耗等都比较大。对于我们这个案例,使用这两种都是可以解决的。

锁的sql实现方式

这里主要以mysql innodb为例。innodb本身是支持行锁的。

悲观锁

要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。

set autocommit=0;
  • 共享锁(s锁)

    SELECT … LOCK IN SHARE MODE

    SELECT … LOCK IN SHARE MODE 在读取的行上设置一个共享锁,其他的session可以读这些行,但在你的事务提交之前不可以修改它们。如果这些行里有被其他的还没有提交的事务修改,你的查询会等到那个事务结束之后使用最新的值。

  • 排它锁(x锁)

    SELECT … FOR UPDATE

    索引搜索遇到的记录,SELECT … FOR UPDATE 会锁住行及任何关联的索引条目,和你对那些行执行 update 语句相同。其他的事务会被阻塞在对这些行执行 update 操作,获取共享锁,或从某些事务隔离级别读取数据等操作。一致性读(Consistent Nonlocking Reads)会忽略在读取视图上的记录的任何锁。(旧版本的记录不能被锁定;它们通过应用撤销日志在记录的内存副本上时被重建。)

注:普通 select 语句默认不加锁,而CUD操作默认加排他锁。

乐观锁

乐观锁也就是在更新时进行查询,通常用一个version字段来实现。

UPDATE ... WHERE...
# 基于version的实现
SELECT ..., verison FROM [table] WHERE id = #{id}
UPDATE [table] SET..., version = version + 1 where id = #{id} AND version = #{version}

当然,在ORM中也有相应的实现方式。具体可以参考细谈Hibernate之悲观锁和乐观锁解决hibernate并发

问题解决

在项目中,由于时间关系没有使用基于version方式的乐观锁,而是直接采用了update ... where的方式。直接对当前的灯号进行查询,如果可用就立刻更新灯的状态为不可用,相当于加共享锁。如果发生并发的情况,同时用update语句,数据库也会自动加上X锁,因此最终只有一个用户可以下单成功。
下单流程:

public int saveOrder(){
    // 执行update ... where
    boolean isAvaliable;
    isAvaliable = denginfoService.updateDengAnyway();
    if (isAvaliable) {
        //下单的业务逻辑
    }
}

public boolean updateDengAnyway(String ccode, List<String> dengid) {
    //判断灯是否可用
    String hql = "update DenginfoEntity set ordertype =2 where ccode=? and dengid=? and (ordertype=0 or (ordertype=1 and ordertime<?))";
    Query query;int flag;
    try {
        for (String deng : dengid) {
            query = getSession().createQuery(hql);
            //参数化赋值
            flag = query.executeUpdate();
            logger.info("update---  " + flag);
            if (flag == 0)
                return false;
        }
    } catch (HibernateException | NullPointerException e) {
        e.printStackTrace();
        return false;
    }
    return true;
}

参考文献

  • PHP如何解决并发下单问题?(不一定是下单,举个例)
  • MySQL中SELECT+UPDATE处理并发更新问题解决方案分享
  • mysql悲观锁以及乐观锁总结和实践
  • MySQL 行级锁SELECT ... LOCK IN SHARE MODE 和 SELECT ... FOR UPDATE

当前文章:http://p6hsfgvf.gwestdon.com/a/363ba_23.html

发布时间:2017-10-20 01:35:46

我的心没有回程    我的心没有回程  我的心没有回程  我的心没有回程  我的心没有回程  我的心没有回程  我的心没有回程  我的心没有回程  我的心没有回程  

http://www.hnhx.net.cn/6x8Y2k0M54/201710100228.htmlhttp://www.hnhx.net.cn/Z94d696264/201710100927.htmlhttp://www.hnhx.net.cn/57Sp705pm3/201710100395.htmlhttp://www.hnhx.net.cn/5B068958d/201710100369.htmlhttp://www.hnhx.net.cn/n677167J21/201710100323.htmlhttp://www.hnhx.net.cn/F902t5Sp0/201710100139.htmlhttp://www.hnhx.net.cn/38B15WnjT3/201710100691.htmlhttp://www.hnhx.net.cn/Q1sK9Y7WM5/201710100576.htmlhttp://www.hnhx.net.cn/8210j97676/201710100705.htmlhttp://www.hnhx.net.cn/624c6F59C/201710100342.htmlhttp://www.hnhx.net.cn/5B068958d/201710100369.htmlhttp://www.hnhx.net.cn/Q1sK9Y7WM5/201710100576.htmlhttp://www.hnhx.net.cn/F902t5Sp0/201710100139.htmlhttp://www.hnhx.net.cn/8210j97676/201710100705.htmlhttp://www.hnhx.net.cn/6x8Y2k0M54/201710100228.htmlhttp://www.hnhx.net.cnhttp://www.hnhx.net.cn/8Xf30982g9/201710100977.htmlhttp://www.hnhx.net.cn/8210j97676/201710100705.htmlhttp://www.hnhx.net.cn/543h2WG3x1/201710100090.htmlhttp://www.hnhx.net.cn/38B15WnjT3/201710100691.htmlhttp://www.hnhx.net.cn/6x8Y2k0M54/201710100228.htmlhttp://www.hnhx.net.cn/0577n2486S/201710100281.htmlhttp://www.uknet.cnhttp://www.hnhx.net.cn/9H75j7wB5d/201710100001.htmlhttp://www.hnhx.net.cn/6x8Y2k0M54/201710100228.htmlhttp://www.hnhx.net.cn/543h2WG3x1/201710100090.htmlhttp://www.hnhx.net.cn/Tm27D92fqG/201710100264.htmlhttp://www.hnhx.net.cn/7734B06Z6/201710100809.htmlhttp://www.hnhx.net.cn/8210j97676/201710100705.htmlhttp://www.hnhx.net.cn/Q1sK9Y7WM5/201710100576.htmlhttp://www.hnhx.net.cn/9H75j7wB5d/201710100001.htmlhttp://zatanzixun.cnhttp://www.uknet.cnhttp://www.hnhx.net.cn/0577n2486S/201710100281.htmlhttp://www.hnhx.net.cn/4047934051/201710100688.htmlhttp://www.hnhx.net.cn/0577n2486S/201710100281.htmlhttp://www.hnhx.net.cn/Z94d696264/201710100927.htmlhttp://www.hnhx.net.cn/543h2WG3x1/201710100090.htmlhttp://www.hnhx.net.cn/8Xf30982g9/201710100977.htmlhttp://www.hnhx.net.cn/6x8Y2k0M54/201710100228.htmlhttp://www.hnhx.net.cn/7734B06Z6/201710100809.htmlhttp://www.hnhx.net.cn/57Sp705pm3/201710100395.htmlhttp://www.hnhx.net.cnhttp://www.hnhx.net.cn/38B15WnjT3/201710100691.htmlhttp://www.hnhx.net.cn/7734B06Z6/201710100809.htmlhttp://www.hnhx.net.cn/38B15WnjT3/201710100691.htmlhttp://www.hnhx.net.cn/Tm27D92fqG/201710100264.htmlhttp://www.hnhx.net.cn/624c6F59C/201710100342.htmlhttp://www.hnhx.net.cn/Tm27D92fqG/201710100264.htmlhttp://www.hnhx.net.cn/0577n2486S/201710100281.html