高并发系统设计:我在腾讯系公司被逼出来的实战心得
上周五晚上十一点,我还在公司死磕一个线上接口超时的问题。婚礼请柬刚发出去没多久,婚期就在三个月后,结果这会儿还得盯着 Grafana 上那条突兀的 99 分位延迟曲线——从平时的 80ms 突然飙到 1.2s。产品经理在群里@我说:“这个接口是用户下单的关键路径,双11前必须稳住!” 我一边回“收到”,一边默默把咖啡续上,心里只想问:谁说程序媛不能又搞技术又备婚?
我是坐标深圳的后端开发,目前在一家腾讯系创业公司做核心交易系统。平日里写 Python、调性能、怼需求,周末还要试婚纱、选婚庆、和婚策师斗智斗勇。最近因为想跳槽(没错,结婚前先涨薪),开始疯狂刷高并发相关的面试题,结果发现光背八股文根本不够——真正的挑战,永远来自生产环境那一行行不受控的流量。
今天就来聊聊高并发系统设计这件事。不讲教科书定义,只说我在项目里踩过的坑、掉过的头发,以及怎么用 Python 搞定一场真实的流量洪峰。
流量来了,系统崩了?别慌,先搞清楚“高并发”到底高在哪
很多人一听到“高并发”就想到 Redis + Kafka + Nginx 三件套,但其实高并发的核心不是堆中间件,而是理解业务瓶颈在哪。
我们系统有个典型的运营活动场景:每周五晚8点限量秒杀。去年双11期间,预估 QPS 是 5k,结果真实峰值冲到 23k!数据库连接池直接打满,MySQL CPU 飙到 100%,API 返回全是 502。
当时我真的想砸电脑。
后来复盘发现,问题出在读写耦合:前端请求一进来,先查库存(SELECT),再扣减(UPDATE),整个过程串行且强依赖 DB。更糟的是,库存表没加缓存,每次都要穿透到 MySQL。
于是我们做了第一轮优化:读写分离 + 缓存前置。
# 伪代码:用 Redis 缓存库存,避免每次查 DB
import redis
r = redis.Redis(host='cache-cluster', port=6379, decode_responses=True)
def deduct_stock(product_id: str, amount: int) -> bool:
# 先尝试从 Redis 扣减
stock_key = f"stock:{product_id}"
current = r.get(stock_key)
if current is None:
# 缓存未命中,回源 DB(这里要加分布式锁防击穿)
with db_lock(product_id):
stock = db.query("SELECT stock FROM products WHERE id = %s", product_id)
r.setex(stock_key, 300, stock) # 缓存5分钟
current = stock
if int(current) >= amount:
# Redis 原子扣减
result = r.decrby(stock_key, amount)
if result >= 0:
# 异步更新 DB(通过消息队列)
send_to_kafka("update_stock", product_id, -amount)
return True
else:
# 超卖了,回滚
r.incrby(stock_key, amount)
return False
return False
这段代码的关键点:
- 缓存兜底:避免每次请求都打 DB
- 原子操作:Redis 的
DECRBY保证线程安全 - 异步持久化:DB 更新走 Kafka,不阻塞主流程
上线后,QPS 从 23k 压到 500 左右打 DB,MySQL 压力骤降。Grafana 曲线终于平滑了,我也能安心去试婚纱了(虽然那天还是迟到了)。
面试题挑战:如何防止超卖?别只说“加锁”
说到超卖,这简直是高并发面试的必考题。我面过几家大厂,面试官最爱问:“你用什么方案防止超卖?”
很多人脱口而出:“加 Redis 分布式锁!”
但你真敢在秒杀场景用 SETNX 加锁吗?锁竞争会导致大量请求排队,响应时间爆炸,用户体验直接崩盘。
我们的做法是:无锁化 + 限流 + 队列削峰。
第一步:前端限流 + 验证码
运营同学一开始反对:“加验证码会影响转化率!”
但事实是:不加验证码,黄牛脚本就把库存抢光了,真实用户根本抢不到。最后我们折中:前 100 名免验证,后续触发图形验证码。转化率反而提升了(因为真实用户留住了)。
第二步:服务层令牌桶限流
用 Python 的 redis-py 实现令牌桶:
def acquire_token(user_id: str, rate: int = 10, capacity: int = 20) -> bool:
"""
每用户每秒最多10次请求,桶容量20
"""
key = f"rate_limit:{user_id}"
now = time.time()
pipe = r.pipeline()
pipe.zremrangebyscore(key, 0, now - 1) # 清理过期令牌
pipe.zcard(key) # 当前令牌数
pipe.zadd(key, {now: now}) # 添加新令牌
pipe.expire(key, 2)
results = pipe.execute()
current_tokens = results[1]
return current_tokens <= capacity
第三步:请求入队,异步处理
所有秒杀请求先进 Kafka,后端消费者按能力消费。这样即使瞬时 10w 请求,系统也能“细水长流”。
| 方案 | 吞吐量 | 超卖风险 | 用户体验 |
|---|---|---|---|
| 直接 DB 扣减 | 低 | 高 | 差(大量失败) |
| Redis + 锁 | 中 | 低 | 中(延迟高) |
| 缓存 + 队列 + 限流 | 高 | 极低 | 好(快速响应“排队中”) |
运营不懂技术?那就用数据说话
技术人常抱怨运营“乱提需求”,但其实高并发系统的稳定性,离不开运营的配合。
比如我们有一次事故:运营临时加了一个“分享得优惠券”活动,结果分享链接没做防刷,黑产用脚本疯狂注册领券,导致用户中心服务 OOM。
后来我们定了规矩:
- 所有运营活动必须提前一周提技术评审
- 必须提供预估流量(附历史数据)
- 关键路径必须有熔断和降级方案
现在每次开需求会,我都会甩出一张压测报告:“你看,如果不限流,DB 会在第 3 秒挂掉。” 运营同学秒懂,再也不敢随便改规则了。
Python 能扛高并发吗?别被偏见耽误了
经常有人吐槽:“Python 不适合高并发,GIL 锁死性能。”
这话对也不对。如果你用 Flask 写个单进程 API 接 1w QPS,那确实不行。但现代 Python 生态早就不止于此了。
我们的做法:
- Web 层:用 Uvicorn + FastAPI(基于 asyncio,异步非阻塞)
- 任务队列:Celery + Redis Broker
- 数据库:asyncpg(异步 PostgreSQL 驱动)
关键配置示例(Docker Compose):
# docker-compose.yml
services:
web:
image: my-fastapi-app
command: uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
environment:
- WORKERS=4
- MAX_REQUESTS=1000 # 防内存泄漏
deploy:
resources:
limits:
memory: 2G
压测结果(4 核 8G 机器):
- 单 worker:~1200 RPS
- 4 workers:~4500 RPS(近乎线性提升)
Python 的优势在于开发效率和生态。用 FastAPI 写一个带限流、认证、监控的接口,可能只要 20 行代码。省下的时间,够我去试三次婚纱了好吗!
最后一点真心话:高并发不是炫技,是责任
很多人学高并发是为了面试,为了跳槽涨薪(包括我)。但当你真正负责一个每天百万用户的系统时,你会意识到:高并发设计的本质,是对用户负责。
一次超时,可能让用户错过限量球鞋;一次超卖,可能让品牌信誉受损;一次宕机,可能让运营几个月的努力白费。
所以别只盯着 QPS 数字,多问问:
- 用户最不能忍的是什么?(通常是“一直转圈”)
- 哪里最容易出事?(往往是第三方依赖)
- 出事后怎么快速恢复?(要有开关、降级、回滚)
我现在每天上班第一件事,就是看监控大盘。不是怕背锅,而是知道背后有成千上万真实的人在用我们的产品——他们可能正在抢一张演唱会门票,或者给我未来的婚礼众筹礼物呢(好吧,这个是我瞎想的)。
结语:一边写代码,一边筹备婚礼,人生何尝不是高并发?
写这篇文章的时候,婚庆公司刚发来最终流程表,而我的 PR 也刚好被 merge。生活和工作,都是高并发系统——需要缓存(提前准备)、限流(分清优先级)、异步处理(别事事亲力亲为),最重要的是:别让自己成为瓶颈。
如果你也在备婚+coding 的路上,欢迎留言交流!顺便问一句:深圳哪家西装定制靠谱?我对象说要穿得像《硅谷》里的 Richard……(救救我)
技术可以学,头发不能少。
高并发不可怕,可怕的是没人帮你 hold 住运营的需求。

评论 0