唯一id的四个要求
- 全局唯一性:不能出现重复的ID号,既然是唯一标识,这是最基本的要求。
- 趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B+ tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能。
- 单调递增:保证下一个ID一定大于上一个ID,例如事务版本号、IM增量消息、排序等特殊需求。
- 信息安全:如果ID是连续的,恶意用户的扒取工作,比如爬订单号,对手可以直接知道我们一天的单量。
- 在一些应用场景下,会需要ID无规则、不规则。
UUID
- 介绍
- UUID(Universally Unique Identifier)的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的36个字符,示例:550e8400-e29b-41d4-a716-446655440000
- 优点
- 性能非常高:本地生成,没有网络消耗。
- 缺点
- 存储不方便:UUID太长了,没有合适的数据可以表示128位的数字,因此通常用长度为36的string类型表示,这会占用36 bytes
- 信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露。
- 无序:UUID并不能保证顺序
雪花算法
- 不依赖于数据库,强依赖于时间戳
- 只需要用一个long类型就可以表示,从现在看开始,41位时间戳可以表示 2 31 2^{31} 231秒,一天是86400秒,这样算起来,差不多就是 2 16 ∗ 1.5 2^{16}*1.5 216∗1.5的样子,所以可以表示大于 2 1 4 = 16384 2^14=16384 214=16384天,因此至少是60年(实际上是69年),基本上没有任何一款应用可以撑到69的
- 1符号位+41位的毫秒时间戳+5位机房号+5位机器号+12位毫秒内自增id
- 当机器故障,发生时间回迁的时候,很可能会出现重复id,不能完全保障可重复性
- 具体实现的时候,一旦发生时间回迁,就会抛出异常,这会导致不可用
- 实现
/**
* SnowFlake的结构如下(每部分用-分开)
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。
*