crypto/rand才是真随机,math/rand仅为伪随机且绝不可用于密码、密钥、token等安全场景;crypto/rand直接读取操作系统熵源(如/dev/urandom),满足密码学安全要求,而math/rand种子可预测、易冲突、不抗爆破。

crypto/rand 才是真随机,math/rand 只能用于非安全场景
如果你要生成密码、密钥、token 或任何不能被预测的值,math/rand 绝对不能用——它只是伪随机,种子一旦暴露或固定,整个序列可完全复现。而 crypto/rand 从操作系统熵池读取(如 Linux 的 /dev/urandom),满足密码学安全要求。
常见错误现象:math/rand 在服务启动时用 time.Now().UnixNano() 做种子,结果多个实例在毫秒级时间戳下生成完全相同的“随机”ID;或者单元测试里没重置种子,导致随机行为不可重现又不可靠。
-
math/rand适合:模拟、游戏逻辑、负载均衡哈希、测试数据填充 -
crypto/rand必须用于:JWT secret、AES key、session token、一次性验证码 - 不要试图给
crypto/rand“加种子”或“重播”,它不接受种子,也不提供Seed()方法
用 crypto/rand 生成字节切片最直接,别自己拼 int
很多人想生成一个 0–99 的随机数,就先用 crypto/rand 读 4 字节再 mod 100——这会引入偏差(bias)。正确做法是:需要整数时,用 crypto/rand.Int() 配合 big.Int;需要字节时,直接填满 []byte。
示例:生成 32 字节随机 token
buf := make([]byte, 32)
_, err := rand.Read(buf)
if err != nil {
// 处理 err,比如系统熵池暂时不可用(极罕见)
}
// buf 已就绪,可 base64 编码或直接用作 key
如果非要整数范围(比如 1–1000):
在 Golang 中使用 samber/hot 进行内存缓存,支持 LRU、LFU、TinyLFU、W‑TinyLFU、S3FIFO、ARC、TwoQueue、SIEVE、FIFO 等淘汰算法,提供 TTL、缓存加载器及分片功能。
n, err := rand.Int(rand.Reader, big.NewInt(1000))
if err != nil {
// ...
}
id := n.Int64() + 1 // 得到 1–1000
-
rand.Read()是最常用、最高效的方式,避免手动转换和模运算偏差 -
rand.Int()内部做了均匀裁剪(rejection sampling),比手动 mod 安全 - 别用
rand.Reader做多次小读取(如循环读 1 字节 10 次),一次读够更高效也更少 syscall
math/rand 在并发下默认共享全局 Rand,容易被意外污染
Go 1.20+ 默认启用 math/rand 的全局 Rand 实例,但它的 Seed() 是包级全局操作——某个 goroutine 调了 rand.Seed(),所有其他地方的随机行为都变了。这在中间件、工具函数、第三方库中极易引发隐蔽 bug。
正确做法:显式创建独立实例,尤其在并发或模块化代码中:
r := rand.New(rand.NewSource(time.Now().UnixNano())) n := r.Intn(100)
- 永远不要在 init() 或包级变量里调
rand.Seed() - 测试时若需可重现,传入固定
rand.NewSource(42),而不是改全局种子 - HTTP handler 中不要复用同一个
rand.Rand实例跨请求,除非你明确控制其状态
性能差异没你想的那么大,别为 crypto/rand 过度优化
有人担心 crypto/rand 比 math/rand 慢几十倍,实际在多数业务场景下不构成瓶颈:生成一个 32 字节 token 的耗时在纳秒级,远低于一次 DB 查询或 HTTP 请求。真正慢的是错误用法——比如每生成 1 字节都调一次 rand.Read(),或反复打开关闭设备文件。
-
crypto/rand底层通常缓存并批量读取熵池,单次Read()几百字节开销几乎恒定 - 如果高频生成大量随机数(如模拟蒙特卡洛),且确定不需要密码学安全,才考虑
math/rand,并自行管理Rand实例 - 混淆“性能差”和“设计误用”:慢的从来不是
crypto/rand,而是没理解它该在哪用、怎么用
真正容易被忽略的是错误归因——把 token 泄露归咎于“随机数不够随机”,其实问题常出在存储方式(明文日志)、传输协议(HTTP)、或权限控制(API 未鉴权),而非生成器本身。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










