主键生成策略之雪花算法
为了缓解数据库服务器压力和提高并发量往往会进行分库分表,这时使用主键自增,查询会冲突。
主键生成策略分为:
【1】中心化生成
中心化生成算法经典的方案
1、主要有基于SEQUENCE区间方案
2、各数据库按特定步长自增
3、基于redis生成自增序列三种
【2】去中心化生成
去中心化方式无需额外部署,以jar包方式被加载,可扩展性也很好,因此更推荐使用。目前主流的去中心化生成算法有:
1、UUID及其变种
2、snowflake算法
1、雪花算法(snowflake算法)
作用: 分布式场景下生成唯一的主键。
Snowflake算法产生是为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统中不同机器产生的id必须不同。Snowflake算法把时间戳,工作机器id,序列号组合在一起生成一个64 个 bit 结构如下:
bit(比特)是表示信息的最小单位,是二进制数的一位包含的信息或2个选项中特别指定1个的需要信息量。一个Byte由8 bits组成,是数据存储的基础单位,1Byte又称为一个字节,用一个字节(Byte)储存,可区别256个数字,每一bit 可以代表0 或 1 的数位讯号。
63 | 62-22 | 21-12 | 11-0 |
---|---|---|---|
1位:2 | 41位:支持69.7年(单位ms) | 10位:1024 | 12位:4096 |
这 64 个 bit 中,其中 1 个 bit 是不用的,然后用其中的 41 bit 作为毫秒数,用 10 bit 作为工作机器 id,12 bit 作为序列号。
给大家举个例子吧,比如下面那个 64 bit 的 long 型数字:
- 第一个部分,是 1 个 bit:0,这个是无意义的。
- 第二个部分是 41 个 bit:表示的是时间戳。
- 第三个部分是 5 个 bit:表示的是机房 id,10001。
- 第四个部分是 5 个 bit:表示的是机器 id,1 1001。
- 第五个部分是 12 个 bit:表示的序号,就是某个机房某台机器上这一毫秒内同时生成的 id 的序号,0000 00000000。
简而言之,
雪花算法就是用一个 64 bit 的数字中各个 bit 位来设置不同的标志位,区分每一个 id。
工作作机器id可以使用IP+Path来区分工作进程。如果工作机器比较少,可以使用配置文件来设置这个id是一个不错的选择,如果机器过多配置文件的维护是一个灾难性的事情,
优势:在服务器规模不是很大(不超过1024条件) 全局唯一 ,单机递增 ,是数字类型,存储索引成本低。
劣势:机器规模大于1024无法支持,需要运维配合解决单机部署多个同服务进程问题。
2、基于mybatis集成雪花算法
1.添加SnowflakeIdWorker配置类
SnowflakeIdWorker与mybatis集成
/**
* Twitter_Snowflake<br>
* SnowFlake的结构如下(每部分用-分开):<br>
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
* 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
* 加起来刚好64位,为一个Long型。<br>
* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
*/
public class SnowflakeIdWorker {
// ==============================Fields===========================================
/** 开始时间截 (2020-08-28) */
private final long twepoch = 1598598185157L;
/** 机器id所占的位数 */
private final long workerIdBits = 5L;
/** 数据标识id所占的位数 */
private final long datacenterIdBits = 5L;
/** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
/** 支持的最大