高并发系统设计:从理论到实践
上周五晚上十点半,我还在公司调接口压测数据。空调早就停了,办公室只剩下我和隔壁组那个“卷王”小李。他端着保温杯走过来,笑嘻嘻地问我:“你这个 Java 服务,QPS 到底能撑多少?”
我瞥了一眼监控面板上飘红的线程池拒绝数,内心一万只草泥马奔腾而过——这项目是去年双11前临时被老板拍脑袋接下的,说是要“打造我们传统制造企业数字化转型标杆”。结果呢?需求文档三天两头改,测试环境跑不通,运维大哥还老吐槽我们“Java 应用吃内存比食堂阿姨打菜还狠”。
我是成都一家传统制造业企业的后端开发,坐标高新区某老旧写字楼。平时写 Java 多,但也经常用 Python 写脚本、做数据清洗或者搭个临时 mock 服务。最近一年,重度依赖 ChatGPT 和 Claude 辅助开发(别笑,谁还没个 AI 搭子),尤其在搞高并发这块,光靠自己啃书早秃了。
被逼上梁山:一个“伪秒杀”需求
事情起因是我们要做一个内部员工福利商城。听起来人畜无害对吧?但产品经理一句“参考淘宝双11”,直接把我们推进了火坑。预估峰值并发 5000+,数据库读写比 8:2,最关键的是——上线时间卡死在两周后!
当时我第一反应是:这不就是个披着“电商”外衣的秒杀系统吗?只不过用户量小点,但对我们这种常年处理 ERP、MES 系统的传统企业来说,已经是“高并发天花板”了。
于是,我翻出尘封已久的《高性能MySQL》和《分布式系统原理》,又偷偷问 ChatGPT 要了个架构草图,开始硬着头皮上。
架构拆解:别一上来就堆中间件
很多新人一听到高并发,立马想到 Redis、Kafka、分库分表……但现实很骨感——我们连 Docker 都没全量上,运维只认 Nginx + Tomcat。所以第一步不是炫技,而是控制流量入口。
1. 前端限流 + Nginx 层过滤
我们前端用了 Vue,加了个简单的按钮防重(点击后禁用 3 秒)。但这远远不够。我在 Nginx 层加了限流配置:
http {
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server {
location /api/v1/order {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://backend;
}
}
}
解释一下:每个 IP 每秒最多 10 个请求,突发可缓存 20 个。超了直接返回 503。上线第一天,运维就夸我“终于没让他半夜爬起来看报警”。
2. 服务层:异步化 + 缓存穿透防护
核心下单接口用 Java 写的(Spring Boot + MyBatis)。一开始我傻乎乎地同步写 DB,结果 JMeter 一压,DB 连接池直接爆了。
后来改成 Redis 预减库存 + MQ 异步入库。流程如下:
- 用户请求 → 校验参数
- Redis 扣减库存(原子操作
DECR) - 若成功,发消息到 RabbitMQ
- 消费者异步写 MySQL,并最终一致性校验
关键代码片段(简化版):
// Redis 扣库存
Long stock = redisTemplate.opsForValue().decrement("stock:" + skuId);
if (stock != null && stock >= 0) {
// 发送 MQ
rabbitTemplate.convertAndSend("order.queue", orderId);
return "success";
} else {
// 库存不足
return "sold out";
}
但这里有个经典坑:缓存穿透。如果某个 SKU 不存在,每次请求都会打到 DB。解决方案?布隆过滤器 or 缓存空值。我们选了后者,因为简单:
// 查询商品信息
Object product = redisTemplate.opsForValue().get("product:" + id);
if (product == null) {
Product dbProduct = productMapper.selectById(id);
if (dbProduct != null) {
redisTemplate.opsForValue().set("product:" + id, dbProduct, 10, TimeUnit.MINUTES);
} else {
// 缓存空值,防止穿透
redisTemplate.opsForValue().set("product:" + id, "", 2, TimeUnit.MINUTES);
}
}
数据库:别让 MySQL 成瓶颈
我们用的还是 MySQL 5.7(别问,问就是“稳定”)。高并发下,最怕慢查询拖垮整个实例。
- 读写分离:主库写,从库读。但注意主从延迟!下单后的查询我强制走主库。
- 索引优化:
order(user_id, create_time)这种组合索引救了命。 - 分页陷阱:深分页用
WHERE id > last_id LIMIT 10替代OFFSET。
另外,我写了个 Python 脚本每天凌晨分析 slow log:
# analyze_slow_log.py
import re
def parse_slow_log(file_path):
with open(file_path) as f:
for line in f:
if "Query_time" in line:
# 提取耗时 > 1s 的 SQL
match = re.search(r"Query_time: (\d+\.\d+)", line)
if float(match.group(1)) > 1.0:
print(f"SLOW QUERY: {line.strip()}")
运维看到后惊了:“你还会 Python?我以为你们 Java 狗只会 new 对象。”
监控与兜底:线上事故教会我的事
上线前三天,压测突然崩了。日志里全是 RejectedExecutionException。一查,线程池配置太保守:
# application.yml
thread-pool:
core-size: 10
max-size: 20
这哪够?我立马调整为:
| 参数 | 原值 | 调整后 |
|---|---|---|
| corePoolSize | 10 | 50 |
| maxPoolSize | 20 | 200 |
| queueCapacity | 100 | 1000 |
同时加了 Hystrix 熔断(虽然现在 Spring Cloud CircuitBreaker 更流行,但我们技术栈老):
@HystrixCommand(fallbackMethod = "placeOrderFallback")
public String placeOrder(OrderRequest req) {
// 正常逻辑
}
public String placeOrderFallback(OrderRequest req) {
// 返回友好提示,避免雪崩
return "系统繁忙,请稍后再试";
}
效果如何?
上线当天,峰值 QPS 达到 6200,系统稳如老狗。DB CPU 最高 45%,Redis 命中率 98.7%。老板在群里发了个 200 块红包,说“数字化转型初见成效”。
更爽的是,测试妹子第一次没在上线后找我吵架。
一点心得(带点自嘲)
- 高并发不是堆技术,是控制的艺术。限流、降级、异步,比盲目上微服务实在得多。
- 传统企业别追求一步到位。我们没用 Kafka,没分库分表,一样扛住了。先跑起来,再优化。
- Python 是 Java 程序员的秘密武器。写监控、刷数据、自动化部署,比 Shell 好使多了。
- ChatGPT 真香,但别全信。它给我生成的 Redis Lua 脚本有一次漏了
KEYS参数,差点酿成事故。
最后,想对和我一样的传统企业开发者说:别觉得自己“土”。我们可能没有大厂的 fancy 架构,但在有限资源下解决问题的能力,才是真正硬核的技术实力。
对了,这篇文章的初稿就是让 Claude 帮我润色的——别告诉领导,就说是我熬夜写的 😏
技术分享小彩蛋:如果你也在传统企业搞高并发,欢迎留言交流!我们可以建个“传统企业 Java 联盟”,一起对抗产品经理的奇思妙想和运维的“稳定压倒一切”。

评论 0