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

作者:yy1209357299 时间:2024-01-18 02:01:41 

说明

如果你的项目流量非常小,完全不用担心有并发的购买请求,那么做这样一个系统意义不大。但如果你的系统要像12306那样,接受高并发访问和下单的考验,那么你就需要一套完整的流程保护措施,来保证你系统在用户流量高峰期不会被搞挂了。

进阶redis+mq实现:参考springboot + rabbitmq + redis实现秒杀

严格防止超卖
保证用户体验:高并发下,别网页打不开了,支付不成功了,购物车进不去了,地址改不了了
防止黑产:防止不怀好意的人群通过各种技术手段把你本该下发给群众的利益全收入了囊中

具体实现

1、核心
mysql乐观锁防止超卖

乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。

这里是引用通常实现是这样的:在表中的数据进行操作时(更新),先给数据表加一个版本(version)字段,每操作一次,将那条记录的版本号加1。也就是先查询出那条记录,获取出version字段,如果要对那条记录进行操作(更新),则先判断此刻version的值是否与刚刚查询出来时的version的值相等,如果相等,则说明这段期间,没有其他程序对其进行操作,则可以执行更新,将version字段的值加1;如果更新时发现此刻的version值与刚刚获取出来的version的值不相等,则说明这段期间已经有其他程序对其进行操作了,则不进行更新操作。

2、建表语句
stock商品表

-- ----------------------------
-- Table structure for stock
-- ----------------------------
DROP TABLE IF EXISTS `stock`;
CREATE TABLE `stock` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 `name` varchar(50) NOT NULL DEFAULT '' COMMENT '名称',
 `count` int(11) NOT NULL COMMENT '库存',
 `sale` int(11) NOT NULL COMMENT '已售',
 `version` int(11) NOT NULL COMMENT '乐观锁,版本号',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

初始化数据:

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

stock_order订单表

-- ----------------------------
-- Table structure for stock_order
-- ----------------------------
DROP TABLE IF EXISTS `stock_order`;
CREATE TABLE `stock_order` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 `sid` int(11) NOT NULL COMMENT '库存ID',
 `name` varchar(30) NOT NULL DEFAULT '' COMMENT '商品名称',
 `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3、业务流程

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

代码实现

1、pom

<dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>

<dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
       </dependency>

<!--mysql-->
       <dependency>
           <groupId>org.mybatis.spring.boot</groupId>
           <artifactId>mybatis-spring-boot-starter</artifactId>
           <version>1.3.0</version>
       </dependency>

<dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
       </dependency>

<!--druid-->
       <dependency>
           <groupId>com.alibaba</groupId>
           <artifactId>druid</artifactId>
           <version>1.1.10</version>
       </dependency>

<dependency>
           <groupId>com.alibaba</groupId>
           <artifactId>druid-spring-boot-starter</artifactId>
           <version>1.1.10</version>
       </dependency>

<dependency>
           <groupId>com.github.pagehelper</groupId>
           <artifactId>pagehelper</artifactId>
           <version>5.2.0</version>
       </dependency>

<dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <version>1.18.8</version>
       </dependency>

<dependency>
           <groupId>org.junit.jupiter</groupId>
           <artifactId>junit-jupiter</artifactId>
           <version>5.8.2</version>
           <scope>test</scope>
       </dependency>

2、model

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

可通过逆向工程进行配置,参考idea+mybatis逆向工程

4、dao

public interface StockMapper {

Stock checkStock(Integer id);//校验库存

int updateSale(Stock stock);//扣除库存
}
public interface StockOrderMapper {

//创建订单
   void createOrder(StockOrder order);
}

5、sql

商品校验和减库存

<select id="checkStock" parameterType="java.lang.Integer" resultType="com.yy.msserver.model.vo.Stock">
   select * from stock where id = #{id}
 </select>

<update id="updateSale" parameterType="com.yy.msserver.model.vo.Stock" >
   update stock
   set sale = #{sale,jdbcType=INTEGER} + 1,
       version = #{version,jdbcType=INTEGER} + 1,
       count = #{count,jdbcType=INTEGER} - 1
   where id = #{id,jdbcType=INTEGER}
     AND count > 0 AND version = #{version}
 </update>

下订单

<insert id="createOrder" parameterType="com.yy.msserver.model.vo.StockOrder">
   insert into stock_order (sid, name,
                            create_time)
   values (#{sid,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR},
           #{createTime,jdbcType=TIMESTAMP})
 </insert>

6、service

public interface StockOrderService {
   public Integer createOrder(Integer id);
}

7、实现

/**
* @author code
* @Date 2022/6/24 9:25
* Description 订单实现
* Version 1.0
*/
@Service
public class StockOrderServiceImpl implements StockOrderService {
   @Autowired
   private StockOrderMapper stockOrderMapper;

@Autowired
   private StockMapper stockMapper;

@Override
   @Transactional(rollbackFor = Exception.class)
   public Integer createOrder(Integer id) {
       //校验库存
       Stock stock = checkStock(id);
       if(stock.getCount()>0){
           System.out.println("当前库存:" + stock.getCount());
           //扣库存
           if(updateSale(stock) == 1){
               return createOrder(stock);
           }else {
               return 0;
           }
       }
       return 0;
   }

//校验库存
   private Stock checkStock(Integer id) {
       return stockMapper.checkStock(id);
   }

//扣库存
   private int updateSale(Stock stock){
       return stockMapper.updateSale(stock);
   }

//下订单
   private Integer createOrder(Stock stock){
       StockOrder order = new StockOrder();
       order.setSid(stock.getId());
       order.setCreateTime(new Date());
       order.setName(stock.getName());
       stockOrderMapper.createOrder(order);
       return order.getId();
   }
}

8、测试
模拟100人参与活动

@SpringBootTest
class MsServerApplicationTests {
   @Autowired
   private StockOrderService stockOrderService;

@Test
   void contextLoads() throws InterruptedException {

// 库存初始化为10,这里通过CountDownLatch和线程池模拟100个并发
       int threadTotal = 100;

ExecutorService executorService = Executors.newCachedThreadPool();

final CountDownLatch countDownLatch = new CountDownLatch(threadTotal);
       for (int i = 0; i < threadTotal ; i++) {
           int uid = i;
           executorService.execute(() -> {
               try {
                   stockOrderService.createOrder(1);
               } catch (Exception e) {
                   e.printStackTrace();
               }
               countDownLatch.countDown();
           });
       }

countDownLatch.await();
       executorService.shutdown();

}
}

9、结果

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

商品表

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

订单表

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

来源:https://blog.csdn.net/yy1209357299/article/details/125448728

标签:mysql,乐观锁,秒杀
0
投稿

猜你喜欢

  • 基于Python计算圆周率pi代码实例

    2021-03-06 05:13:17
  • 一篇文章搞懂Python Unittest测试方法的执行顺序

    2023-11-01 09:38:01
  • php文件缓存类用法实例分析

    2023-08-17 16:26:44
  • TypeScript与JavaScript项目里引入MD5校验和

    2024-05-28 15:40:03
  • VueJs单页应用实现微信网页授权及微信分享功能示例

    2024-04-16 09:35:50
  • 使用GitHub和Python实现持续部署的方法

    2022-07-16 22:54:35
  • 在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误。未找到或无法访问服务器

    2024-01-18 04:28:24
  • python获取http请求响应头headers中的数据的示例

    2023-06-30 14:49:02
  • Python实现向服务器请求压缩数据及解压缩数据的方法示例

    2023-03-14 05:02:08
  • 一文详解MySQL主从同步原理

    2024-01-24 03:00:59
  • Kears 使用:通过回调函数保存最佳准确率下的模型操作

    2023-02-24 12:36:56
  • python中Tkinter 窗口之输入框和文本框的实现

    2023-02-17 06:11:33
  • css有趣而诡异的数组

    2009-02-04 16:06:00
  • 可用于监控 mysql Master Slave 状态的python代码

    2023-01-05 07:39:30
  • Python练习之操作MySQL数据库

    2024-01-20 12:29:22
  • python如何支持并发方法详解

    2021-05-29 16:50:17
  • Django模板标签{% for %}循环,获取制定条数据实例

    2023-02-25 02:27:59
  • tween.js缓动补间动画算法示例

    2024-05-21 10:13:40
  • django rest framework 数据的查找、过滤、排序的示例

    2023-07-18 16:33:39
  • golang程序进度条实现示例详解

    2024-04-25 13:22:45
  • asp之家 网络编程 m.aspxhome.com