高并发系统设计:从理论到实践——一个35岁老码农的血泪总结
上周五晚上11点,我坐在上海出租屋的书桌前,盯着屏幕上一堆goroutine leak的报错,手机突然震动。是老婆发来的消息:“这周末还回来吗?” 我看了眼日历——又是月底了。房租3500,房贷6800,孩子下个月兴趣班又要交钱。我深吸一口气,回她:“项目上线卡住了,可能回不去了。” 发完这句话,心里一沉。异地快两年了,每次说“周末见”,最后都变成“下次一定”。
而这一切的源头,就是那个该死的高并发需求。
事情得从去年十月说起。当时公司接了个大客户,要求我们的支付回调接口支持每秒5000+ QPS。老板在周会上拍桌子:“搞不定就换人!” 技术总监老张转头就甩给我:“你不是面过阿里P7吗?这种题你应该熟啊。”
我苦笑。面试题和生产环境,那能是一回事儿吗?
面试题 vs 现实:别被八股文骗了
记得去年跳槽时,某大厂二面官问我:“Go 里怎么防止 goroutine 泄漏?”
我张口就来:“用 context 控制生命周期,channel 加 select 超时,再配合 sync.WaitGroup……”
面试官点头:“不错,基础扎实。”
可现实呢?上周我查线上问题,发现一个看似完美的 context.WithTimeout,结果因为下游 Redis 连接池打满,超时根本没触发——因为 goroutine 卡在获取连接上了!理论告诉你怎么做,但没告诉你坑在哪。
更讽刺的是,我月薪从15k涨到22k,靠的就是背熟了这些“高并发八股文”。可真上战场,才发现自己像个拿着玩具枪上战场的新兵。
实战:Go 写高并发,不是光会 channel 就行
我们最初的方案很简单粗暴:收到请求 → 开 goroutine → 处理 → 返回。本地压测 2000 QPS 没问题,一上 K8s 集群,Pod 直接 OOM 杀掉。
第一个教训:无限制并发 = 自杀
我赶紧加上信号量控制:
var sem = make(chan struct{}, 1000) // 最多1000个并发
func handleRequest(w http.ResponseWriter, r *http.Request) {
select {
case sem <- struct{}{}:
defer func() { <-sem }()
// 处理逻辑
default:
http.Error(w, "Too many requests", 429)
}
}
QPS 稳住了,但延迟飙升。为啥?因为所有请求都在等信号量,队列堵死了。
第二个教训:限流不是挡箭牌,要分层
后来我们引入了 漏桶 + 令牌桶混合限流:
- 网关层(Nginx)做粗粒度限流,防DDoS
- 服务层用 Go 的
golang.org/x/time/rate做细粒度限流 - 关键路径加熔断(用 Hystrix-go)
这才稳住。但你以为这就完了?
缓存穿透、雪崩、击穿?真实场景比面试题复杂十倍
面试题最爱问:“缓存雪崩怎么办?”
答:“加随机过期时间。”
现实中呢?我们有一次 Redis 集群主从切换,所有 key 同时失效,瞬间打爆数据库。加随机过期?没用!因为失效是集群事件触发的,跟 TTL 无关。
最后怎么解决的?二级缓存 + 本地缓存兜底。用 Go 的 bigcache 或 ristretto 在每个实例内存里缓一份热点数据。哪怕 Redis 挂了,也能扛几秒——足够运维介入了。
还有一次,恶意用户用不存在的订单号疯狂刷接口,导致数据库 CPU 100%。标准答案是“布隆过滤器”。但布隆过滤器有误判率,而且动态更新成本高。我们最后用了 空值缓存 + 异步校验:查不到的数据也缓存 30 秒,同时后台任务去 DB 校验,确认不存在再延长缓存。
你看,面试题给的是理想解,现实给的是烂摊子。
和老婆的深夜电话:技术之外的压力
有天凌晨2点,我还在调 Redis Pipeline 批量写入的性能问题。手机响了,老婆声音带着哭腔:“孩子发烧了,39度,我一个人搞不定……”
我当时手抖得连键盘都按不准。那一刻,我真的想删掉所有代码,回老家开个便利店算了。
但第二天早上,我还是爬起来改代码。为什么?因为我知道,如果这个项目崩了,我的35岁简历,在招聘市场上就是一张废纸。没有选择权的人,只能把技术当救命稻草。
转折:从“背题”到“建体系”
后来我逼自己做了一件事:不再只盯着 Go 语法和并发模型,而是从系统视角看问题。
我画了一张图,贴在显示器边上:
用户请求 → CDN → API网关(限流/鉴权) → 服务层(熔断/降级) → 缓存层(多级) → 数据库(读写分离/分库分表)
每个环节都问自己三个问题:
- 它崩了怎么办?(容灾)
- 它慢了怎么办?(监控+告警)
- 它被刷爆怎么办?(弹性伸缩)
比如数据库,我不再只想着“加索引”,而是:
- 读写分离,走不同连接池
- 写操作异步化(用 Kafka 解耦)
- 热点数据提前预热
高并发不是某个语言的特性,而是一套工程体系。Go 只是工具,goroutine 再轻量,也扛不住你乱开10万个。
给后来者的建议:别做“八股文程序员”
如果你也在准备高并发面试,听我一句劝:
- 先搞懂业务场景:电商秒杀、社交Feed、支付回调……不同场景的瓶颈完全不同。别一上来就谈“百万连接”。
- 动手压测:用 wrk 或 vegeta 真实打自己的服务。你会发现,很多“理论上没问题”的代码,一压就崩。
- 监控先行:Prometheus + Grafana 必须配好。没有监控的高并发系统,就像闭着眼开车。
- 接受不完美:现实系统永远有瑕疵。重要的是快速发现、快速恢复。SLA 比 SLO 更重要。
结尾:35岁,还在写代码的老兵
写这篇文章的时候,窗外下着雨。老婆刚发消息说孩子退烧了。我松了口气,顺手把上周修复的 goroutine leak 提交了 PR。
我知道,明天还会有新的 bug,新的需求,新的焦虑。但我不再怕了。因为高并发教会我的,不是如何写更快的代码,而是如何在混乱中保持清醒。
35岁,异地,房贷,孩子的奶粉钱……这些压力不会消失。但至少,当我坐在电脑前,还能用一行行代码,给自己和家人撑起一片天。
最后送大家一句话:面试题会过时,但解决问题的能力永远不会。
共勉。

评论 0