基础知识![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/d87a47876d2f115e3d0204fbc6f8db1a.png#pic_center)
基本原理:由4部份组成一个64字节的整数(bigint).
- 字节计算读取顺序从左至右,下标计数从0开始.
- 第0位为符号位:正数是0,负数是1.但是有些语言没有无符号整数(java\PostgreSQL),为保证可移植性因此固定为0。
- 第1-41位为时间戳:单位为毫秒,时间戳只有41位,所以值范围为0-2199023255551毫秒,最多可以存储69年的数据.因为值范围较小,不能直接使用UNIX时间戳,应该使用时间戳的差值(当前时间-你指定的开始时间).你指定的开始时间可以是任意时间,比如系统于2020年开始开发,这个时间可以指定为2020-1-1.
select (2199023255551::bigint) / (24::bigint * 60 * 60 * 1000) / 365
- 第42-51位为机器id:可以自己组合这10bit数据.例如:如果主机都在C类IP段之内(0-255),可以取IP地址的最后一位做为机器id.此时只需要8字节即可,多出来的2字节可以分配给时间戳,此时时间戳占用43字节,值范围为0-8796093022207,存储范围可以扩大至278年.
select (((1::bigint)<<43) - 1) / (24::bigint * 60 * 60 * 1000) / 365
- 第52-63位顺序号:值范围为0-4095,为同一时间戳同一个机器可以生成的ID个数,也就是同一毫秒同一个机器最多可以生成4095个ID.
select (((1::bigint)<<12) - 1)
位运算技巧
要设置bit位的任一1位为0时,将该位设置为0,其它位设置为1,然后执行&运算.例如将第7位设置为0
select (32767::bit(16)) & x'FF7F'
-- 32767二进制表示为 01111111 11111111
-- FF7F二进制表示为 11111111 01111111
-- 结果 01111111 01111111
要设置bit位的任一1位为1时,将该位设置为1,其它位设置为0,然后执行|运算.例如将第7位设置为1
select (0::bit(16)) & x'0080'
-- 0二进制表示为 00000000 00000000
-- 0080二进制表示为 00000000 10000000
-- 结果 00000000 10000000
用SQL实现雪花算法snowflake
select
id>>22,
((id<<41)&9223372036854775807)>>53,
((id<<51)&9223372036854775807)>>51
from (select (((2199023255551::bigint)<<22) | (1023<<12) | 4095 ) as id) as tmp
- 2199023255551:为时间戳的最大值
- 1023:为机器id的最大值
- 4095:为顺序号的最大值
- 注意:如果输入值超过最大值将无法还原数据,因此在生成时必须先范围范围是否在有效范围内.
- 9223372036854775807是确保符号位设置0,因为算法规定符号位必须0.
--9223372036854775807的二进制
0111111111111111111111111111111111111111111111111111111111111111
- 在机器id和顺序号左移位时必须保持符号位,因此机器id左移41位,顺序号左移51位.左移位完成后,符号位不一定为0,因此在左移位后必须保证符号位为0.
变种
在使用分布式程序时,此时同一毫秒内同一台机器只会产生很少量的序列号,因此可以将工作机器id扩充至12位、14位、16位,序列号使用10位、8位、6位.具体如何选择请结合需求.
- 工作机器id12位值范围:1-4095
- 工作机器id14位值范围:1-16383
- 工作机器id16位值范围:1-65535
- 序列号10位值范围:1-1023
- 序列号8位值范围:1-255
- 序列号6位值范围:1-63
工作机器id12位,序列号10位
select
id>>22,
((id<<41)&9223372036854775807)>>51,
((id<<53)&9223372036854775807)>>53
from (select (((2199023255551::bigint)<<22) | (4095<<10) | 1023 ) as id) as tmp
工作机器id14位,序列号8位
select
id>>22,
((id<<41)&9223372036854775807)>>49,
((id<<55)&9223372036854775807)>>55
from (select (((2199023255551::bigint)<<22) | (16383<<8) | 2 ) as id) as tmp
工作机器id16位,序列号6位
select
id>>22,
((id<<41)&9223372036854775807)>>47,
((id<<57)&9223372036854775807)>>57
from (select (((2199023255551::bigint)<<22) | (65535<<6) | 63 ) as id) as tmp