高并发系统设计:我踩过的坑和捡到的宝

DigitalNomad
2026-01-05 23:22
阅读 1640

上周五晚上十一点半,我又在公司加班。窗外是上海陆家嘴冰冷的霓虹,桌上是第三杯冰美式,屏幕里是一堆 Too many connections 的报错日志。就在前天,产品经理轻描淡写地说:“咱们这个活动页面,双11当天预计 50w QPS,你看着搞一下。”——那一刻我真想把键盘砸他脸上。

我是谁?一个坐标上海、租住在公司隔壁小区的后端工程师,平时重度依赖 Claude 和 ChatGPT 写代码(别笑,你肯定也这样),业余喜欢研究 Go runtime、TCP 协议栈甚至区块链底层共识机制。最近正处在要不要跳槽的迷茫期——不是因为技术没成长,而是感觉每天都在给“高并发”三个字擦屁股。

所以今天,我想聊聊高并发系统设计这件事。不是教科书式的理论堆砌,而是从血泪教训中总结出的实战路径。如果你也在被流量压垮的边缘反复横跳,这篇或许能帮你少熬几个通宵。


一、高并发不是“加机器”就能解决的

很多新人(包括曾经的我)以为高并发 = 负载均衡 + Redis + MySQL 主从。结果呢?去年双11,我们一个抽奖接口直接打爆了数据库连接池,线上服务雪崩,运维在群里@所有人:“兄弟们,救火!”

问题出在哪?没有分层拆解压力

真正扛住高并发的系统,一定是分层卸载的。我的经验是:

请求进来之前就干掉它,比让它进数据库再拒绝要优雅一万倍。

具体怎么做?

  • 前端限流:按钮点击后 disable 3 秒,防止用户狂点。
  • Nginx 层限流:用 limit_req 控制单 IP 请求频率。
  • 网关层熔断:比如用 Kratos 或 go-zero 的 breaker 模块,避免下游故障扩散。
  • 业务层削峰填谷:异步化 + 消息队列(Kafka/Pulsar)缓冲突发流量。
  • 缓存兜底:Redis 缓存热点数据,甚至提前预热。

举个例子:我们后来把抽奖逻辑改成“先入队列,后台消费”,前端只返回“排队中”。QPS 从 5w 降到 2k,数据库稳如老狗。


二、Go 是高并发系统的绝佳拍档

为什么选 Go?不是因为它“云原生”,而是它的 goroutine + channel + 原生协程调度 真的香。

我们用 Go 重写了核心服务后,内存占用降了 40%,GC 停顿从 50ms 降到 2ms 以内。关键在于:

  • 轻量级协程:10w 并发连接,内存开销不到 Java 的 1/5。
  • 内置网络库net/http 性能足够好,配合 fasthttp 更猛。
  • 简洁的并发模型:用 errgroupworker pool 轻松控制并发度。

下面是一个典型的高并发接口骨架(简化版):

// 抽奖接口示例:带限流、缓存、降级
func LotteryHandler(w http.ResponseWriter, r *http.Request) {
    // 1. 用户鉴权 & 参数校验
    uid := getUserID(r)
    if !isValidUser(uid) {
        http.Error(w, "invalid user", 403)
        return
    }

    // 2. 本地缓存 + 分布式锁防刷
    key := fmt.Sprintf("lottery:%s", uid)
    if cache.Exists(key) {
        w.Write([]byte("already participated"))
        return
    }

    // 3. 异步入队(削峰)
    if err := queue.Push(LotteryTask{UID: uid}); err != nil {
        // 4. 降级策略:直接返回“稍后再试”
        log.Printf("queue full, degrade for %s", uid)
        w.Write([]byte("busy, try later"))
        return
    }

    // 5. 快速响应
    w.Write([]byte("success"))
}

注意:这里没有直接操作数据库,所有重逻辑都丢给后台 worker。这才是高并发的正确打开方式。


三、数据库:别让 SQL 成为瓶颈

再牛的 Go 服务,如果数据库没设计好,照样扑街。

我们的教训:

  • 没加索引,SELECT * FROM orders WHERE user_id = ? 全表扫描,CPU 100%
  • 事务太大,一个抽奖接口锁住整张表,其他服务全卡住
  • 没做分库分表,单表 2 亿行,慢查询日志爆炸

后来我们做了这些优化:

优化项 具体措施
读写分离 写走主库,读走从库(但注意主从延迟)
分库分表 用户 ID 哈希分 16 库,每库 64 表
缓存穿透防护 空值缓存 + BloomFilter
批量操作 插入用 INSERT INTO ... VALUES (...), (...) 而非循环单条

特别提一句:不要迷信 ORM。在高并发场景下,手写 SQL + 预编译往往更高效。我们用 sqlx 替代 GORM 后,DB CPU 降了 30%。


四、区块链?别急,它也有高并发启示

你可能会问:标题里提到区块链,这玩意儿跟高并发有啥关系?

其实,区块链的共识机制和状态同步模型,对分布式系统设计很有启发

比如:

  • 最终一致性 vs 强一致性:比特币用 PoW 达成最终一致,牺牲了 TPS 换取去中心化。而我们的电商系统,其实也可以接受“订单状态延迟 1 秒可见”。
  • 状态分片(Sharding):以太坊 2.0 的分片思路,和我们数据库分库分表本质相通——把全局状态拆成独立子集,并行处理
  • 抗女巫攻击:区块链用 PoW/PoS 防止恶意节点刷请求,而我们的 API 可以用 token bucket + 设备指纹做类似防护。

我不是说要把业务上链,而是从区块链的极端约束中,反向思考高并发的设计哲学:如何在不可靠网络、恶意节点、资源受限下,依然保证系统可用性?


五、综合:高并发不是单一技术,而是一套组合拳

最后说点实在的。高并发系统设计,从来不是某个“银弹”技术能解决的,而是工程、架构、监控、文化的综合结果

我们团队现在有一套 checklist:

压测常态化:每周用 wrk/jmeter 打一次,模拟 2 倍峰值流量
监控全覆盖:Prometheus + Grafana 看 QPS、延迟、错误率、GC
预案演练:故意 kill 服务、断网、灌满磁盘,看系统是否自愈
文档即代码:架构图、限流策略、降级开关全部写进 README

最让我感慨的是:高并发的终点,其实是“让用户感觉不到高并发”。他们点一下按钮,秒出结果,背后可能是几十个服务、上千台机器在默默协作。


结语:跳槽前,先搞定手头的“高并发”

写这篇文章时,我已经连续三天没睡好。不是因为焦虑跳槽,而是上线前的 final check 让人神经紧绷。但说真的,正是这些高压项目,才逼我真正理解了“系统设计”的含义

以前我觉得会写算法、懂源码就够了。现在明白:工程能力 = 技术深度 × 落地能力 × 故障嗅觉

如果你也在迷茫要不要跳槽,我的建议是:先把当前项目的高并发难题啃下来。等你能从容应对 10w QPS 的时候,简历自然有人抢着看。

毕竟,这个世界不缺会背八股文的人,缺的是能在线上炸了之后,还能一边喝冰美式一边 debug 的狠人。

(完)

P.S. 刚收到运维消息:新版本上线后,CPU 稳定在 30% 以下。终于可以回家睡觉了。明天?明天再说吧。

评论 0

最热最新
暂无评论
DigitalNomadLv.1
0
影响力
0
文章
0
粉丝