高并发系统设计:从理论到实践——一个Spark老油条的血泪复盘
嗨,各位码友。我是阿哲,在大数据领域摸爬滚打三年,日常和 Spark 打交道打得比女朋友还多(别问,问就是远程办公+单身狗)。平时除了在公司搞离线数仓、实时计算这些“正经活”,我还热衷参加各种技术分享会,偶尔也研究点前端动画玩玩——比如用 GSAP 做个粒子雨,或者用 Three.js 搞个3D地球转圈圈。虽然产品经理总说我“不务正业”,但谁让我对交互和视觉有执念呢?
今天这篇不是讲 Spark 的(虽然我真的很想吹一波 Structured Streaming 的背压机制),而是想聊聊高并发系统设计。为啥?因为上个月面试某大厂时,被一道“如何设计一个支持百万 QPS 的秒杀系统?”直接干懵了。回去后痛定思痛,结合自己去年参与的一个区块链数据聚合项目实战经验,决定写点干货,顺便帮正在求职的兄弟们避坑。
起因:被面试官按在地上摩擦
事情得从上个月说起。我在 BOSS 上看到一家 Web3 初创公司招后端,薪资给得挺狠,JD 里赫然写着“熟悉高并发、分布式系统设计”。我当时心想:“小意思,天天处理 TB 级数据流,还怕你区区百万请求?”
结果面试官上来就问:
“假设我们要做一个 NFT 交易市场,用户在开售瞬间疯狂抢购,系统要支撑 50 万 QPS,你怎么设计?”
我嘴一瓢:“用 Redis 缓存库存,加分布式锁……”
“Redis 单实例能扛 10 万 QPS 吗?锁竞争怎么解决?超卖怎么办?数据库怎么扛住回源压力?”
我:……(大脑宕机)
那一刻,我仿佛看到了自己被拒的邮件标题:“感谢您的时间,但……”
回家路上我就意识到一个问题:大数据开发≠高并发系统设计。我天天调 Spark 参数、优化 Shuffle、处理 Kafka 积压,但真到了面向 C 端用户的高并发场景,很多细节根本没碰过。
于是,我翻出去年做的一个项目——一个基于区块链的链上数据聚合服务,正好涉及高并发读写,今天就拿它当案例,复盘一下从理论到落地的全过程。
项目背景:链上数据 API 的“流量海啸”
我们团队去年接了个需求:为某 DeFi 协议提供实时 NFT 持仓查询接口。用户通过钱包地址,能查到他在某个 NFT 合约下的所有资产。
听起来简单?但问题来了:
- 区块链数据是公开的,但查询速度慢(以太坊 RPC 调一次可能几百毫秒)
- 用户量爆发式增长,尤其在项目方空投前夕,QPS 直接飙到 20 万+
- 我们的 API 是免费开放的,意味着谁都可能来刷(包括机器人)
最惨的是去年 11 月某天晚上 8 点,一个热门 NFT 项目空投,我们的服务直接 503。监控报警响成一片,运维同事在 Slack 里疯狂@我:“阿哲!快看看!API 又崩了!”
我当时正撸着代码听 Lo-fi,看到消息差点把键盘砸了——又双叒叕是数据库被打爆了。
实战拆解:四层防线扛住流量洪峰
第一关:流量削峰 + 网关限流
首先得承认,不是所有请求都值得处理。很多是爬虫、脚本、恶意刷单。所以第一件事:在入口做拦截。
我们用了 Kong 网关 + 自研限流插件(基于令牌桶算法),配置如下:
rate_limiting:
minute: 60 # 每分钟最多 60 次
burst: 10 # 允许突发 10 次
key: ip # 按 IP 限流
但光这样不够。高峰期合法用户也会被误杀。于是我们加了动态权重机制:
- 正常用户:限流宽松
- 已知机器人 IP(从历史日志聚类得出):直接 block
- 新 IP 首次访问:走验证码流程(用 hCaptcha,防绕过)
吐槽一句:产品经理一开始非要“无感体验”,不让加验证码。结果上线第一天就被 DDoS,最后灰溜溜同意了。
第二关:缓存穿透 & 击穿防御
用户查一个钱包地址,如果缓存没命中,就得去查链上数据——这一步最耗时。更要命的是,攻击者可以故意查询不存在的地址,导致每次都要穿透到后端。
我们做了三重防护:
- 布隆过滤器(Bloom Filter):提前加载所有已知有效钱包地址(每天凌晨用 Spark 任务跑全量扫描,输出到 Redis)。查询前先过 BF,无效地址直接返回
{ "data": null }。 - 空值缓存:对查询不到的结果,也缓存 5 分钟(带特殊标记),避免重复穿透。
- 热点 Key 自动发现:用 Redis 的
KEYS太危险,我们改用 Redis Streams + 异步统计模块,每 10 秒聚合一次访问频次,超过阈值的 Key 自动提升为“热点”,走本地缓存(Caffeine)。
// 伪代码:热点 Key 本地缓存
if (hotKeyDetector.isHot(walletAddress)) {
return localCache.get(walletAddress);
} else {
return redisCache.get(walletAddress);
}
有个坑:一开始用 Guava Cache,结果 OOM 了。后来换成 Caffeine,内存占用降了 60%,真香。
第三关:异步化 + 最终一致性
链上数据更新是有延迟的(区块确认需要时间)。我们没必要强一致。于是大胆采用 “写时更新 + 读时补偿” 策略:
- 用户查数据 → 返回缓存(可能旧)
- 后台有个 Spark Streaming 作业,监听区块链事件(通过 WebSocket 接入 Alchemy),一旦发现新交易,就异步更新缓存和 DB
- 如果用户查到的数据“看起来不对”(比如刚转账却没显示),前端 JS 会自动发起一次
force_refresh=true请求,触发实时回源
前端这块我可得意了——用 Intersection Observer + requestIdleCallback,只在用户滚动到相关区域时才加载数据,减少无效请求。产品经理看了直呼“高级”。
第四关:数据库分库分表 + 读写分离
最终还是要落库。我们的 PostgreSQL 表结构长这样:
| wallet_address | contract_addr | token_id | balance | last_updated |
|---|---|---|---|---|
| 0x...a1 | 0x...b2 | 123 | 1 | 2023-11-05 |
初期单表 5 亿行,慢查询直接拖垮 DB。解决方案:
- 按合约地址哈希分 64 库,每个库再按钱包地址哈希分 256 表
- 写操作走主库,读操作走只读副本(AWS RDS Read Replica)
- 对高频查询字段(如
wallet_address)建 BRIN 索引(适合时间/数值连续字段),比 B-Tree 节省 70% 空间
性能对比(单节点 vs 分库分表后):
| 指标 | 单库单表 | 64 库 * 256 表 |
|---|---|---|
| 平均查询延迟 (ms) | 320 | 18 |
| 最大 QPS | 1,200 | 85,000+ |
| CPU 使用率峰值 | 98% | 45% |
关于“区块链”和“JavaScript”的意外联动
很多人以为区块链后端就是 Solidity + Go,其实不然。我们这个项目里,前端 JS 扮演了关键角色:
- 用户钱包连接用 Web3.js / Ethers.js,但为了防刷,我们在 JS 层加了 设备指纹(Canvas 渲染、WebGL 特征等)
- 查询结果用 IndexedDB 本地缓存,减少重复请求
- 甚至用 Web Workers 做 Bloom Filter 的本地校验(虽然最终没上,但 POC 成功了)
这让我意识到:高并发不仅是后端的事,前后端协同才能做到极致优化。现在我写 API,都会主动问前端:“你们能帮我挡掉多少无效请求?”
面试题挑战:我的答案升级版
回到开头那道面试题。现在再答,我会这样说:
- 分层防御:网关限流 → 缓存(BF + 空值 + 热点)→ 异步更新 → DB 分片
- 库存扣减:用 Redis Lua 脚本保证原子性,预减库存,异步下单
- 兜底策略:队列削峰(Kafka)、降级开关(Hystrix)、熔断机制
- 监控体系:Prometheus + Grafana 看 QPS/错误率/延迟,ELK 查日志,Sentry 抓前端异常
最关键的是:不要追求“完美架构”,而要“快速迭代 + 数据驱动”。我们第一版只用了 Redis + 限流,撑不住就加 BF,再不行就分库。每次上线都有 A/B 测试,用真实流量验证效果。
给求职者的真心话
如果你也在准备高并发相关的面试(尤其是投 Web3、电商、社交类公司),别光背八股文。建议:
- 动手搭个 demo:用 Node.js + Redis + PostgreSQL 模拟秒杀,部署到 AWS Free Tier
- 关注 SRE 思维:高并发不仅是性能,更是可观测性、可恢复性
- 善用开源方案:Sentinel、Redisson、ShardingSphere 都有成熟实践,别 reinvent the wheel
我就是因为去年那个线上事故,逼自己啃完了《Designing Data-Intensive Applications》,现在面试再也不慌了(至少能聊 30 分钟)。
结语:程序员的浪漫在于“搞定它”
上周五晚上 11 点,我又收到一条告警:“API 延迟突增”。但这次,我淡定地喝了口冰美式,打开 Grafana——哦,原来是某个新上线的 DApp 在疯狂调用。我调整了限流策略,加了热点缓存,10 分钟搞定。
看着监控曲线重新平稳,那一刻的成就感,比做出一个炫酷的前端动画还爽。
高并发系统设计没有银弹,只有不断踩坑、复盘、优化。但正是这种“把混乱变成有序”的过程,让我觉得写代码这件事,还挺酷的。
共勉。
—— 一个在家撸 Spark 也撸高并发的远程打工人
P.S. 如果你对区块链数据管道 or 前端性能优化感兴趣,欢迎来 GitHub 找我(ID: zhe-spark-dev)。最近在用 Svelte 做一个链上数据可视化工具,求 star 🙏

评论 0