热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

并发出体验--解决小规模并发下单的问题

场景描述现在有这么一个业务场景,线上通过手机app下单买祈福灯,支付成功后,线下寺庙点亮。存在多个用户同时选择同一个灯的情况出现,如下图。此时,正常情况应为一个用户下单成功,其余显示灯已被选

场景描述

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

分析过程

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

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

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

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

 乐观锁与悲观锁

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

  显然乐观锁的相应速度快,悲观锁的时间消耗等都比较大。对于我们这个案例,使用这两种都是可以解决的。关于乐观锁和悲观锁详情可参考【https://www.cnblogs.com/Kevin-ZhangCG/p/10272293.html】

 悲观锁

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

set autocommit=0;

共享锁

SELECT … LOCK IN SHARE MODE

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

排他锁

SELECTFOR 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}

问题解决

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

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

public boolean updateDengAnyway(String ccode, List 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;
}

 

 

 

 

 原文参考【http://www.cnblogs.com/Sinte-Beuve/p/7631745.html】

 

 


推荐阅读
  • EPICS Archiver Appliance存储waveform记录的尝试及资源需求分析
    本文介绍了EPICS Archiver Appliance存储waveform记录的尝试过程,并分析了其所需的资源容量。通过解决错误提示和调整内存大小,成功存储了波形数据。然后,讨论了储存环逐束团信号的意义,以及通过记录多圈的束团信号进行参数分析的可能性。波形数据的存储需求巨大,每天需要近250G,一年需要90T。然而,储存环逐束团信号具有重要意义,可以揭示出每个束团的纵向振荡频率和模式。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文介绍了在使用TortoiseSVN的Repo-browser浏览SVN时出现的错误,以及解决方法。文章提到了卸载当前版本、安装较低版本、使用更下一层的路径等解决方案。同时指出该问题可能是客户端与SVN服务端不匹配造成的,且服务端无法升级更高的SVN版本。 ... [详细]
  • 本文讨论了在数据库打开和关闭状态下,重新命名或移动数据文件和日志文件的情况。针对性能和维护原因,需要将数据库文件移动到不同的磁盘上或重新分配到新的磁盘上的情况,以及在操作系统级别移动或重命名数据文件但未在数据库层进行重命名导致报错的情况。通过三个方面进行讨论。 ... [详细]
  • 安装mysqlclient失败解决办法
    本文介绍了在MAC系统中,使用django使用mysql数据库报错的解决办法。通过源码安装mysqlclient或将mysql_config添加到系统环境变量中,可以解决安装mysqlclient失败的问题。同时,还介绍了查看mysql安装路径和使配置文件生效的方法。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • javascript  – 概述在Firefox上无法正常工作
    我试图提出一些自定义大纲,以达到一些Web可访问性建议.但我不能用Firefox制作.这就是它在Chrome上的外观:而那个图标实际上是一个锚点.在Firefox上,它只概述了整个 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • 本文介绍了一些好用的搜索引擎的替代品,包括网盘搜索工具、百度网盘搜索引擎等。同时还介绍了一些笑话大全、GIF笑话图片、动态图等资源的搜索引擎。此外,还推荐了一些迅雷快传搜索和360云盘资源搜索的网盘搜索引擎。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
author-avatar
mobiledu2502905343
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有