无锁秒杀系统设计:基于Java的高效实现

news2025/5/12 14:33:59

引言

在电商促销活动中,秒杀场景是非常常见的。为了确保高并发下的数据一致性、性能以及用户体验,本文将介绍几种不依赖 Redis 实现的无锁秒杀方案,并提供简化后的 Java 代码示例和架构图

一、基于数据库乐观锁机制

✅ 实现思路:

  • 使用版本号字段控制库存更新,避免超卖问题。
  • 每次扣减前检查版本号是否匹配,保证原子性操作。

📌 架构图:

在这里插入图片描述

🧱 数据库表结构(伪 SQL):

CREATE TABLE product_stock (
    product_id BIGINT PRIMARY KEY,
    stock_count INT NOT NULL,
    version INT DEFAULT 0
);

🧾 核心实体类(Lombok):

@Data
public class ProductStock {
    private Long productId;
    private Integer stockCount;
    private Integer version;
}

🔐 乐观锁服务类:

@Service
public class OptimisticLockService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public boolean reduceStock(Long productId) {
        String selectSql = "SELECT stock_count, version FROM product_stock WHERE product_id = ?";
        Map<String, Object> result = jdbcTemplate.queryForMap(selectSql, productId);
        int stockCount = (int) result.get("stock_count");
        int version = (int) result.get("version");

        if (stockCount <= 0) return false;

        String updateSql = "UPDATE product_stock SET stock_count = ?, version = ? WHERE product_id = ? AND version = ?";
        int rowsAffected = jdbcTemplate.update(
            updateSql,
            stockCount - 1,
            version + 1,
            productId,
            version
        );

        return rowsAffected > 0;
    }
}

二、利用数据库唯一约束防重下单

✅ 实现思路:

  • 利用数据库唯一索引特性防止重复下单。
  • 先插入预订单记录,再执行库存扣除。

📌 架构图:

在这里插入图片描述

🧱 数据库表结构(伪 SQL):

CREATE TABLE pre_order (
    user_id BIGINT,
    product_id BIGINT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY(user_id, product_id)
);

🧾 核心实体类:

@Data
public class PreOrder {
    private Long userId;
    private Long productId;
}

🔐 防重下单服务类:

@Service
public class UniqueConstraintService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public boolean placeOrder(Long userId, Long productId) {
        try {
            // 插入预订单(唯一约束)
            jdbcTemplate.update("INSERT INTO pre_order(user_id, product_id) VALUES (?, ?)", userId, productId);

            // 扣减库存
            int rowsAffected = jdbcTemplate.update("UPDATE product_stock SET stock_count = stock_count - 1 WHERE product_id = ? AND stock_count > 0", productId);

            if (rowsAffected == 0) {
                rollbackOrder(userId, productId);
                return false;
            }

            return true;
        } catch (DuplicateKeyException e) {
            // 唯一键冲突
            return false;
        }
    }

    private void rollbackOrder(Long userId, Long productId) {
        jdbcTemplate.update("DELETE FROM pre_order WHERE user_id = ? AND product_id = ?", userId, productId);
    }
}

三、消息队列结合批量处理(异步化削峰填谷)

✅ 实现思路:

  • 将所有秒杀请求放入消息队列中。
  • 后台消费者按批次处理,避免瞬时冲击数据库。

📌 架构图:

用户发起请求
发送至消息队列
消费者监听消息
批量处理订单
扣减库存
生成订单

🧾 消息体定义:

@Data
public class OrderMessage {
    private Long userId;
    private Long productId;
}

🧾 消息生产者(Spring Boot 示例):

@Component
public class MessageProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendOrder(OrderMessage message) {
        rabbitTemplate.convertAndSend("order_queue", message);
    }
}

🧾 消费者逻辑(模拟批量处理):

@Component
public class OrderConsumer {

    @RabbitListener(queues = "order_queue")
    public void process(OrderMessage message) {
        // 异步处理下单逻辑
        System.out.println("Processing order: " + message.getUserId() + " -> " + message.getProductId());
        // 调用库存服务或其他实际业务逻辑
    }
}

四、内存计算 + 定期同步至数据库(推荐用于超高频读写)

✅ 实现思路:

  • 使用 ConcurrentHashMap 维护商品库存缓存。
  • 用户请求优先修改内存中的值,降低 DB 压力。
  • 定时任务定期将变更同步到数据库。

📌 架构图:

在这里插入图片描述

🧾 内存库存管理器:

@Component
public class InMemoryStockManager {

    private final Map<Long, Integer> stockCache = new ConcurrentHashMap<>();

    public void initStock(Long productId, int stockCount) {
        stockCache.put(productId, stockCount);
    }

    public boolean reduceStock(Long productId) {
        return stockCache.computeIfPresent(productId, (k, v) -> v > 0 ? v - 1 : v) != null;
    }

    public int getStock(Long productId) {
        return stockCache.getOrDefault(productId, 0);
    }
}

🧾 异步下单服务:

@Service
public class AsyncOrderService {

    @Autowired
    private InMemoryStockManager stockManager;

    private final ExecutorService executor = Executors.newFixedThreadPool(5);

    public void placeOrder(Long userId, Long productId) {
        if (stockManager.reduceStock(productId)) {
            executor.submit(() -> savePreOrderToDB(userId, productId));
        } else {
            System.out.println("库存不足");
        }
    }

    private void savePreOrderToDB(Long userId, Long productId) {
        // 这里可以调用 DAO 或 JdbcTemplate 插入预订单
        System.out.println("保存预订单:" + userId + " 购买了商品ID:" + productId);
    }
}

🧾 定时同步任务:

@Component
public class SyncTask {

    @Scheduled(fixedRate = 60_000) // 每分钟执行一次
    public void syncOrdersToDatabase() {
        List<PreOrder> orders = fetchUnsyncedOrders();
        for (PreOrder order : orders) {
            updateProductStock(order.getProductId());
        }
        deleteSyncedOrders(orders);
    }

    private List<PreOrder> fetchUnsyncedOrders() {
        // 查询待同步订单
        return List.of(new PreOrder(1L, 1001L), new PreOrder(2L, 1001L));
    }

    private void updateProductStock(Long productId) {
        // 执行更新库存
        System.out.println("同步库存:" + productId);
    }

    private void deleteSyncedOrders(List<PreOrder> orders) {
        // 删除已经同步的订单
        orders.forEach(order -> System.out.println("删除订单:" + order));
    }
}

✅ 总结与建议

方案特点适用场景
乐观锁简单、数据一致性高并发量适中
唯一约束下单防止重复下单单用户限购
消息队列异步解耦、削峰填谷大流量场景
内存计算+定时同步高吞吐、低延迟超高并发秒杀

💡 推荐组合使用:

  • “内存计算 + 唯一约束” + “定时同步” 是一个非常实用且高效的组合,在秒杀中表现优异。
  • 可根据业务阶段动态启用不同策略,例如前 5 秒使用内存计算,后续切为乐观锁。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2373993.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NCCL N卡通信机制

转自我的博客&#xff1a;https://shar-pen.github.io/2025/05/05/torch-distributed-series/nccl_communication/ from IPython.display import Image import logging import torch import torch.distributed as distpytorch 分布式相关api torch.distributed.init_process_…

Alpha3DCS公差分析系统_国产替代的3D精度管控方案-SNK施努卡

随着智能制造发展规划的深入推进&#xff0c;工业软件国产化替代已上升为国家战略。在公差分析这一细分领域&#xff0c;长期被国外软件垄断的局面正被打破。 苏州施努卡自主研发的Alpha3DCS&#xff0c;凭借完全自主知识产权和军工级安全标准&#xff0c;成为国内实现三维公差…

ABB电机控制和保护单元与Profibus DP主站转Modbus TCP网关快速通讯案例

ABB电机控制和保护单元与Profibus DP主站转Modbus TCP网关快速通讯案例 在现代工业自动化系统中&#xff0c;设备之间的互联互通至关重要。Profibus DP和Modbus TCP是两种常见的通信协议&#xff0c;分别应用于不同的场景。为了实现这两种协议的相互转换&#xff0c;Profibus …

0基础 | L298N电机驱动模块 | 使用指南

引言 在嵌入式系统开发中&#xff0c;电机驱动是一个常见且重要的功能。L298N是一款高电压、大电流电机驱动芯片&#xff0c;广泛应用于各种电机控制场景&#xff0c;如直流电机的正反转、调速&#xff0c;以及步进电机的驱动等。本文将详细介绍如何使用51单片机来控制L298N电…

【金仓数据库征文】金仓数据库:创新驱动,引领数据库行业新未来

一、引言 在数字化转型的时代洪流中&#xff0c;数据已跃升为企业的核心资产&#xff0c;宛如企业运营与发展的 “数字命脉”。从企业日常运营的精细化管理&#xff0c;到战略决策的高瞻远瞩制定&#xff1b;从客户关系管理的深度耕耘&#xff0c;到供应链优化的全面协同&…

大模型系列(五)--- GPT3: Language Models are Few-Shot Learners

论文链接&#xff1a; Language Models are Few-Shot Learners 点评&#xff1a; GPT3把参数规模扩大到1750亿&#xff0c;且在少样本场景下性能优异。对于所有任务&#xff0c;GPT-3均未进行任何梯度更新或微调&#xff0c;仅通过纯文本交互形式接收任务描述和少量示例。然而&…

Qt QCheckBox 使用

1.开发背景 Qt QCheckBox 是勾选组件&#xff0c;具体使用方法可以参考 Qt 官方文档&#xff0c;这里只是记录使用过程中常用的方法示例和遇到的一些问题。 2.开发需求 QCheckBox 使用和踩坑 3.开发环境 Window10 Qt5.12.2 QtCreator4.8.2 4.功能简介 4.1 简单接口 QChec…

系统架构-面向服务架构(SOA)

概述 服务指的是系统对外提供的功能集 从应用的角度定义&#xff0c;可以认为SOA是一种应用框架&#xff0c;将日常业务划分为单独的业务功能和流程&#xff08;即服务&#xff09;&#xff0c;SOA使用户可以构建、部署和整合这些服务。 从软件的基本原理定义&#xff0c;SO…

AJAX原理

AJAX使用XHR 对象和服务器进行数据交互 XHR <p class"my-p"></p><script>const xhr new XMLHttpRequest()xhr.open(GET,http://hmajax.itheima.net/api/province)xhr.addEventListener(loadend,()>{// console.log(xhr.response)const data …

Paddle Serving|部署一个自己的OCR识别服务器

前言 之前使用C部署了自己的OCR识别服务器&#xff0c;Socket网络传输部分是自己写的&#xff0c;回过头来一看&#xff0c;自己犯傻了&#xff0c;PaddleOCR本来就有自己的OCR服务器项目&#xff0c;叫PaddleServing&#xff0c;这里记录一下部署过程。 1 下载依赖环境 1.1 …

Web开发—Vue工程化

文章目录 前言 Vue工程化 一、介绍 二、环境准备 1.介绍create-vue 2.NodeJS安装 3.npm介绍 三&#xff0c;Vue项目创建 四&#xff0c;项目结构 五&#xff0c;启动项目 六&#xff0c;Vue项目开发流程 七&#xff0c;API风格 前言 Vue工程化 前面我们在介绍Vue的时候&#…

Word如何制作三线表格

1.需求 将像这样的表格整理成论文中需要的三线表格。 2.直观流程 选中表格 --> 表格属性中的边框与底纹B --> 在设置中选择无&#xff08;重置表格&#xff09;–> 确定 --> 选择第一行&#xff08;其实是将第一行看成独立表格了&#xff0c;为了设置中线&…

【实战教程】零基础搭建DeepSeek大模型聊天系统 - Spring Boot+React完整开发指南

&#x1f525; 本文详细讲解如何从零搭建一个完整的DeepSeek AI对话系统&#xff0c;包括Spring Boot后端和React前端&#xff0c;适合AI开发入门者快速上手。即使你是编程萌新&#xff0c;也能轻松搭建自己的AI助手&#xff01; &#x1f4da;博主匠心之作&#xff0c;强推专栏…

用C语言实现的——一个支持完整增删查改功能的二叉排序树BST管理系统,通过控制台实现用户与数据结构的交互操作。

一、知识回顾 二叉排序树&#xff08;Binary Search Tree&#xff0c;BST&#xff09;&#xff0c;又称二叉查找树或二叉搜索树&#xff0c;是一种特殊的二叉树数据结构。 基本性质&#xff1a; ①有序性 对于树中的每个节点&#xff0c;其左子树中所有节点的值都小于该节点的…

论文阅读笔记——ROBOGROUND: Robotic Manipulation with Grounded Vision-Language Priors

RoboGround 论文 一类中间表征是语言指令&#xff0c;但对于空间位置描述过于模糊&#xff08;“把杯子放桌上”但不知道放桌上哪里&#xff09;&#xff1b;另一类是目标图像或点流&#xff0c;但是开销大&#xff1b;由此 GeoDEX 提出一种兼具二者的掩码。 相比于 GR-1&#…

『 测试 』测试基础

文章目录 1. 调试与测试的区别2. 开发过程中的需求3. 开发模型3.1 软件的生命周期3.2 瀑布模型3.2.1 瀑布模型的特点/缺点 3.3 螺旋模型3.3.1 螺旋模型的特点/缺点 3.4 增量模型与迭代模型3.5 敏捷模型3.5.1 Scrum模型3.5.2 敏捷模型中的测试 4 测试模型4.1 V模型4.2 W模型(双V…

robomaster机甲大师--电调电机

文章目录 C620电调ID设置速率 电调发送报文电调接收报文cubemx程序初始化发送接收 C620电调 ID设置 速率 1Mbps 电调发送报文 发送的数据为控制电机的输出电流&#xff0c;需要将can数据帧的ID设置为0x200 电调接收报文 机械角度&#xff1a;电机的0到360度映射到0到几千转…

少儿编程机构用的教务系统

在编程教育行业快速发展的今天&#xff0c;培训机构面临着学员管理复杂、课程体系专业性强、教学效果难以量化等独特挑战。爱耕云教务系统针对编程培训机构的特殊需求&#xff0c;提供了一套全方位的数字化解决方案&#xff0c;帮助机构实现高效运营和教学质量提升。 为什么编…

基于VSCode+PlatformIO环境的ESP8266的HX1838红外模块

以下是针对ESP8266开发板的红外遥控解码系统开发教程&#xff0c;基于VSCodePlatformIO环境编写 一、概述 本实验通过ESP8266开发板实现&#xff1a; 红外遥控信号解码自定义按键功能映射串口监控输出基础设备控制&#xff08;LED&#xff09; 硬件组成&#xff1a; NodeMC…

Linux中的防火墙

什么是防火墙 windows防火墙的设置 linux防火墙设置命令 什么是防火墙&#xff1f; 防火墙是一种网络安全设备&#xff0c;它能够&#xff1a; 监控和过滤进出网络的流量 阻止不安全的连接 保护计算机和网络免受未授权访问 创建一个安全边界 简单来说&#xff0c;防火…