Base62编码选它因字符集(0-9a-zA-Z)无URL敏感符+、/、=,可直用于路径;而Base64需转义且与Base62混用会解码失败;编码须固定字符顺序并注意大小写敏感。

Base62 编码为什么选它而不是 Base64
Base62(a-z + A-Z + 0-9)生成的短码不含 +、/ 和 =,能直接用在 URL 路径里,不需额外编码或转义。Base64 的 + 和 / 在 URL 中有特殊含义,容易被中间代理或浏览器误处理;而 = 作为填充符,在路径中也显得突兀。
常见错误现象:base64.URLEncoding.EncodeToString 生成的短码带 - 和 _,看似安全,但和 Base62 混用会导致解码失败——两者字符集不重叠,不能混搭。
- 只用
math/big.Int或手写除余循环实现编码,别依赖第三方 Base64 库改字符表 - 确保编码字符顺序固定:比如必须是
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",顺序错会导致同 ID 解出不同短码 - 注意大小写敏感:HTTP 路由默认区分大小写,
"Ab"和"ab"是两个不同短码,这是特性不是 bug
如何把数据库自增 ID 映射成短码(含冲突兜底)
直接拿 MySQL 的 id 做 Base62 编码最简单,但前提是 ID 全局唯一且单调递增。问题在于:如果服务多实例写入、或用了分库分表,ID 可能重复或跳变,导致短码碰撞。
立即学习“go语言免费学习笔记(深入)”;
使用场景:单机 SQLite / 单主 MySQL 场景下可直接用 LAST_INSERT_ID();高并发时建议用独立发号器(如 github.com/sony/sonyflake),输出 uint64 后再进 Base62。
- 插入新记录后,立刻用
id调用 Base62 编码函数,得到code - 把
code写回同一行(更新short_url.code字段),并加唯一索引:UNIQUE KEY idx_code (code) - 如果
UPDATE报ErrDuplicateEntry,说明撞码了——此时递增原id再试一次,最多重试 3 次(概率极低,但必须防)
Go 里怎么写一个线程安全的 Base62 编解码函数
Base62 编码本身无状态,但若用 sync.Pool 复用 bytes.Buffer 或预分配切片,能减少 GC 压力。不过对短链接这种低延迟、中等 QPS 场景,直接用 make([]byte, 0, 6) 更清晰,不必过度优化。
参数差异:编码函数输入是 uint64,输出 string;解码函数反之。别传 int——32 位系统下 int 可能溢出,统一用 uint64 避免隐式转换问题。
func encode(n uint64) string {
if n == 0 {
return "0"
}
var buf [11]byte // 64bit 最大值 18446744073709551615 → Base62 后最长 11 位
i := len(buf) - 1
for n > 0 {
buf[i] = digits[n%62]
i--
n /= 62
}
return string(buf[i+1:])
}
-
digits必须定义为全局var digits = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - 解码时遇到非法字符(如空格、中文)应直接返回 error,不要静默跳过——否则可能把
"a b"当成"ab"解 - 别在 HTTP handler 里做耗时解码(如从磁盘读映射表),所有短码查表必须走内存缓存(
map[string]string+sync.RWMutex)
短码路由匹配时为什么不能用正则捕获全部路径
写 http.HandleFunc("/<code>:code", handler) 看似方便,但 Go 标准库的 http.ServeMux 不支持通配符,实际会匹配字面量 ":code" 这个字符串。真正要捕获路径段,得用 strings.TrimPrefix(r.URL.Path, "/") 手动取最后一段,或换用 gorilla/mux 等路由器。
性能影响:每次请求都调 strings.Split(r.URL.Path, "/") 会分配切片,不如 strings.LastIndex + string[:] 零分配截取。
- 路径设计建议统一为
/c/<code>abc123,避免根路径冲突(比如/healthz被当成短码) - 对
/c/后的code做长度校验(如 4–8 字符),超长或含非法字符直接 404,不查库 - 302 跳转响应头必须设
Location,且值要用url.QueryEscape处理原始目标 URL 中的特殊字符,否则用户点击后可能跳到错误地址
真正麻烦的是缓存一致性:短码映射变了,CDN 或浏览器可能还缓存着旧跳转。上线前得确认所有中间层没对 302 做傻瓜式缓存。










