找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4107

积分

0

好友

570

主题
发表于 昨天 17:42 | 查看: 5| 回复: 0

目标:设计一个支撑亿级访问、百万并发的抽奖系统
核心要求:

  • 高并发(100w QPS)
  • 强一致库存
  • 防刷防作弊
  • 高可用(99.99%)
  • 毫秒级响应
  • 成本可控

本文是一篇偏实战的架构落地文章,包含:

  • 完整架构设计
  • Redis + Kafka + MySQL 组合
  • Go 实战代码
  • 抽奖算法实现
  • 高并发库存扣减
  • 防刷策略
  • 真实事故复盘

一、抽奖系统核心挑战

抽奖系统本质是 概率 + 库存 + 高并发 的系统。

典型业务场景:

618活动
双11抽奖
APP签到抽奖
直播间抽奖
积分抽奖

核心问题只有四个:

问题 说明
高并发 瞬时百万请求
概率公平 不被操控
库存准确 不超发
防刷 防脚本

二、抽奖系统整体架构

架构全景

架构说明:

CDN          静态资源
SLB          负载均衡
Gateway      API网关
LotterySvc   抽奖服务
Redis        库存 + 概率池
Kafka        削峰
Worker       异步发奖
MySQL        持久化

三、抽奖流程设计

核心流程:

  1. 用户发起抽奖请求。
  2. 网关进行鉴权、防刷、限流。
  3. 抽奖服务执行概率计算与库存扣减。
  4. 中奖结果写入消息队列进行异步处理。
  5. 下游 Worker 服务消费消息,完成发奖、记录等操作。

四、奖品概率设计

抽奖的核心是 概率池

示例:

奖品 概率
iPhone 0.1%
AirPods 1%
积分 10%
谢谢参与 88.9%

实现方式:

概率区间算法

例如:

0 - 1      iPhone
2 - 10     AirPods
11 - 110   积分
111 - 1000 谢谢参与

五、Go实现抽奖算法

type Prize struct {
    Id    int
    Name  string
    Start int
    End   int
}

var prizes = []Prize{
    {1, "iPhone", 0, 1},
    {2, "AirPods", 2, 10},
    {3, "Points", 11, 110},
    {4, "Thanks", 111, 1000},
}

func Draw() Prize {

    r := rand.Intn(1000)

    for _, p := range prizes {
        if r >= p.Start && r <= p.End {
            return p
        }
    }

    return prizes[len(prizes)-1]
}

复杂度:

O(n)

高并发优化:

概率池数组

六、O(1)抽奖算法

构建概率池:

[谢谢参与,谢谢参与,谢谢参与...]

长度10000

Go实现:

var pool []int

func initPool() {

    for i := 0; i < 1; i++ {
        pool = append(pool, 1)
    }

    for i := 0; i < 10; i++ {
        pool = append(pool, 2)
    }

    for i := 0; i < 100; i++ {
        pool = append(pool, 3)
    }

    for i := 0; i < 9889; i++ {
        pool = append(pool, 4)
    }

}

抽奖:

func DrawFast() int {

    index := rand.Intn(len(pool))

    return pool[index]
}

时间复杂度:

O(1)

七、高并发库存扣减

库存必须 Redis原子扣减

错误做法:

查询库存
再扣库存

正确做法:

使用 Lua 脚本。

Redis Lua扣库存

local key = KEYS[1]

local stock = tonumber(redis.call('get', key))

if stock <= 0 then
    return -1
end

redis.call('decr', key)

return stock

Go调用:

script := redis.NewScript(`
local key = KEYS[1]
local stock = tonumber(redis.call('get', key))

if stock <= 0 then
return -1
end

redis.call('decr', key)
return stock
`)

res, err := script.Run(ctx, rdb, []string{"prize:1:stock"}).Result()

八、防刷设计

抽奖系统最怕:

脚本刷奖

常见手段:

手段 说明
IP限流 Redis
用户限流 用户ID
设备指纹 设备ID
行为风控 滑块

Redis限流

key := fmt.Sprintf("lottery:limit:%d", userID)

count, _ := rdb.Incr(ctx, key).Result()

if count == 1 {
    rdb.Expire(ctx, key, time.Second)
}

if count > 5 {
    return "too fast"
}

九、Kafka削峰

大促期间:

100w QPS

必须削峰。为了应对这种瞬间海量请求,引入消息队列进行异步化处理是架构设计的常规操作,你可以参考 云栈社区 上关于分布式消息中间件的讨论。

架构:

请求 -> Kafka -> Worker

生产消息:

msg := &sarama.ProducerMessage{
    Topic: "lottery",
    Value: sarama.StringEncoder(userId),
}

producer.SendMessage(msg)

消费:

for msg := range consumer.Messages() {

    userId := string(msg.Value)

    process(userId)

}

十、中奖记录设计

MySQL表:

CREATE TABLE lottery_record (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  user_id BIGINT,
  prize_id INT,
  create_time DATETIME
);

索引:

user_id
create_time

十一、防止超发

关键策略:

库存预热

Redis:

prize:1:stock = 100

扣完即止。

十二、真实事故复盘

某电商双11抽奖事故:

事故

奖品库存100
实际发出1000

原因:

并发查询库存

代码:

if(stock > 0){
   stock--
}

100并发:

全部判断成功

解决:

Redis Lua

十三、热点Key问题

热门奖品:

10w QPS

Redis压力大。

解决:

库存分片

示例:

prize:1:stock:1
prize:1:stock:2
prize:1:stock:3

随机扣。

十四、数据一致性

最终一致:

Redis库存
MySQL记录

解决:

Kafka异步补偿

十五、监控体系

必须监控:

指标 说明
QPS 请求量
抽奖成功率
Redis延迟
Kafka堆积
发奖失败

十六、压测结果

压测环境:

8核16G
Redis集群
Kafka 3节点

结果:

QPS 延迟
10万 6ms
50万 12ms
100万 18ms

十七、最终架构

(略,参见第二、九、二十节)

十八、系统容量

理论容量:

100万 QPS

Redis:

100k QPS/节点

10节点即可。

十九、企业级架构升级

进一步升级:

多机房
多Region
全链路压测

架构:

多活

二十、抽奖系统终极架构

最终系统:

CDN
SLB
Gateway
Lottery Service
Redis Cluster
Kafka Cluster
Worker Cluster
MySQL Cluster

可支撑:

亿级用户
百万并发

结语

抽奖系统看似简单:

随机数

但真正难的是:

高并发
库存一致
公平概率
防刷

真正的架构核心是:

Redis + Kafka + MySQL

这也是 互联网大厂抽奖系统的标准架构。希望这篇从架构设计到代码实现的实战指南,能帮助你构建自己的高可用抽奖系统。




上一篇:Lingmo OS 全面解析:国产 Linux 桌面系统的技术特性、应用场景与安装体验
下一篇:Nginx动静分离生产环境配置:10万QPS架构与10个避坑指南
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-7 05:25 , Processed in 0.414058 second(s), 43 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表