画秒杀系统流程图

秒杀系统流程图

秒杀系统关键点

  1. 高并发处理:
  • 使用网关(如 Nginx)进行流量限流,避免过载。
  • 分布式锁或 Redis 原子操作控制并发。
  1. 活动状态检查:
  • Redis 存储活动状态(如 seckill:activity:1:status),快速判断活动是否进行中。
  1. 用户资格校验:
  • Redis Set 记录参与用户(如 seckill:activity:1:users),检查是否重复参与。
  • 示例: SADD seckill:activity:1:users user123 和 SISMEMBER。
  1. 库存扣减(Redis Lua 脚本):
  • 为什么用 Lua 脚本?
    • 保证原子性,避免并发超卖。
    • 减少网络往返,提高性能。
  • Redis Key: seckill:activity:1:stock(库存)。
  • Lua 脚本示例:
lua 复制代码
local stock_key = KEYS[1]
local current_stock = tonumber(redis.call('GET', stock_key) or 0)
if current_stock <= 0 then
    return -1  -- 库存不足
end
redis.call('DECR', stock_key)
return current_stock - 1  -- 返回剩余库存
  • Java 调用 Lua 脚本(Spring Boot + Redis):
java 复制代码
@Autowired
private StringRedisTemplate redisTemplate;

public boolean deductStock(String activityId) {
    String stockKey = "seckill:activity:" + activityId + ":stock";
    String script = "local stock_key = KEYS[1] " +
                    "local current_stock = tonumber(redis.call('GET', stock_key) or 0) " +
                    "if current_stock <= 0 then return -1 end " +
                    "redis.call('DECR', stock_key) " +
                    "return current_stock - 1";
    
    Long result = redisTemplate.execute(
        new DefaultRedisScript<>(script, Long.class),
        Collections.singletonList(stockKey)
    );
    
    return result != null && result >= 0;
}
  1. 订单生成:
  • 异步队列(如 RabbitMQ、Kafka)处理订单生成,减轻数据库压力。
  • 示例: 将 {userId, activityId, timestamp} 发送到队列。
  1. 数据库写入:
  • 异步任务消费队列,批量插入订单到 MySQL。
  • 避免实时写库导致瓶颈。
  1. 防超卖:
  • Redis Lua 脚本确保库存不减为负。
  • 数据库加乐观锁(如 UPDATE stock SET count = count - 1 WHERE id = ? AND count > 0)。
  1. 返回响应:
  • 扣减成功后立即返回"秒杀成功",后续操作异步完成。

完整流程伪代码

java 复制代码
@RestController
public class SeckillController {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @PostMapping("/seckill/{activityId}")
    public String seckill(@PathVariable String activityId, @RequestParam String userId) {
        // 1. 检查活动状态
        String status = redisTemplate.opsForValue().get("seckill:activity:" + activityId + ":status");
        if (!"ongoing".equals(status)) {
            return "活动未开始或已结束";
        }

        // 2. 检查用户资格
        if (redisTemplate.opsForSet().isMember("seckill:activity:" + activityId + ":users", userId)) {
            return "已参与秒杀";
        }

        // 3. 扣减库存 (Lua 脚本)
        if (!deductStock(activityId)) {
            return "库存不足";
        }

        // 4. 标记用户参与
        redisTemplate.opsForSet().add("seckill:activity:" + activityId + ":users", userId);

        // 5. 异步生成订单
        rabbitTemplate.convertAndSend("seckill-queue", 
            new OrderMessage(userId, activityId, System.currentTimeMillis()));

        return "秒杀成功";
    }
}

补充:

redis减扣后 减扣 MySQL 库存方案

1. 异步减扣 MySQL 库存(推荐)

  • 时机
    • Redis 减库存成功后,将任务发送到异步队列(如 RabbitMQ、Kafka),由后台消费者异步更新 MySQL 库存。
  • 流程
    1. 用户发起秒杀请求。
    2. Redis Lua 脚本扣减库存(原子操作)。
    3. 扣减成功后:
      • 发送消息到队列(如 {activityId, userId, timestamp})。
      • 返回"秒杀成功"给前端。
    4. 队列消费者异步处理:
      • 更新 MySQL 库存表。
      • 生成订单记录。
java 复制代码
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private RabbitTemplate rabbitTemplate;

@PostMapping("/seckill/{activityId}")
public String seckill(@PathVariable String activityId, @RequestParam String userId) {
    // Redis 减库存
    if (!deductStock(activityId)) {
        return "库存不足";
    }
    // 异步更新 MySQL
    rabbitTemplate.convertAndSend("seckill-queue", 
        new OrderMessage(activityId, userId, System.currentTimeMillis()));
    return "秒杀成功";
}

// Lua 脚本扣库存
private boolean deductStock(String activityId) {
    String stockKey = "seckill:stock:" + activityId;
    String script = "local stock = tonumber(redis.call('GET', KEYS[1]) or 0) " +
                    "if stock <= 0 then return 0 end " +
                    "redis.call('DECR', KEYS[1]) " +
                    "return 1";
    Long result = redisTemplate.execute(
        new DefaultRedisScript<>(script, Long.class),
        Collections.singletonList(stockKey)
    );
    return result != null && result == 1;
}

// 队列消费者
@Component
@RabbitListener(queues = "seckill-queue")
public class SeckillConsumer {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @RabbitHandler
    public void process(OrderMessage msg) {
        // 更新 MySQL 库存
        String sql = "UPDATE seckill_stock SET stock = stock - 1 WHERE activity_id = ? AND stock > 0";
        int updated = jdbcTemplate.update(sql, msg.getActivityId());
        if (updated > 0) {
            // 插入订单
            jdbcTemplate.update("INSERT INTO seckill_order (activity_id, user_id, create_time) VALUES (?, ?, ?)",
                msg.getActivityId(), msg.getUserId(), msg.getTimestamp());
        }
    }
}
优点
  • 高性能: Redis 减库存后立即返回,MySQL 异步处理,避免实时写库瓶颈。
  • 高并发: 适合秒杀场景,减少数据库压力。
缺点
  • 数据一致性: Redis 和 MySQL 可能短暂不一致(最终一致性)。
  • 失败处理: 队列消费失败需重试或补偿。
  • 适用场景
  • 高并发秒杀,优先保证响应速度。

2. 同步减扣 MySQL 库存

  • 时机
    • Redis 减库存成功后,在同一事务中同步更新 MySQL 库存。
  • 流程
    1. 用户发起秒杀请求。
    2. Redis Lua 脚本扣减库存。
    3. 扣减成功后:
    4. 立即更新 MySQL 库存。
    5. 生成订单。
    6. 返回"秒杀成功"。
java 复制代码
@PostMapping("/seckill/{activityId}")
@Transactional
public String seckill(@PathVariable String activityId, @RequestParam String userId) {
    // Redis 减库存
    if (!deductStock(activityId)) {
        return "库存不足";
    }
    // 同步更新 MySQL
    int updated = jdbcTemplate.update(
        "UPDATE seckill_stock SET stock = stock - 1 WHERE activity_id = ? AND stock > 0",
        activityId
    );
    if (updated == 0) {
        // 回滚 Redis(可选)
        redisTemplate.opsForValue().increment("seckill:stock:" + activityId);
        return "库存不足";
    }
    // 插入订单
    jdbcTemplate.update("INSERT INTO seckill_order (activity_id, user_id, create_time) VALUES (?, ?, ?)",
        activityId, userId, System.currentTimeMillis());
    return "秒杀成功";
}
优点
  • 强一致性: Redis 和 MySQL 库存保持同步。
  • 简单: 无需异步队列。
缺点
  • 性能瓶颈: MySQL 写操作耗时,影响并发能力。
  • 回滚复杂: 如果 MySQL 更新失败,需回滚 Redis。
  • 适用场景
  • 低并发场景,或对数据一致性要求极高。

3. 延迟减扣 MySQL 库存(定时同步)

  • 时机
    • Redis 减库存后,通过定时任务(如每分钟)批量同步 MySQL 库存。
  • 流程
    1. Redis 减库存。
    2. 记录每次扣减的日志(如 Redis List seckill:stock:log)。
    3. 定时任务读取日志,批量更新 MySQL。
  • 实现示例
java 复制代码
// 秒杀接口
@PostMapping("/seckill/{activityId}")
public String seckill(@PathVariable String activityId, @RequestParam String userId) {
    if (!deductStock(activityId)) {
        return "库存不足";
    }
    // 记录日志
    redisTemplate.opsForList().leftPush("seckill:stock:log", 
        activityId + "," + userId + "," + System.currentTimeMillis());
    return "秒杀成功";
}

// 定时任务
@Component
@EnableScheduling
public class StockSyncTask {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Scheduled(fixedRate = 60000) // 每分钟
    public void syncStock() {
        List<String> logs = redisTemplate.opsForList().range("seckill:stock:log", 0, -1);
        if (logs != null && !logs.isEmpty()) {
            Map<String, Integer> stockUpdates = new HashMap<>();
            for (String log : logs) {
                String[] parts = log.split(",");
                String activityId = parts[0];
                stockUpdates.merge(activityId, 1, Integer::sum);
            }
            // 批量更新 MySQL
            for (Map.Entry<String, Integer> entry : stockUpdates.entrySet()) {
                jdbcTemplate.update(
                    "UPDATE seckill_stock SET stock = stock - ? WHERE activity_id = ?",
                    entry.getValue(), entry.getKey()
                );
            }
            redisTemplate.opsForList().trim("seckill:stock:log", logs.size(), -1); // 清空已处理日志
        }
    }
}
优点
  • 性能优化: 批量处理,减少 MySQL 频繁写。
  • 容错: 日志记录便于排查。
缺点
  • 一致性延迟: MySQL 库存更新有延迟。
  • 复杂性: 需维护日志和定时任务。
  • 适用场景
  • 中等并发,允许短暂不一致。

选择依据

方案 MySQL减库存时机 一致性 性能 复杂度 适用场景
异步减扣 Redis 后异步队列 最终一致 高并发秒杀
同步减扣 Redis 后立即同步 强一致 低并发强一致性
延迟减扣 Redis 后定时批量 延迟一致 中等并发可接受延迟

推荐方案

  • 高并发秒杀: 采用异步减扣。
    • Redis 负责实时库存控制,MySQL 异步更新。
    • 通过队列解耦,确保高吞吐量。
  • 关键点:
    • Redis Lua 脚本保证原子性。
    • 异步任务失败时,需重试或补偿(如记录失败日志)。
相关推荐
✎ ﹏梦醒͜ღ҉繁华落℘10 小时前
Visio 绘制流程图
流程图
秋95 天前
AI快速生成可编辑的流程图的方法
流程图
宁静致远20215 天前
Mermaid VSCode插件制作流程图保存方法
ide·vscode·流程图
min1811234567 天前
小型网站开发简易流程步骤 在线画图工具快速生成开发流程图方法
论文阅读·信息可视化·毕业设计·流程图·论文笔记
DYuW5gBmH8 天前
Kafka 成功消费消息的完整流程图
分布式·kafka·流程图
数说星榆18110 天前
无人员伤亡车辆事故处理流程图 快速结案流程
架构·电脑·流程图·职场发展·课程设计
dajun18112345610 天前
信息系统运维管理全流程详解 在线画图工具绘制运维流程图表技巧
运维·数据库·信息可视化·流程图·旅游·论文笔记
数说星榆18110 天前
罕见病例报告流程图学术论文用
论文阅读·人工智能·流程图
fish-man11 天前
测试流程图显示
流程图
橙色日落11 天前
Vue2 + LogicFlow 实现可视化流程图编辑功能+常用属性大全
前端·vue·流程图·logicflow