从日活百万到扛住秒杀:我的高并发实战复盘

RAG研究生
2026-03-29 18:37
阅读 1063

上个月刚从老东家离职,准备拉几个兄弟搞点自己的事情。创业前总得给自己攒点“技术弹药”,于是我回头整理了在上家公司做高并发系统时踩过的坑、熬过的夜、掉过的头发——特别是去年双11大促期间,我们那个差点把服务器干冒烟的订单服务。

说实话,当时作为技术总监,压力不是一般的大。产品经理在群里@我:“老板说今年GMV要翻三倍,系统撑不住就换人。”运维兄弟幽幽回了一句:“再加机器预算超了,财务已经把我拉黑了。”测试妹子直接甩过来一个JIRA链接:“压测5000 QPS就崩,你看着办。”

那会儿真想砸电脑。但转念一想,不就是高并发嘛?无非是缓存、队列、限流、降级那一套。可真上手才发现,理论和实践之间差了十个凌晨三点的咖啡因剂量。

为什么高并发不是“多开几台服务器”那么简单?

很多人(包括我早期)以为高并发就是堆机器。但现实很骨感:资源永远不够,成本永远敏感,而用户永远不会体谅你的架构瓶颈

我们遇到的第一个问题,是在商品详情页。看似简单的读接口,高峰期每秒请求超过8万次。数据库主库CPU飙到98%,Redis集群也扛不住穿透+击穿的双重暴击。更惨的是,因为没做熔断,下游库存服务被拖垮,整个下单链路雪崩。

这时候我才意识到:高并发不是单一组件的性能问题,而是整个链路的协同设计

缓存策略:别再用“先查DB再写缓存”了

早期我们用的是最朴素的缓存模式:

def get_product(product_id):
    data = redis.get(f"product:{product_id}")
    if not data:
        data = db.query("SELECT * FROM products WHERE id = ?", product_id)
        redis.setex(f"product:{product_id}", 300, json.dumps(data))
    return data

看起来没问题?但在高并发下,一旦缓存失效(比如TTL到了),成千上万个请求会同时打到数据库——这就是经典的缓存击穿

后来我们改成了逻辑过期 + 后台异步刷新

  • 缓存里存两个字段:dataexpire_time
  • 请求进来先拿缓存,如果 expire_time < now,则返回旧数据,同时触发一个后台任务去更新
  • 用 Redis 的 SETNX 做分布式锁,避免多个线程同时刷新

这个方案的关键在于牺牲一点数据实时性,换系统稳定性。对商品信息这种非强一致场景,完全够用。

不过写这个逻辑的时候,我差点把自己绕晕。还好有 Aider 这个神器——它能根据我的自然语言描述,直接生成带注释的 Python 代码。比如我输入:“写一个带逻辑过期和异步刷新的商品缓存类,用 Redis 和 threading”,它几秒就给我整出来了,还自动处理了异常和锁释放。

Aider 真是我最近开发效率飞升的秘密武器。以前写这种并发逻辑要反复查文档、调试死锁,现在靠精准的 Prompt 工程,直接让它“扮演资深后端工程师”,输出的代码质量堪比 senior review 过的。

接口设计:防抖、限流、降级一个都不能少

光有缓存还不够。我们发现很多流量其实是无效的——比如用户疯狂点击“立即购买”,或者爬虫扫全站。

于是我们在网关层加了三层防护:

  1. 请求防抖:同一用户 500ms 内重复请求直接返回缓存结果
  2. 令牌桶限流:基于用户 ID + 接口维度,用 Guava RateLimiter(Java)或 token-bucket(Python)实现
  3. 动态降级:当系统负载 > 80%,自动关闭非核心功能(比如个性化推荐、评论加载)

这里有个血泪教训:限流一定要做在最外层!我们一开始把限流放在业务层,结果 Nginx 还是被打满,TCP 连接耗尽。后来直接用 OpenResty 在 Lua 层做 IP 级限流,效果立竿见影。

# nginx.conf 片段
lua_shared_dict limit_req_store 10m;

access_by_lua_block {
    local limiter = require "resty.limit.req"
    local limit, err = limiter.new("limit_req_store", 10, 100) -- 10 req/sec, burst 100
    if not limit then
        ngx.log(ngx.ERR, "failed to instantiate a limiter: ", err)
        return ngx.exit(500)
    end

    local key = ngx.var.binary_remote_addr
    local delay, err = limit:incoming(key, true)
    if not delay then
        if err == "rejected" then
            return ngx.exit(429) -- Too Many Requests
        end
        ngx.log(ngx.ERR, "failed to limit req: ", err)
        return ngx.exit(500)
    end
}

数据库:分库分表不是万能药

说到数据库,很多人第一反应就是“赶紧分库分表”。但分库分表带来的复杂度是指数级上升的——跨库 JOIN、全局 ID、分布式事务……我们团队试过 ShardingSphere,结果发现维护成本太高,反而拖慢了迭代速度。

后来我们走了另一条路:读写分离 + 热点数据拆分

  • 主库只处理写请求,最多支撑 3000 TPS
  • 读请求走多个只读副本,通过 ProxySQL 自动路由
  • 对于订单这类写密集型表,我们按用户 ID 哈希,拆成 64 个物理表(但还在同一个库)

关键优化点在于批量写入。原来每次下单要插入 5 张表(订单、明细、日志、流水、优惠券),现在改成:

  1. 先写 Kafka 消息队列
  2. 异步消费,批量 insert(一次 100 条)
  3. 失败的消息进死信队列,人工介入

这样数据库写压力直接降了 70%。虽然牺牲了“下单即可见”的体验,但用户其实根本感知不到——毕竟支付成功页还要转圈 2 秒呢。

队列:用好 Kafka,但别滥用

说到 Kafka,我们曾经犯过一个低级错误:把所有异步任务都塞进去,结果 topic 膨胀到上百个,消费者组管理混乱,消息积压严重。

后来我们做了严格分级:

消息类型 Topic 命名规范 保留时间 消费者数量
核心交易 order-critical 7天 3
日志埋点 log-analytics 1天 10
通知类 notify-push 3天 5
批量任务 batch-job 30天 2

而且每个 topic 都配了监控告警:lag > 10万 或 consumer offline > 5min 就钉钉报警。运维兄弟再也不用半夜爬起来查队列了(他说请我喝了杯奶茶表示感谢)。

性能调优:从 5000 QPS 到 50000 QPS 的秘密

最后说说具体的性能数字。我们订单创建接口最初只能扛 5000 QPS,经过以下优化后达到 50000+:

  1. 连接池调优:MySQL 连接数从默认 100 调到 2000,配合 HikariCP 的 maximumPoolSize
  2. JVM 参数优化:G1GC + -XX:MaxGCPauseMillis=200
  3. 本地缓存:热点商品用 Caffeine 做 L1 缓存,减少 Redis 访问
  4. 序列化优化:JSON 改用 Protobuf,体积减少 60%
  5. 异步非阻塞:Spring WebFlux 替代传统 MVC(但注意:不是所有场景都适合!)

这里特别提一下 Prompt 工程的作用。比如我想调优 JVM,直接问 Claude:“给一个高吞吐 Java 服务的 G1GC 参数配置,要求暂停时间 < 200ms,堆内存 8GB”,它立刻给出:

-Xms8g -Xmx8g 
-XX:+UseG1GC 
-XX:MaxGCPauseMillis=200 
-XX:G1HeapRegionSize=16m 
-XX:InitiatingHeapOccupancyPercent=35

省了我翻官方文档的时间。当然,参数还是要结合实际压测调整——AI 给的是起点,不是终点。

写在最后:高并发的本质是“妥协的艺术”

折腾了半年多,我最大的感悟是:没有完美的架构,只有不断权衡的取舍

  • 要一致性还是可用性?
  • 要实时性还是吞吐量?
  • 要开发速度还是系统稳定?

创业之后,我打算把这些经验产品化。比如做一个“高并发诊断助手”,用 Aider + RAG 技术,让中小团队也能快速获得架构建议。毕竟不是每个公司都有钱养一群 P8 架构师。

对了,如果你也在搞高并发系统,欢迎交流。我已经不用再背锅了(暂时),但看到同类问题还是会手痒——可能这就是程序员的宿命吧 😅


附:关键指标对比

优化阶段 订单接口 QPS 平均延迟 (ms) 错误率 (%) DB CPU (%)
初版 4800 210 2.1 95
缓存+限流后 18000 85 0.3 65
异步+队列后 35000 62 0.1 45
全链路优化后 52000 48 0.05 38

数据不会骗人。有时候你缺的不是技术,而是一套系统性的思考框架——加上一杯续命的冰美式。

评论 0

最热最新
暂无评论
RAG研究生Lv.1
0
影响力
0
文章
0
粉丝