fastrand是加密安全的高速伪随机数生成器,基于系统熵源初始化并重复哈希生成随机值,无状态、不可复现,不支持种子重置,用于map迭代起点等场景。

Go 的 fastrand 不维护跨 goroutine 或跨调用的“状态”,它本质是 PC/SP 混淆生成的伪随机数,每次调用都是独立快照 —— 所以你无法“重置”或“复现”它的序列,也不能靠 rand.Seed() 影响它。
fastrand 在 map 迭代起点中的实际作用
每次 range 一个 map,运行时会调用 runtime.mapiterinit,其中关键一步就是:startBucket := fastrand() % (1 。这里 <code>h.B 是当前桶数量的对数(即 bucket 数为 2^B),fastrand() 提供起始 bucket 索引。
这个值只决定“从哪个 bucket 开始扫”,不参与 key 的哈希计算,也不影响元素在 bucket 内的排列顺序。
- 即使 map 内容完全相同,两次
range的起始 bucket 极大概率不同 - 当
B == 0(即只有 1 个 bucket)时,fastrand() % 1恒为 0,此时迭代顺序才可能稳定(但 bucket 内部仍按 tophash 排) - 扩容后
B变大,模数变大,起始点分布更散,顺序更难预测
为什么 GODEBUG=mapiter=1 和 rand.Seed(0) 都无效
GODEBUG=mapiter=1 仅控制 map 是否启用“迭代器预分配”和“扩容时是否阻塞迭代”,它不干预 fastrand() 的调用逻辑;而 math/rand 的 Seed() 完全不影响 runtime.fastrand() —— 二者底层毫无关系:
-
runtime.fastrand()基于当前 goroutine 的栈指针(SP)和程序计数器(PC)做轻量混淆,无全局状态 -
math/rand是用户态 PRNG,依赖显式 seed 和内部 state 字段 - 哪怕你在测试里
rand.Seed(0)后立刻range m,结果依然随机
真正可控的哈希种子只在 hash/maphash 中
如果你需要可复现、可配置的哈希行为(比如做缓存键一致性校验或安全哈希),必须绕过内置 map,改用 hash/maphash:
h := maphash.New()
h.Write([]byte("my-key"))
seed := h.Sum64() // 这个 seed 可复现、可传入自定义结构
注意:hash/maphash 的输出不用于 runtime map,它只供你自己构造确定性哈希逻辑。内置 map 的哈希过程始终使用固定、不可控的 hash0 字段(来自 hmap.hash0),该字段在 map 创建时由 fastrand() 初始化,之后就固化了。
真正容易被忽略的是:map 迭代顺序的“随机性”不是 bug,而是刻意设计的防御机制 —— 它防止外部依赖未定义行为,也规避了基于遍历顺序的 DoS 攻击。别试图驯服它,该用有序结构时就用 sort.Slice + keys 切片。


















