高并发系统设计:我在跳槽前刷题时悟出的实战心得

一颗后端星球
2025-12-28 22:53
阅读 1730

上周五晚上十一点半,我正一边调试一个诡异的 goroutine 泄漏问题,一边在 LeetCode 上刷“设计 Twitter”那道经典面试题。突然收到 HR 的微信:“下周一面,对方是做高并发交易平台的,Go 技术栈为主。” 我手一抖,差点把咖啡泼到机械键盘上——这不就是我最近啃源码、压测、调优折腾了三个月的方向吗?

作为一枚混迹 DevOps 场子多年的工程师,平时既要给 CI/CD 流水线“打补丁”,又要帮后端同学排查线上慢查询,还得抽空研究 Kubernetes 调度器源码(别问,问就是想跳槽)。但说实话,高并发系统设计这个话题,以前总觉得是架构师的事,直到去年双11我们系统被流量冲垮,我才意识到:不懂高并发,连运维都做不踏实。


一次“史诗级”故障引发的思考

事情得从公司搞大促说起。产品经理拍脑袋说要搞“秒杀活动”,技术总监直接甩锅给我们中间件组:“扛不住就优化!” 结果当天零点一到,API 网关直接 502,数据库 CPU 打满,Redis 连接池耗尽,监控告警响成交响乐。我当时坐在工位上看着 Grafana 曲线直冲云霄,内心 OS:这哪是秒杀,这是自杀式压测吧?

复盘会上,CTO 冷冷一句:“你们连缓存穿透和雪崩的区别都说不清,还敢上线?” 那一刻,我默默打开了《Designing Data-Intensive Applications》,并决定:跳槽前,必须把高并发这块硬骨头啃下来。

而 Go,成了我首选的实验语言——简洁、高并发模型优雅、标准库强大,关键是,现在大厂后端岗几乎都要求会 Go。于是我一边写自动化部署脚本,一边用 Go 搭了个迷你高并发系统练手。


从一道面试题开始:如何设计一个短链服务?

最近刷题时遇到这么一道高频 面试题挑战

设计一个高性能短链接生成与跳转服务,支持每秒 10 万次请求。

乍一看简单,但细想全是坑:ID 生成怎么保证全局唯一且高性能?跳转接口怎么扛住突发流量?数据库会不会成为瓶颈?

我第一反应是用自增 ID + Base62 编码,但马上意识到:单点数据库主键自增在分布式环境下根本不可靠。于是翻了下 Twitter 的 Snowflake 源码,结合 Go 的 atomic 和 time 包,自己撸了个分布式 ID 生成器:

type Snowflake struct {
    mu       sync.Mutex
    timestamp int64
    sequence  int64
    nodeID    int64 // 机器 ID,部署时注入
}

func (s *Snowflake) Generate() int64 {
    s.mu.Lock()
    defer s.mu.Unlock()

    now := time.Now().UnixNano() / 1e6
    if s.timestamp == now {
        s.sequence = (s.sequence + 1) & 0xfff
        if s.sequence == 0 {
            // 同一毫秒内序列号用完,等待下一毫秒
            for now <= s.timestamp {
                now = time.Now().UnixNano() / 1e6
            }
        }
    } else {
        s.sequence = 0
    }
    s.timestamp = now
    return (now << 22) | (s.nodeID << 12) | s.sequence
}

这段代码在线上跑了一周,QPS 稳稳 30w+,内存占用不到 50MB。Go 的轻量级 goroutine 和高效的调度器,真不是吹的


缓存策略:别让 Redis 成为你的阿喀琉斯之踵

短链的核心是“查”,所以缓存是关键。但缓存用不好,反而会拖垮系统。

我一开始天真地以为“加个 Redis 就行了”,结果模拟压测时发现:当大量请求同时查询同一个不存在的短码(比如用户乱输 URL),所有请求都会穿透到数据库——这就是典型的缓存穿透

解决办法?布隆过滤器(Bloom Filter)!但 Go 标准库没有,于是用了 github.com/bits-and-blooms/bloom/v3

bf := bloom.NewWithEstimates(1000000, 0.01) // 预估100万条,误判率1%
// 写入时:bf.Add([]byte(shortCode))
// 查询前先判断:if !bf.Test([]byte(shortCode)) { return 404 }

另外,缓存雪崩也得防。我把热点 key 的过期时间加了随机偏移:

baseTTL := 3600 // 1小时
randomOffset := rand.Intn(300) // ±5分钟抖动
ttl := baseTTL + randomOffset
redis.Set(ctx, key, value, time.Duration(ttl)*time.Second)

运维经验告诉我:线上事故往往不是技术不行,而是边界情况没考虑到


数据库设计:别再用 INT 做主键了!

很多人图省事,短链表直接这么建:

CREATE TABLE short_urls (
    id INT AUTO_INCREMENT PRIMARY KEY,
    long_url VARCHAR(2048) NOT NULL,
    created_at TIMESTAMP
);

但问题来了:INT 最大才 21 亿,按每天 1 亿条算,半年就爆了。而且自增 ID 容易被爬虫遍历。

我改用 BIGINT 存 Snowflake ID,并加了复合索引:

CREATE TABLE short_urls (
    id BIGINT PRIMARY KEY, -- 来自 Snowflake
    short_code CHAR(8) UNIQUE NOT NULL, -- Base62 编码后的字符串
    long_url TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_created (created_at)
);

同时,为了减少数据库压力,读写分离 + 分库分表也安排上了。用 Go 的 database/sql 配合 sharding-sphere 中间件,轻松实现按 ID 范围分片。


接口设计与限流:保护你的服务不被玩坏

高并发不只是“快”,更是“稳”。再牛的系统,也扛不住恶意刷量。

我在 API 网关层(用 Go 写的轻量级网关)加了令牌桶限流:

limiter := rate.NewLimiter(rate.Every(time.Second/1000), 100) // 每秒1000令牌,突发100

func redirectHandler(w http.ResponseWriter, r *http.Request) {
    if !limiter.Allow() {
        http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
        return
    }
    // ... 正常处理逻辑
}

另外,跳转接口必须做到幂等且无状态,HTTP 状态码用 302(临时重定向),避免 SEO 问题。同时,响应头加上 Cache-Control: public, max-age=31536000,让浏览器和 CDN 缓存一年——能不上服务器的请求,就别让它上来


压测与监控:上线前的最后一道防线

光写代码不够,得验证。我用 hey(Go 版 wrk)做压测:

hey -z 5m -c 5000 -q 200 http://localhost:8080/abc123

配合 Prometheus + Grafana 监控关键指标:

指标 阈值 说明
QPS ≥ 100,000 目标吞吐量
P99 延迟 ≤ 50ms 用户可感知延迟
DB CPU ≤ 70% 避免资源争抢
Goroutine 数 稳定 防止泄漏

有一次压测发现 goroutine 数持续上涨,最后定位到是 HTTP Client 没设置超时,连接一直 hang 住。Go 的并发虽爽,但资源管理不能懒


跳槽前的顿悟:高并发不是堆技术,而是权衡

折腾这套系统的过程中,我逐渐明白:高并发设计的本质,是在成本、复杂度、性能之间找平衡

  • 不是所有场景都需要分库分表,先优化 SQL 和索引;
  • 不是所有数据都要强一致,最终一致性往往更实用;
  • 不是缓存越多越好,缓存一致性维护成本可能更高。

上周面试时,面试官问:“如果让你重新设计,会改什么?”
我答:“我会先砍掉 80% 的功能,聚焦核心路径。毕竟,能活着的系统,才是好系统。”


写在最后

如今我的小短链系统已经跑在公司测试环境,QPS 稳定在 12w+,P99 延迟 28ms。虽然离真正的工业级还有距离,但至少,我不再怕高并发相关的 面试题 了。

如果你也在准备跳槽,或者被线上高并发问题折磨,不妨从一道 面试题挑战 开始,用 Go 动手造个轮子。纸上谈兵终觉浅,绝知此事要 coding

对了,前端同事看我折腾这么久,问我能不能加个酷炫的粒子动画展示跳转过程……我翻了个白眼:“你先让产品别半夜改需求,我再考虑给你加 Lottie。”

(完)

评论 0

最热最新
暂无评论
一颗后端星球Lv.1
0
影响力
0
文章
0
粉丝