源码解读 github.com/satori/go.uuid
从头开始跟踪
u2 := uuid.NewV4()
NewV4 函数调用
// NewV4 returns random generated UUID. |
- 随机数填充
func (g *generator) safeRandom(dest []byte) { |
- 设置版本号相关 bit
// SetVersion sets version bits. |
- 设置变体(有多个标准,相当于标准变体标识)
// SetVariant sets variant bits. |
- uuid 表现结构, 变成 16 进制 字符并插入 -
func (u UUID) String() string { |
const hextable = "0123456789abcdef" |
总结
v4 版本是基于随机数,然后对对应的版本和变体设置. 并转化为 16 进制字符后的结果. 由于 uuid 是 128bit, 长度比较长,所以使得随机数的碰撞几率不会很高.但依然无法让唯一性完全成立.
其他策略
Setup used by MongoDB[1]:
[ time, machine_id, process_id, counter ]
Twitter Snowflake[2] use something similar to the above.
Cassandra[3] use something like this:
[ time, version, variant, counter, node_id ]
From PingCap:
TiDB 的约束更强一点,需要不重复且单调递增
所以这边是有三个 pd-server 来负责发号,号 = 物理 timestamp + 逻辑时钟
前 48 位是物理时间戳,后 16 位是一个逻辑时钟(就是一个计数器)
每隔 1s 将物理时间通过 raft 复制到所有 pd-server 中
即使 master 挂了,新的 master 被选举出来,也能保证 sleep(1s) 后,一定能分配出更大且没有交集的号
PingCap 的基本思路我觉得刨除 master/slave 的集群外. uuid 生成的主要是
timestamp+counter(逻辑时钟)
首先他们是 DB, 所以肯定希望 timestamp 来做前面部分,有利于排序索引
另外独立的 timestamp 对于集中的生成 uuid 可能无法满足需求,粒度可能太大容易出现重复, +counter 主要就看 counter 是怎么针对并发的请求来做处理的. 比如加锁来避免更细粒度的冲突(没有细问)
参考
https://www.ietf.org/rfc/rfc4122.txt
https://news.ycombinator.com/item?id=10606910
https://golang.org/pkg/crypto/rand/#Read
https://en.wikipedia.org/wiki//dev/random