base62比base64更适合短链接,因其字符集“0-9a-za-z”全为url-safe字符,无需转义,可直接用于路径;而base64的+、/、=易被代理转义或截断,混用base62会导致解码失败。

Base62 编码不是必须的,但它是当前最实用、最省心的短码生成方式——尤其当你想避免 URL 编码、代理拦截、字符歧义这三类问题时。
为什么 Base62 比 Base64 更适合短链接路径
Base64 的 + 和 / 在 HTTP 路径中会被某些 CDN、反向代理(如 Nginx 默认配置)、甚至浏览器自动转义成 %2B、%2F,导致解码失败;= 作为填充符也容易被截断或忽略。而 Base62 字符集 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 全部是 URL-safe 字符,可直接拼进路径如 /aB3xK9,无需任何编码处理。
常见错误现象:
- 用
base64.URLEncoding替代 Base62:生成的-和_看似安全,但和 Base62 字符集不重叠,一旦混用(比如前端误以为是 Base62 解码),必然失败 - 硬删
base64.StdEncoding结果里的+//:字符集被破坏,长度不固定,解码时无法对齐,decodeMap查表会 panic
实操建议:
- 手写两个映射:一个
[]byte编码表,一个map[byte]int解码表(别用strings.IndexByte查,慢) - 编码函数输入必须是
uint64,不是int——32 位系统下int可能溢出,且 MySQLAUTO_INCREMENT默认是bigint unsigned - 别用浮点或字符串中间转换,全程整数除余运算,性能高、无精度丢失
自增 ID + Base62 是首选,但不能直接暴露
用数据库主键 ID 做 Base62 源头,是最轻量、唯一、无碰撞、易统计的方案。但它的问题是:ID 单调递增 → 短码可预测 → 容易被爬虫遍历、反推总量、批量探测失效链接。
实操建议:
- 混淆优先选异或掩码:
id ^ 0x5DE4E6A2,快、可逆(调试友好)、不增加存储开销 - 别用
crypto/rand为每个 ID 生成随机 salt:QPS 上千时 DB 写入延迟明显上升 - 别用
md5(id)或sha256(id)哈希:哈希后仍要查重+重试,逻辑变重,且短码长度不可控 - 初期量小可先跳过混淆,靠
Nginx limit_req或中间件限速防遍历,后续平滑切到异或方案
MySQL 表结构设计要避开唯一索引重试陷阱
高并发下靠 INSERT IGNORE + 唯一索引兜底,会导致大量 Duplicate entry 'xxx' for key 'idx_code' 错误,拖垮应用和 DB 连接池。
实操建议:
- 写入前先
SELECT short_code FROM urls WHERE short_code = ? FOR UPDATE,查不到再INSERT - 用
INSERT IGNORE仅作最后防线,不是主逻辑 -
short_code字段加UNIQUE KEY,但不要依赖它来“发现冲突”——那是业务层该干的事 - 避免在 HTTP handler 里直连 DB 获取自增 ID;预取一批(如 1000 个)缓存在内存,用完再取,降低 DB 压力
跳转必须用 302 + 显式禁用缓存
用 http.StatusMovedPermanently(301)会导致浏览器/CDN 强制缓存跳转目标,后期改链或灰度发布时完全不可控。而 http.StatusFound(302)是临时重定向,配合明确的 Cache-Control 才可靠。
实操建议:
- 跳转前必须校验原始 URL 协议:
strings.HasPrefix(longURL, "http://") || strings.HasPrefix(longURL, "https://"),否则可能跳到javascript:alert(1)引发 XSS - 强制设置:
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") - 别透传
r.Referer(),短链本身不应成为 Referer 追踪入口 - 解析路径段时注意大小写敏感:
/Abc和/abc是两个不同短码,这是 Go HTTP 路由默认行为,不是 bug
真正难的不是写通逻辑,而是把混淆、查重、缓存控制、协议校验这几处细节全压进一次请求生命周期里——漏掉任意一个,上线后都会变成线上事故的伏笔。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











