vault backup: 2026-03-08 21:49:35

This commit is contained in:
2026-03-08 21:49:35 +08:00
parent a572699097
commit 74fc46df62
2 changed files with 944 additions and 1 deletions

View File

@@ -198,6 +198,7 @@
}, },
"active": "fcbc762a80282002", "active": "fcbc762a80282002",
"lastOpenFiles": [ "lastOpenFiles": [
"questions/07-系统设计/限购库存系统设计.md",
"questions/07-系统设计/交易撮合引擎突发流量处理.md", "questions/07-系统设计/交易撮合引擎突发流量处理.md",
"16-LeetCode Hot 100/三数之和-改进版示例.md", "16-LeetCode Hot 100/三数之和-改进版示例.md",
"算法解题思路改进方案.md", "算法解题思路改进方案.md",
@@ -228,7 +229,6 @@
"16-LeetCode Hot 100/翻转二叉树.md", "16-LeetCode Hot 100/翻转二叉树.md",
"16-LeetCode Hot 100/二叉树的最大深度.md", "16-LeetCode Hot 100/二叉树的最大深度.md",
"16-LeetCode Hot 100/二叉树的中序遍历.md", "16-LeetCode Hot 100/二叉树的中序遍历.md",
"16-LeetCode Hot 100/最小栈.md",
"16-LeetCode Hot 100", "16-LeetCode Hot 100",
"00-项目概述", "00-项目概述",
"questions/15-简历面试", "questions/15-简历面试",

View File

@@ -0,0 +1,943 @@
# 限购库存系统设计
## 题目描述
设计一个限购库存系统,用于处理商品秒杀、限购等场景。
**业务场景**
- 某商品库存有限如1000件
- 用户抢购热情高涨,短时间内大量请求
- 每个用户有购买数量限制如每人最多买3件
- 需要防止超卖、刷单、恶意攻击
**核心需求**
1. 保证库存准确性,不能超卖
2. 支持高并发QPS 达到 10万+
3. 防止用户刷单、恶意抢购
4. 保证系统可用性和数据一致性
5. 提供良好的用户体验
**问题**
1. 如何设计库存扣减方案?
2. 如何防止超卖?
3. 如何处理高并发?
4. 如何防止刷单?
5. 如何保证数据一致性?
---
## 思路推导
### 问题分析
**核心挑战**
1. **超卖问题**库存1000件卖了1200件
2. **性能问题**10万QPS下系统不崩溃
3. **公平性问题**:防止机器刷单
4. **一致性问题**:分布式环境下的数据一致性
**为什么难?**
- 高并发 + 库存扣减 = 严重的锁竞争
- 分布式环境 + 数据一致性 = CAP权衡
- 用户体验 + 安全防护 = 难以平衡
### 为什么这样思考?
**核心原则****性能 > 一致性 > 用户体验**
就像演唱会抢票一样:
- 先到先得(性能优先)
- 不允许卖出比座位多的票(准确性)
- 防止黄牛刷票(安全性)
---
## 解题思路
### 核心思想
**多级缓存 + 异步扣减 + 分布式锁 + 限流防护**
```
┌─────────────────────────────────────────────────┐
│ 用户请求 │
└──────────────────┬──────────────────────────────┘
┌─────────────────┐
│ 1. 限流防护层 │ ← 防止刷单、恶意攻击
│ - 用户限流 │
│ - IP限流 │
│ - 验证码 │
└────────┬─────────┘
┌─────────────────┐
│ 2. 预热层 │ ← 减轻数据库压力
│ - Redis库存 │
│ - 本地缓存 │
└────────┬─────────┘
┌─────────────────┐
│ 3. 防刷层 │ ← 防止机器刷单
│ - 用户行为分析 │
│ - 风控规则 │
└────────┬─────────┘
┌─────────────────┐
│ 4. 库存扣减层 │ ← 核心逻辑
│ - Redis原子操作│
│ - 分布式锁 │
│ - 数据库乐观锁 │
└────────┬─────────┘
┌─────────────────┐
│ 5. 订单处理层 │ ← 异步处理
│ - 消息队列 │
│ - 订单落地 │
└─────────────────┘
```
---
## 详细方案
### 方案一Redis + Lua 脚本(推荐)
**核心思路**:利用 Redis 的原子性操作,保证库存扣减的准确性
#### 1.1 数据结构设计
```go
// 库存信息
type StockInfo struct {
ProductID string // 商品ID
TotalStock int64 // 总库存
AvailableStock int64 // 可用库存
SoldStock int64 // 已售库存
Price float64 // 价格
LimitPerUser int // 每人限购
}
// 用户购买记录
type UserPurchase struct {
UserID string
ProductID string
Quantity int
OrderTime time.Time
}
```
#### 1.2 Redis 数据结构
```redis
# 商品库存
product:{productID}:stock = 1000
# 用户购买数量
user:{userID}:product:{productID}:qty = 2
# 购买记录(用于防刷)
product:{productID}:purchase_set = {userID1, userID2, ...}
# 分布式锁
lock:product:{productID}
```
#### 1.3 Lua 脚本实现
```lua
-- 扣减库存的 Lua 脚本
local function deduct_stock(product_id, user_id, quantity)
-- 1. 获取商品库存
local stock_key = "product:" .. product_id .. ":stock"
local stock = tonumber(redis.call('GET', stock_key))
-- 2. 检查库存是否充足
if stock < quantity then
return {err = "insufficient_stock", remaining = stock}
end
-- 3. 获取用户已购买数量
local user_qty_key = "user:" .. user_id .. ":product:" .. product_id .. ":qty"
local user_qty = tonumber(redis.call('GET', user_qty_key)) or 0
-- 4. 检查是否超过限购
local limit_key = "product:" .. product_id .. ":limit"
local limit_per_user = tonumber(redis.call('GET', limit_key)) or 1
if user_qty + quantity > limit_per_user then
return {err = "exceed_limit", user_qty = user_qty, limit = limit_per_user}
end
-- 5. 扣减库存(原子操作)
redis.call('DECRBY', stock_key, quantity)
-- 6. 更新用户购买数量
redis.call('INCRBY', user_qty_key, quantity)
redis.call('EXPIRE', user_qty_key, 3600) -- 1小时过期
-- 7. 记录购买用户
local purchase_set = "product:" .. product_id .. ":purchase_set"
redis.call('SADD', purchase_set, user_id)
redis.call('EXPIRE', purchase_set, 86400) -- 24小时过期
return {ok = true, remaining = stock - quantity}
end
-- 执行脚本
return deduct_stock(KEYS[1], ARGV[1], ARGV[2])
```
#### 1.4 Go 实现
```go
type StockService struct {
redis *redis.Client
script *redis.Script
}
func NewStockService(redis *redis.Client) *StockService {
script := redis.NewScript(`
local product_id = KEYS[1]
local user_id = ARGV[1]
local quantity = tonumber(ARGV[2])
local stock_key = "product:" .. product_id .. ":stock"
local stock = tonumber(redis.call("GET", stock_key))
if stock == nil then
return {err = "product_not_found"}
end
if stock < quantity then
return {err = "insufficient_stock", remaining = stock}
end
local user_qty_key = "user:" .. user_id .. ":product:" .. product_id .. ":qty"
local user_qty = tonumber(redis.call("GET", user_qty_key)) or 0
local limit_key = "product:" .. product_id .. ":limit"
local limit_per_user = tonumber(redis.call("GET", limit_key)) or 1
if user_qty + quantity > limit_per_user then
return {err = "exceed_limit", user_qty = user_qty, limit = limit_per_user}
end
redis.call("DECRBY", stock_key, quantity)
redis.call("INCRBY", user_qty_key, quantity)
redis.call("EXPIRE", user_qty_key, 3600)
local purchase_set = "product:" .. product_id .. ":purchase_set"
redis.call("SADD", purchase_set, user_id)
redis.call("EXPIRE", purchase_set, 86400)
return {ok = true, remaining = stock - quantity}
`)
return &StockService{
redis: redis,
script: script,
}
}
func (s *StockService) DeductStock(ctx context.Context, productID, userID string, quantity int) (*StockResult, error) {
keys := []string{productID}
values := []interface{}{userID, quantity}
result, err := s.script.Run(ctx, s.redis, keys, values).Result()
if err != nil {
return nil, err
}
// 解析结果
resultMap, ok := result.(map[string]interface{})
if !ok {
return nil, errors.New("invalid result type")
}
stockResult := &StockResult{}
if errMsg, exists := resultMap["err"]; exists {
stockResult.Error = errMsg.(string)
if errMsg == "insufficient_stock" {
stockResult.Remaining = int(resultMap["remaining"].(int64))
}
} else if _, exists := resultMap["ok"]; exists {
stockResult.Success = true
stockResult.Remaining = int(resultMap["remaining"].(int64))
}
return stockResult, nil
}
type StockResult struct {
Success bool
Error string
Remaining int
}
```
---
### 方案二:数据库乐观锁
**核心思路**:利用数据库的原子性和行锁,保证数据一致性
#### 2.1 表设计
```sql
-- 商品库存表
CREATE TABLE products (
product_id BIGINT PRIMARY KEY,
total_stock INT NOT NULL,
available_stock INT NOT NULL,
version INT NOT NULL DEFAULT 0, -- 乐观锁版本号
limit_per_user INT NOT NULL DEFAULT 1,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_available_stock (available_stock)
) ENGINE=InnoDB;
-- 用户购买记录表
CREATE TABLE user_purchases (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
product_id BIGINT NOT NULL,
quantity INT NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_user_product (user_id, product_id), -- 防止重复购买
INDEX idx_product (product_id)
) ENGINE=InnoDB;
-- 订单表
CREATE TABLE orders (
order_id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
product_id BIGINT NOT NULL,
quantity INT NOT NULL,
status TINYINT NOT NULL DEFAULT 0, -- 0:待支付, 1:已支付, 2:已取消
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX idx_user_product (user_id, product_id),
INDEX idx_status (status)
) ENGINE=InnoDB;
```
#### 2.2 乐观锁实现
```go
func (s *StockService) DeductStockWithOptimisticLock(ctx context.Context, productID, userID int64, quantity int) error {
return s.db.Transaction(ctx, func(tx *gorm.DB) error {
// 1. 查询商品信息(带锁)
var product Product
err := tx.Set("gorm:query_option", "FOR UPDATE"). // 行锁
Where("product_id = ?", productID).
First(&product).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("product not found")
}
return err
}
// 2. 检查库存
if product.AvailableStock < quantity {
return errors.New("insufficient stock")
}
// 3. 检查用户购买数量
var totalPurchase int64
err = tx.Model(&UserPurchase{}).
Where("user_id = ? AND product_id = ?", userID, productID).
Select("COALESCE(SUM(quantity), 0)").
Scan(&totalPurchase).Error
if err != nil {
return err
}
if int(totalPurchase)+quantity > product.LimitPerUser {
return errors.New("exceed purchase limit")
}
// 4. 扣减库存(乐观锁)
result := tx.Model(&Product{}).
Where("product_id = ? AND available_stock >= ? AND version = ?",
productID, quantity, product.Version).
Updates(map[string]interface{}{
"available_stock": gorm.Expr("available_stock - ?", quantity),
"version": gorm.Expr("version + 1"),
})
if result.RowsAffected == 0 {
return errors.New("stock update failed, please retry")
}
// 5. 记录购买信息
purchase := &UserPurchase{
UserID: userID,
ProductID: productID,
Quantity: quantity,
}
if err := tx.Create(purchase).Error; err != nil {
return err
}
return nil
})
}
```
**问题**:数据库压力大,不适合超高并发场景
---
### 方案三:分布式锁 + Redis
**核心思路**:使用分布式锁保证并发安全
#### 3.1 Redis 分布式锁
```go
type DistributedLock struct {
redis *redis.Client
key string
value string
expire time.Duration
}
func (d *DistributedLock) Lock(ctx context.Context) error {
// 使用 SETNX 实现
ok, err := d.redis.SetNX(ctx, d.key, d.value, d.expire).Result()
if err != nil {
return err
}
if !ok {
return errors.New("lock already held")
}
return nil
}
func (d *DistributedLock) Unlock(ctx context.Context) error {
// 使用 Lua 脚本保证原子性
script := `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
end
return 0
`
_, err := d.redis.Eval(ctx, script, []string{d.key}, []string{d.value}).Result()
return err
}
// 使用分布式锁扣减库存
func (s *StockService) DeductStockWithLock(ctx context.Context, productID, userID string, quantity int) error {
lockKey := fmt.Sprintf("lock:product:%s", productID)
lock := &DistributedLock{
redis: s.redis,
key: lockKey,
value: userID,
expire: 5 * time.Second,
}
// 获取锁
if err := lock.Lock(ctx); err != nil {
return err
}
defer lock.Unlock(ctx)
// 扣减库存逻辑
stockKey := fmt.Sprintf("product:%s:stock", productID)
result, err := s.redis.DecrBy(ctx, stockKey, quantity).Result()
if err != nil {
return err
}
if result < 0 {
return errors.New("insufficient stock")
}
return nil
}
```
---
### 方案四:限流防护
**核心思路**:在扣减库存前进行多层限流
#### 4.1 用户级限流
```go
type RateLimiter struct {
redis *redis.Client
}
// 滑动窗口限流
func (r *RateLimiter) AllowUser(ctx context.Context, userID string, maxRequests int, window time.Duration) bool {
key := fmt.Sprintf("ratelimit:user:%s", userID)
now := time.Now().UnixNano()
windowStart := now - window.Nanoseconds()
// 使用 Redis Sorted Set 实现滑动窗口
pipe := r.redis.Pipeline()
// 移除窗口外的记录
pipe.ZRemRangeByScore(ctx, key, "0", strconv.FormatInt(windowStart, 10))
// 添加当前请求
pipe.ZAdd(ctx, key, redis.Z{Score: float64(now), Member: userID})
// 设置过期时间
pipe.Expire(ctx, key, window)
// 统计窗口内请求数
pipe.ZCard(ctx, key)
results, err := pipe.Exec(ctx)
if err != nil {
return false
}
count := results[3].(*redis.IntCmd).Val()
return count <= int64(maxRequests)
}
```
#### 4.2 验证码
```go
func (s *StockService) VerifyCaptcha(ctx context.Context, userID, ticket, captcha string) bool {
key := fmt.Sprintf("captcha:%s:%s", userID, ticket)
stored, err := s.redis.Get(ctx, key).Result()
if err != nil {
return false
}
// 验证码一次性使用
s.redis.Del(ctx, key)
return stored == captcha
}
```
---
### 方案五:防刷策略
**核心思路**:多维度识别和防止机器刷单
#### 5.1 用户行为分析
```go
type AntiSpamService struct {
redis *redis.Client
}
// 检查用户行为风险
func (a *AntiSpamService) CheckUserRisk(ctx context.Context, userID string) (risk float64, reason string) {
risk := 0.0
// 1. 检查购买频率
purchaseCount, _ := a.redis.Get(ctx, fmt.Sprintf("user:%s:purchase_count:1h", userID)).Int()
if purchaseCount > 100 {
risk += 0.5
reason = "高频购买"
}
// 2. 检查注册时间
registerTime, _ := a.redis.Get(ctx, fmt.Sprintf("user:%s:register_time", userID)).Int64()
if time.Now().Unix()-registerTime < 86400 { // 注册不到1天
risk += 0.3
if reason == "" {
reason = "新用户"
}
}
// 3. 检查设备指纹
deviceCount, _ := a.redis.Get(ctx, fmt.Sprintf("user:%s:device_count", userID)).Int()
if deviceCount > 5 {
risk += 0.4
if reason == "" {
reason = "多设备"
}
}
// 4. 检查IP关联账号数
ipKey := fmt.Sprintf("ip:%s:user_count", getUserIP(ctx))
ipUserCount, _ := a.redis.Get(ctx, ipKey).Int()
if ipUserCount > 10 {
risk += 0.3
if reason == "" {
reason = "同IP多账号"
}
}
return risk, reason
}
// 是否允许购买
func (a *AntiSpamService) AllowPurchase(ctx context.Context, userID string) (bool, string) {
risk, reason := a.CheckUserRisk(ctx, userID)
if risk > 0.7 {
// 高风险:直接拒绝
return false, "高风险用户:" + reason
}
if risk > 0.4 {
// 中风险:要求验证码
return false, "需要验证码"
}
return true, ""
}
```
#### 5.2 黑名单机制
```go
type BlacklistService struct {
redis *redis.Client
}
// 检查是否在黑名单
func (b *BlacklistService) IsBlacklisted(ctx context.Context, userID string) (bool, string) {
// 1. 用户黑名单
if exists, _ := b.redis.SIsMember(ctx, "blacklist:user", userID).Result(); exists {
return true, "用户黑名单"
}
// 2. IP黑名单
ip := getUserIP(ctx)
if exists, _ := b.redis.SIsMember(ctx, "blacklist:ip", ip).Result(); exists {
return true, "IP黑名单"
}
// 3. 设备黑名单
deviceID := getDeviceID(ctx)
if exists, _ := b.redis.SIsMember(ctx, "blacklist:device", deviceID).Result(); exists {
return true, "设备黑名单"
}
return false, ""
}
```
---
### 方案六:库存预加载与预热
**核心思路**:提前将库存加载到 Redis减轻数据库压力
#### 6.1 预热库存
```go
func (s *StockService) PreloadStock(ctx context.Context, productID string) error {
// 1. 从数据库加载库存
var product Product
err := s.db.Where("product_id = ?", productID).First(&product).Error
if err != nil {
return err
}
// 2. 加载到 Redis
stockKey := fmt.Sprintf("product:%s:stock", productID)
limitKey := fmt.Sprintf("product:%s:limit", productID)
pipe := s.redis.Pipeline()
pipe.Set(ctx, stockKey, product.AvailableStock, 24*time.Hour)
pipe.Set(ctx, limitKey, product.LimitPerUser, 24*time.Hour)
// 3. 加载用户购买记录
var purchases []UserPurchase
s.db.Where("product_id = ? AND created_at > ?", productID, time.Now().Add(-24*time.Hour)).
Find(&purchases)
for _, purchase := range purchases {
userQtyKey := fmt.Sprintf("user:%d:product:%s:qty", purchase.UserID, productID)
pipe.Set(ctx, userQtyKey, purchase.Quantity, time.Hour)
}
_, err = pipe.Exec(ctx)
return err
}
// 批量预热多个商品
func (s *StockService) PreloadStocks(ctx context.Context, productIDs []string) error {
for _, productID := range productIDs {
if err := s.PreloadStock(ctx, productID); err != nil {
log.Errorf("Failed to preload stock for product %s: %v", productID, err)
}
}
return nil
}
```
#### 6.2 定时同步
```go
// 定时将 Redis 中的数据同步回数据库
func (s *StockService) SyncToDatabase(ctx context.Context) error {
// 1. 获取所有商品库存
keys, _, err := s.redis.Scan(ctx, "product:*:stock").Result()
if err != nil {
return err
}
for _, key := range keys {
// 2. 获取 Redis 中的库存
stock, _ := s.redis.Get(ctx, key).Int()
// 3. 提取 productID
productID := strings.TrimPrefix(key, "product:")
productID = strings.TrimSuffix(productID, ":stock")
// 4. 更新数据库
s.db.Model(&Product{}).
Where("product_id = ?", productID).
Update("available_stock", stock)
}
return nil
}
```
---
### 方案七:订单异步处理
**核心思路**:库存扣减后,异步创建订单
#### 7.1 消息队列
```go
type OrderMessage struct {
OrderID string `json:"order_id"`
UserID string `json:"user_id"`
ProductID string `json:"product_id"`
Quantity int `json:"quantity"`
Timestamp time.Time `json:"timestamp"`
}
func (s *StockService) DeductStockAsync(ctx context.Context, productID, userID string, quantity int) (*OrderResult, error) {
// 1. 扣减库存(同步)
result, err := s.DeductStock(ctx, productID, userID, quantity)
if err != nil {
return nil, err
}
// 2. 发送订单创建消息(异步)
orderID := generateOrderID()
orderMsg := &OrderMessage{
OrderID: orderID,
UserID: userID,
ProductID: productID,
Quantity: quantity,
Timestamp: time.Now(),
}
msgBytes, _ := json.Marshal(orderMsg)
if err := s.kafka.SendMessage("orders", msgBytes); err != nil {
// 消息发送失败,回滚库存
s.rollbackStock(ctx, productID, userID, quantity)
return nil, err
}
return &OrderResult{
OrderID: orderID,
Success: true,
}, nil
}
func (s *StockService) rollbackStock(ctx context.Context, productID, userID string, quantity int) {
stockKey := fmt.Sprintf("product:%s:stock", productID)
userQtyKey := fmt.Sprintf("user:%s:product:%s:qty", userID, productID)
pipe := s.redis.Pipeline()
pipe.IncrBy(ctx, stockKey, quantity)
pipe.DecrBy(ctx, userQtyKey, quantity)
pipe.Exec(ctx)
}
```
---
## 实战案例
### 案例1秒杀活动
**场景**
- 商品iPhone 15 Pro库存1000台
- 限购每人1台
- 预估QPS10万
**处理流程**
```
1. 活动前预热T-10分钟
├─ 预热库存到 Redis
├─ 预热用户购买记录
└─ 启动监控告警
2. 活动开始T=0
├─ 10万QPS 请求涌入
├─ 第一层用户限流每秒1次
│ └─ 拦截 90% 请求 → 剩余 1万QPS
├─ 第二层:验证码验证
│ └─ 拦截 50% 请求 → 剩余 5千QPS
├─ 第三层Redis Lua 扣减库存
│ └─ 原子操作,保证准确
└─ 第四层:异步创建订单
└─ Kafka 消息队列
3. 活动进行中T+1分钟
├─ 库存1000 → 0
├─ Redis 压力:正常
├─ 数据库压力:低(异步写入)
└─ 订单队列5000 个待处理
4. 活动结束后
├─ 停止接收新订单
├─ 处理剩余订单
├─ 同步数据到数据库
└─ 生成销售报表
```
### 案例2黄牛刷单检测
**场景**
- 某用户使用脚本在1秒内发起100次请求
- IP地址相同
- 设备指纹相同
**处理流程**
```
1. 监控检测异常
├─ 用户购买频率100次/秒
├─ IP地址单一来源
└─ 触发告警
2. 风控规则匹配
├─ 规则11分钟内购买>10次 → 高风险
├─ 规则2同IP多账号>5个 → 高风险
└─ 规则3新用户大额购买 → 中风险
3. 自动处理
├─ 加入黑名单24小时
├─ 冻结订单
├─ 释放库存
└─ 通知风控团队
4. 人工审核
├─ 检查用户历史行为
├─ 确认是否为恶意刷单
└─ 决定是否永久封禁
```
---
## P7 加分项
### 深度理解
1. **为什么选择 Redis 而不是数据库?**
- 性能Redis QPS 可达 10万+MySQL 只有几千
- 原子性Redis Lua 脚本保证原子操作
- 轻量级内存操作比磁盘IO快得多
2. **如何保证 Redis 和数据库一致性?**
- 最终一致性:允许短暂的不一致
- 定时同步:定时将 Redis 数据同步到数据库
- 对账系统:定时比对 Redis 和数据库数据
- 补偿机制:发现不一致时自动修复
3. **如何处理网络分区?**
- CAP权衡选择 AP可用性 + 分区容错)
- Redis Cluster多主复制保证高可用
- 降级策略Redis 不可用时降级为数据库
### 实战扩展
**相关技术**
- Redis缓存、分布式锁
- Kafka消息队列、异步处理
- Hystrix熔断器
- Sentinel限流熔断
- Prometheus监控告警
**最佳实践**
1. 预热:提前加载库存到 Redis
2. 限流:多层限流保护
3. 异步:订单异步处理
4. 监控:实时监控告警
5. 降级:异常情况降级处理
### 变体问题
1. **如何支持多种商品同时秒杀?**
- 为每个商品单独设置库存 key
- 使用 Redis Cluster 分片
- 不同商品使用不同的 key 前缀
2. **如何处理库存回滚?**
- 订单超时未支付自动回滚
- 使用延迟消息队列
- 设置订单 TTL
3. **如何支持预约抢购?**
- 预售阶段:只允许预约,不扣减库存
- 抢购阶段:按预约顺序扣减库存
- 使用 Sorted Set 实现排队
4. **如何防止黄牛倒卖?**
- 实名制购买
- 限制转卖时间如7天内不可转卖
- 价格波动策略(价格逐步上涨)
---
## 总结
**核心要点**
1. **Redis + Lua**:原子操作,保证库存准确性
2. **多层限流**:防止系统过载
3. **异步处理**:订单异步创建,提高性能
4. **防刷策略**:多维度识别刷单行为
5. **预热机制**:提前加载库存,减轻压力
**技术栈**
- 缓存Redis库存、用户记录
- 消息队列Kafka异步订单
- 限流Redis + 滑动窗口
- 分布式锁Redis SETNX
- 监控Prometheus + Grafana
**关键指标**
- QPS10万+
- 库存准确性100%
- 响应时间P99 < 100ms
- 系统可用性99.99%
**易错点**
- 忘记使用 Lua 脚本导致并发问题
- 过度依赖数据库导致性能瓶颈
- 忽视防刷策略导致黄牛刷单
- 没有降级预案导致系统雪崩
- 忘记异步处理导致响应慢