秒杀系统

简历描述

高并发秒杀系统 - Golang

  • 设计并实现基于 Go、Kafka、Etcd 的高并发秒杀系统,涵盖抢购下单、库存扣减、异步落库、排行榜等核心功能,单节点 QPS 3000,支持 K8s 3 节点集群部署。
  • 使用 Etcd 实现强一致分布式锁,结合 WAL 持久化记录锁状态(UUID + LeaseID),实现锁的自动续租与服务重启恢复机制,避免锁丢失与惊群问题。
  • 引入令牌桶限流与 Kafka 异步下单机制,将秒杀高峰削峰至 1000 QPS,结合 Redis 缓存库存预热策略,在3节点环境下,1000 QPS 持续 30 秒压测,总请求中约 20% 出现 429 限流,总成功率达 70%。
  • 实现 Redis ZSet 异步排行榜同步机制,使用 Kafka + Goroutine 将高并发更新操作异步解耦,排行榜更新时间控制在 100ms 以内。
  • 部署 Prometheus 和 Grafana,监控库存命中率、限流 QPS、Kafka 消费延迟等关键指标,保障系统性能与稳定。

问题 第一行

设计并实现基于 Go、Kafka、Etcd 的高并发秒杀系统,涵盖抢购下单、库存扣减、异步落库、排行榜等核心功能,单节点 QPS 3000,支持 K8s 3 节点集群部署。

1. 秒杀系统整体架构是怎样的?各组件之间如何通信?

架构总览:

  • 整体分为:网关层(限流)→ 秒杀服务(校验、锁、库存)→ 消息队列(Kafka)→ 异步落库服务 → 排行榜同步服务
  • 各模块通过 HTTP/gRPC + Kafka 实现异步解耦;
  • 读操作走 Redis 缓存,写操作通过 Kafka 异步落库,最终一致性。

通信流程简述:

  1. 用户发起秒杀请求,经网关限流(令牌桶)后到达 seckill-service
  2. seckill-service 检查库存缓存 → 加 Etcd 锁 → 原子扣减 Redis 库存;
  3. 请求成功后写入 Kafka(下单 Topic);
  4. order-consumer 消费消息并写入 MySQL;
  5. 同时 Kafka 另一路消息用于排行榜异步更新(ZSet);
  6. 所有模块接入 Prometheus,暴露 /metrics 端点用于监控。

2. 令牌桶算法是什么,解决什么问题,具体数值设置为多少

原理简述:

  • 令牌桶是流控算法之一,系统按固定速率生成令牌,请求必须获取令牌才被处理,超限请求被拒绝(返回 429);
  • 相比漏桶能容忍短期突发流量(令牌可以累积);

在本项目中:

  • 每个商品设置 1000 QPS 的令牌速率(rate = 1000/s),桶容量为 1000;
  • 使用 Redis + Lua 实现共享限流器,支持多副本共享状态;
  • 限流控制入口在网关或服务中间件(Gin 中间件);

解决的问题:

  • 避免瞬时流量压垮下游系统(Redis、MySQL);
  • 提升服务稳定性,使后端服务处理能力与流量强度解耦。

3. 单节点 QPS 3000 是如何测试的?测试工具和环境如何配置?

  • 使用k6进行压力测试,配置了 1000 个虚拟用户并发请求,最大尝试每秒 3000 次请求,持续 30 秒。测试环境为单节点部署,docker-compose 启动 Golang 服务,加上健康检查,Kafka 和 Etcd 也在同一节点上运行。
  • 单节点,i5-12600KF, 32GB DDR4 6000MHz;接口为本地网关直连(无 CDN、无反向代理),实际上线运行时为防止下游写压力,设置削峰限流至 1000 QPS。

4. Kafka 在系统中的作用具体是什么?为什么不用 RabbitMQ?

核心解耦组件:

  • 接收异步下单消息;
  • 分发排行榜更新事件;
  • 实现削峰填谷,防止高并发直接写数据库;(削峰:在流量高峰期,通过限流、排队、异步处理等方式,减少瞬时请求量,防止系统过载;填谷:在流量低谷期,利用系统空闲资源处理积压的请求,避免资源浪费。)

选择Kafka的原因:

  • 吞吐高,消息积压容忍度高
  • 持久化存储默认使用WAL,高可用
  • 分区有序适合订单类场景,RabbitMQ无强顺序保证
  • Go有sarama库支持

5. Etcd 主要做什么?为什么不选 Redis 实现锁?

etcd的作用:

  • 提供 CP(强一致)分布式锁服务
  • 使用 Put + Lease + Compare 保证锁唯一性与可恢复性;
  • 锁内容包含 UUID、商品 ID、租约 ID,配合 WAL 落盘支持自动恢复;
  • 避免惊群与锁漂移问题。

惊群问题

问题描述

当多个客户端同时竞争同一把锁时,如果锁释放,所有等待的客户端会同时被唤醒,并疯狂重试抢锁,导致:

  • 大量无效请求:只有一个客户端能成功,其余请求被拒绝,造成资源浪费。
  • 网络和 CPU 压力激增:高并发场景下可能压垮服务端。

etcd 的解决方案

etcd 通过 Lease(租约) + Watch 机制 避免惊群:

  1. 客户端抢锁失败后,不会轮询查询,而是通过 Watch 监听锁的释放事件。
  2. 锁释放时,etcd 只会通知一个等待的客户端(按 FIFO 或公平调度),其他客户端继续等待。

对比 Redis 的缺陷
Redis 的锁(如 Redlock)在释放时,所有客户端会立即重试抢锁,容易引发惊群效应。

Etcd Redis
一致性 CP 保证强一致 AP,有主从延迟
锁续约 有 Lease 机制 需手动设置过期
健康检查 原生绑定客户端 需额外处理心跳
自动恢复 Lease+WAL 可恢复 崩溃后锁状态丢失

6. 3 节点 K8s 部署是多台物理机还是单机模拟?调度策略怎么做的?

7. 下单、扣库存、落库、排行榜之间是串行的还是异步解耦的?

8. 高并发下如何避免超卖和重复下单?

9. 如何做多副本部署时的服务注册与发现?

问题 第二行

使用 Etcd 实现强一致分布式锁,结合 WAL 持久化记录锁状态(UUID + LeaseID),实现锁的自动续租与服务重启恢复机制,避免锁丢失与惊群问题。

为什么选用 Etcd 做分布式锁,而不是 Redis?

你是如何使用 LeaseID 做自动续租的?是否会续租失败?

什么是 WAL(Write-Ahead Log)?你怎么实现落盘和恢复的?

如果服务意外崩溃,如何保证锁的状态不会丢失?

什么是惊群问题?Etcd 如何避免?

你们有没有考虑锁饥饿、死锁的情况?

Lease 到期前锁失效怎么办?是否会误删其他节点的锁?

问题 第三行

引入令牌桶限流与 Kafka 异步下单机制,将秒杀高峰削峰至 1000 QPS,结合 Redis 缓存库存预热策略,在 3 节点环境下,1000 QPS 持续 30 秒压测,总请求中约 20% 出现 429 限流,总成功率达 70%。

为什么用令牌桶限流而不是漏桶?限流粒度是 IP 级还是全局?

你是如何设置令牌生成速率和桶容量的?

Kafka 下单异步流程怎么保证消息不丢?用了哪些配置?

Redis 缓存库存是怎么预热的?预热策略是什么?怎么回源?

为什么 429 限流率这么高?是否考虑更细化限流策略?

成功率 70% 是针对所有请求吗?剩下 30% 是失败还是限流?

压测用的是什么工具?并发模型是怎样的?

如果 Kafka 消费延迟了,会导致库存错误吗?如何避免重复消费?

如何保证 Kafka 消息幂等?是否使用了唯一 ID 或幂等键?

问题 第四行

实现 Redis ZSet 异步排行榜同步机制,使用 Kafka + Goroutine 将高并发更新操作异步解耦,排行榜更新时间控制在 100ms 以内。

排行榜的业务目的是什么?展示什么指标?

为什么使用 Redis ZSet?ZSet 是怎么构造 key 和 score 的?

异步同步具体是怎么做的?Kafka 消息如何转成 ZSet 操作?

Kafka 消费慢了怎么办?是否有批量处理优化?

排行榜更新频率高怎么做并发控制?

为什么控制在 100ms 以内?这个数据怎么测出来的?

Redis 高并发写入是否考虑分片或持久化压力?

问题 第五行

部署 Prometheus 和 Grafana,监控库存命中率、限流 QPS、Kafka 消费延迟等关键指标,保障系统性能与稳定。

用的哪些指标做系统监控?Prometheus 是如何采集这些指标的?

限流 QPS 如何暴露为指标?是通过 middleware 中间件采集的吗?

Kafka 消费延迟是怎么算的?是消费时间减生产时间吗?

库存命中率是如何定义和记录的?从 Redis 采集吗?

有设置告警吗?什么阈值下触发告警?

Grafana 展示的关键仪表盘有哪些?

是否有做分布式链路追踪(如 Jaeger)?对定位性能瓶颈是否有帮助?