因为未调用rand.seed()或使用固定种子;go 1.20+应改用rand.new(rand.newsource(time.now().unixnano())),且crypto/rand与math/rand不可混用。

为什么 rand.Intn() 每次运行都返回相同数字?
因为没调用 rand.Seed(),或者用了固定种子(比如 rand.Seed(1))。Go 1.20+ 已弃用全局 rand.Seed(),改用显式 rand.New() + rand.NewSource()。
正确做法是:用当前纳秒时间戳初始化源,再构造独立的 *rand.Rand 实例:
src := rand.NewSource(time.Now().UnixNano()) r := rand.New(src) n := r.Intn(100) // [0, 100)
- 别在循环里反复新建
*rand.Rand,性能差且可能因时间精度不足导致重复种子 - 测试时若需可重现结果,才用固定种子(如
rand.NewSource(42)),生产环境必须用真随机源 - 全局包级变量
rand.Rand可复用,但注意并发安全 ——*rand.Rand本身不是并发安全的,多 goroutine 写需加锁或改用sync.Pool
crypto/rand 和 math/rand 能不能混用?
不能。二者设计目标完全不同:math/rand 是伪随机数生成器(PRNG),快、可重现;crypto/rand 读取操作系统熵池(如 Linux 的 /dev/urandom),慢但密码学安全。
典型误用场景:
- 用
math/rand生成 API token、加密密钥、session ID → 有被预测风险 - 用
crypto/rand做游戏抽卡、蒙特卡洛模拟 → 不必要地拖慢性能,还可能因系统熵不足阻塞(极少见,但Read()在某些嵌入式环境会)
生成 32 字节随机 token 的正确写法:
b := make([]byte, 32)
_, err := crypto/rand.Read(b)
if err != nil {
log.Fatal(err)
}
token := hex.EncodeToString(b)
如何安全地生成范围内的密码学随机整数?
crypto/rand 没有直接的 Intn(),必须手动实现防偏移(bias)的均匀采样。错误做法是 int(randUint64()) % n,会导致小数值概率略高。
推荐使用 crypto/rand 官方示例中的 Int() 方法(Go 1.22+)或手动实现 rejection sampling:
func CryptoRandInt(max int) (int, error) {
if max = max
mask := bits
mask |= mask >> 1
mask |= mask >> 2
mask |= mask >> 4
mask |= mask >> 8
mask |= mask >> 16
mask |= mask >> 32
for {
var b [8]byte
_, err := crypto/rand.Read(b[:])
if err != nil {
return 0, err
}
v := binary.LittleEndian.Uint64(b[:]) & mask
if int(v)
- 不要自己写“取模”逻辑,尤其当
max不是 2 的幂时 - 如果项目已升级到 Go 1.22+,直接用
rand.Int(cryptoRand, big.NewInt(int64(max))),它内部已做无偏处理 - 注意
crypto/rand.Read()返回的是字节流,不是整数 —— 别试图用binary.Read()直接读int,大小端和填充易出错
为什么本地开发时 crypto/rand 有时卡住?
极少情况下(如容器无权限访问 /dev/urandom、Windows WSL1 旧内核、某些 chroot 环境),crypto/rand.Read() 可能短暂阻塞等待熵。这不是 bug,是设计行为 —— 但现代 Linux/Windows/macOS 几乎不会发生。
排查步骤:
- 检查是否运行在极度受限环境(如 scratch 镜像未挂载
/dev) - 临时用
strace -e trace=openat,read go run main.go看是否卡在/dev/urandom或 fallback 路径 - 避免在 init 函数中首次调用
crypto/rand,改到 handler 或业务启动后 —— 减少冷启动感知延迟
真正需要 fallback 的场景极少;强行用 math/rand 补位会破坏安全性,不建议。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











