推荐用crypto/rand生成6–8字节随机数再Base62编码,避免自增ID暴露序号和并发冲突;MySQL存映射+Redis缓存穿透防护,跳转统一用302,校验URL合法性并限制HTTP连接池与数据库连接数。

短地址生成必须避开冲突和可预测性
直接用自增 ID 转 62 进制(0-9a-zA-Z)看似简单,但暴露序号、易被爬取、并发插入时还可能因主键冲突重试——这不是高性能,是给自己埋雷。
推荐方案:用 crypto/rand 生成 6–8 字节随机字节,再 Base62 编码。6 字节 → 约 56 位空间 → 理论上需生成约 2.4 亿条才到 1% 冲突概率(生日悖论),线上够用。
-
rand.Read()比math/rand更安全,不依赖 seed,避免多 goroutine 竞态 - 编码表固定为
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",别手抖写反大小写顺序 - 生成后必须查库去重;失败则重试(建议上限 3 次,再失败就返回 500,别死循环)
数据库选型和索引直接影响 QPS 上限
MySQL 单机扛不住高并发短链跳转(尤其是热 key 场景),但完全弃用关系型库又丢掉事务和运维成熟度。折中方案:MySQL 存映射 + Redis 缓存穿透防护。
- MySQL 表结构只要两列:
short_code(主键,VARCHAR(8))和original_url(TEXT);别加冗余字段 -
short_code必须建唯一索引,且设为聚簇索引(InnoDB 默认),避免回表 - Redis 用
GET查缓存,未命中再查 DB 并SETNX回填;过期时间建议 24h,别设永不过期 - 注意缓存击穿:对空结果也缓存(如
SET short:abc "" EX 60),防止恶意刷不存在的 code
HTTP 跳转必须用 302 还是 301?看场景
301 是永久重定向,浏览器和 CDN 会强缓存 Location,后续改目标 URL 就失效了。短链服务几乎都需要运营后台随时修改目标地址,所以一律用 http.StatusFound(302)。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- Go 标准库里别写
w.WriteHeader(302)+w.Header().Set("Location", ...),直接用http.Redirect(w, r, url, http.StatusFound) - 务必在
Redirect前校验url是否合法(是否以http://或https://开头,长度是否超限),否则可能被用于开放重定向漏洞 - 跳转前记录一次轻量日志(如短码、IP、User-Agent Hash),别记全量,否则磁盘 IO 直接拖垮吞吐
goroutine 泄漏和连接耗尽是最隐蔽的性能杀手
很多人用 http.DefaultClient 发请求(比如回调通知、URL 预检),但它的 Transport 默认不限制连接池大小,高峰时瞬间创建上千连接,触发 too many open files。
- 所有出站 HTTP 调用必须用自定义
http.Client,设置MaxIdleConnsPerHost = 100、IdleConnTimeout = 30 * time.Second - 数据库连接池(
*sql.DB)要显式调用SetMaxOpenConns(20)和SetMaxIdleConns(10),别依赖默认值 - 用
pprof定期看/debug/pprof/goroutine?debug=2,如果数量持续 >1k 且不降,大概率有 channel 阻塞或忘记close()
短链服务的瓶颈往往不在算法,而在连接管理、缓存策略和错误兜底的细节里。上线前至少压测 3 轮:纯跳转、带缓存穿透、高冲突短码生成,每轮都看 go tool pprof 的 CPU 和 heap profile。


















