文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2022-07-01 10:00

关注

说明

如果你的项目流量非常小,完全不用担心有并发的购买请求,那么做这样一个系统意义不大。但如果你的系统要像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;

初始化数据:

在这里插入图片描述

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、业务流程

在这里插入图片描述

代码实现

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

在这里插入图片描述

可通过逆向工程进行配置,参考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、实现


@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乐观锁秒杀内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯