基于mysql乐观锁实现秒杀的示例代码

下面是基于MySQL乐观锁实现秒杀的完整攻略:

背景介绍

在高并发场景下,主要涉及到的两个问题是:安全性与性能。乐观锁技术可以在不加锁的情况下保证多个并发请求对同一资源进行操作时,不会发生数据覆盖的情况。

技术方案

  • 在MySQL中,通过对update语句设置where条件来实现乐观锁控制。
  • 在应用层面,可以通过重试机制来实现乐观锁。

示例说明

下面通过两个示例来详细讲解如何基于MySQL乐观锁实现秒杀。

示例一

首先创建一个名为seckill的数据表,包含以下字段:

id INT PRIMARY KEY AUTO_INCREMENT COMMENT '秒杀id',  
seckill_name VARCHAR(120) NOT NULL COMMENT '秒杀名称',  
start_time TIMESTAMP NOT NULL COMMENT '秒杀开始时间',  
end_time TIMESTAMP NOT NULL COMMENT '秒杀结束时间',  
total INT NOT NULL COMMENT '秒杀商品总数量',  
version INT NOT NULL COMMENT '秒杀版本号',  
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',  
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间'

其中,version字段表示当前秒杀数据的版本号。

接下来,编写Java代码,实现对秒杀商品进行秒杀的功能:

public class SeckillService {
  private final AtomicInteger state = new AtomicInteger(0);
  private final SeckillMapper seckillMapper;

  public SeckillService(SeckillMapper seckillMapper) {
    this.seckillMapper = seckillMapper;
  }

  /**
   * 秒杀商品
   *
   * @param seckillId 秒杀id
   * @param userPhone 用户手机号
   * @return 秒杀结果
   */
  public SeckillExecution executeSeckill(long seckillId, long userPhone) {
    Seckill seckill = seckillMapper.queryById(seckillId);

    if (seckill == null) {
      return new SeckillExecution(seckillId, SeckillStateEnum.NOT_FOUND);
    }

    long nowTime = System.currentTimeMillis();
    if (nowTime < seckill.getStartTime().getTime() || nowTime > seckill.getEndTime().getTime()) {
      return new SeckillExecution(seckillId, SeckillStateEnum.REPEATED);
    }

    // 查询库存数量,如果库存不足,则不进行秒杀
    int stockCount = seckillMapper.getStockCount(seckillId);
    if (stockCount == 0) {
      return new SeckillExecution(seckillId, SeckillStateEnum.NO_STOCK);
    }

    int updCount = 0;
    while (updCount == 0) {
      updCount = seckillMapper.reduceStock(seckillId, seckill.getVersion() + 1);
    }

    SeckillSuccess success = new SeckillSuccess();
    success.setSeckillId(seckillId);
    success.setUserPhone(userPhone);
    success.setState(SeckillStateEnum.SEC_KILLED.getState());
    success.setCreateTime(new Date());

    int insertCount = seckillMapper.insertSuccessKilled(success);
    if (insertCount == 0) {
      updCount = seckillMapper.reduceStock(seckillId, seckill.getVersion() - 1);
      return new SeckillExecution(seckillId, SeckillStateEnum.REPEATED);
    }

    SeckillSuccess querySuccess = seckillMapper
        .querySuccessKilledWithSeckill(seckillId, userPhone, success.getCreateTime());

    return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS, querySuccess);
  }
}

在上述代码中,查询库存数量后,通过while循环进行乐观锁的实现,如果更新库存行数为0,则重试。

示例二

此示例中,使用Spring Boot 2.3.7版本进行开发。

首先创建以下两个表:

创建秒杀商品表,包含以下字段:

id INT PRIMARY KEY AUTO_INCREMENT COMMENT '秒杀id',  
seckill_name VARCHAR(120) NOT NULL COMMENT '秒杀名称',  
start_time TIMESTAMP NOT NULL COMMENT '秒杀开始时间',  
end_time TIMESTAMP NOT NULL COMMENT '秒杀结束时间',  
total INT NOT NULL COMMENT '秒杀商品总数量'

创建秒杀商品记录表,包含以下字段:

id INT PRIMARY KEY AUTO_INCREMENT COMMENT '秒杀记录id',  
seckill_id INT NULL COMMENT '秒杀id',  
user_phone BIGINT NULL COMMENT '用户手机号',  
state INT NULL COMMENT '秒杀状态',  
create_time TIMESTAMP NULL COMMENT '秒杀时间',
version INT NOT NULL COMMENT '乐观锁版本号'

其中,version字段表示当前秒杀记录的版本号。

接下来,编写Java代码,实现基于Spring Boot 2.3.7版本的秒杀功能:

@Service
@Slf4j
public class SeckillService {
  private final SeckillMapper seckillMapper;
  private final SeckillRecordMapper seckillRecordMapper;

  public SeckillService(SeckillMapper seckillMapper,
                        SeckillRecordMapper seckillRecordMapper) {
    this.seckillMapper = seckillMapper;
    this.seckillRecordMapper = seckillRecordMapper;
  }

  @Transactional(rollbackFor = Exception.class)
  public int seckill(long seckillId, long userPhone) {
    Seckill seckill = seckillMapper.queryById(seckillId);
    if (seckill == null) {
      throw new RuntimeException("秒杀活动不存在");
    }
    Date now = new Date();
    if (seckill.getEndTime().getTime() < now.getTime() ||
        seckill.getStartTime().getTime() > now.getTime()) {
      throw new RuntimeException("秒杀活动未开始或已结束");
    }
    int result = seckillMapper.reduceInventory(seckillId,
        seckill.getVersion(),
        new Date());
    if (result == 0) {
      throw new RuntimeException("秒杀商品已经被抢光了!");
    } else {
      int insResult = seckillRecordMapper.insertRecord(seckillId, userPhone);
      log.info("插入秒杀记录{},结果{}", seckillId, insResult);
      return result;
    }
  }
}

在上述代码中,通过@Transactional注解来实现对数据库的事务管理,从而保证秒杀过程中的数据一致性。其中,SeckillMapper类和SeckillRecordMapper类分别用于操作秒杀商品表和秒杀商品记录表。

至此,就完成了基于MySQL乐观锁实现秒杀的示例代码的完整攻略。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:基于mysql乐观锁实现秒杀的示例代码 - Python技术站

(0)
上一篇 2023年5月21日
下一篇 2023年5月21日

相关文章

  • 以前架征途时的合区的SQL语句代码备份

    以前架设途游时的合区过程涉及到对数据库进行备份和修改操作。下面,我们将详细讲解如何备份“以前架征途时的合区的SQL语句代码”。 1. 进入MySQL命令行 首先,需要在本机安装MySQL数据库,并打开MySQL命令行模式,输入以下命令: mysql -u root -p 然后,输入MySQL用户密码,即可进入MySQL命令行模式。 2. 备份数据库 在MyS…

    database 2023年5月21日
    00
  • 一个简洁的全自动安装LNMP服务器环境的Shell脚本分享

    下面将为您详细讲解“一个简洁的全自动安装LNMP服务器环境的Shell脚本分享”的完整攻略。 1. 什么是LNMP? LNMP指的是Linux+Nginx+MySQL+PHP的集成环境,它是一种开发环境或者服务器环境。 2. 介绍一下Shell脚本 Shell脚本是一种能够自动化处理任务的脚本语言,它能够通过命令行来运行。简单来说,Shell脚本就是一系列命…

    database 2023年5月22日
    00
  • 碎片拼接技术恢复XenServer服务器SQL Server数据库数据

    碎片拼接技术恢复XenServer服务器SQL Server数据库数据攻略 什么是碎片拼接技术? 碎片拼接技术是指通过拼接物理硬盘上的碎片文件来达到恢复数据的目的。在数据被删除或损坏、硬盘出现坏道等情况下,我们可以使用碎片拼接技术来尝试恢复数据。 准备工作 在使用碎片拼接技术进行数据恢复前,需要先准备以下工作: 磁盘拷贝工具:使用这个工具将硬盘拷贝到另一个硬…

    database 2023年5月18日
    00
  • ServiceStack.Redis 使用链接池方法

    1、RedisManage.cs public static class RedisManager { private static PooledRedisClientManager _prcm; static RedisManager() { CreateManager(); } /// <summary> /// 创建链接池管理对象 /// …

    Redis 2023年4月16日
    00
  • Navicat运行sql文件导入数据不全或导入失败的解决方案

    下面是详细讲解“Navicat运行sql文件导入数据不全或导入失败的解决方案”的完整攻略。 问题的背景 在使用Navicat工具进行sql文件导入时,可能会出现数据导入不全或导入失败的情况,这给数据导入带来了很大的麻烦。因此,我们需要找到解决这种情况的方法。 解决方案 方案一:增加sql文件导入参数 可以通过增加sql文件导入的参数来解决问题。具体操作如下:…

    database 2023年5月18日
    00
  • 总结12个MySQL慢查询的原因分析

    总结12个MySQL慢查询的原因分析 慢查询的定义 MySQL中可以通过slow_query_log来记录执行时间超过一定阈值(默认为10s)的SQL语句,这些被记录下来的SQL语句称作慢查询。 慢查询的原因 在MySQL中,慢查询的原因有很多,下面我们来总结12个常见的慢查询原因: 1. 数据库连接过多 如果连接数过多,就会导致需要排队等待执行,从而降低数…

    database 2023年5月19日
    00
  • MySQL8.0 DDL原子性特性及实现原理

    MySQL 8.0 DDL原子性特性及实现原理攻略 什么是DDL操作 DDL(Data Definition Language)包含了用来创建(CREATE)、修改(ALTER)、删除(DROP)数据库对象(如表,视图,过程等)的语句。对于MySQL而言,通过执行各种DDL操作可以创建,修改数据库对象,从而达到管理数据的目的。 DDL对原始数据的影响 在进行…

    database 2023年5月22日
    00
  • 在MySQL中同时查找两张表中的数据的示例

    在MySQL中同时查找两张表中的数据通常需要使用联合查询。联合查询可以将多个 SELECT 语句的结果合并为一个结果集。以下是实现联合查询的步骤和示例: 使用 SELECT 语句从每个表中选择需要查询的列。 使用 UNION 关键字将两个 SELECT 语句合并为一个结果集。UNION 关键字会默认去重,如果需要保留重复数据,可以使用 UNION ALL。 …

    database 2023年5月22日
    00
合作推广
合作推广
分享本页
返回顶部