mysql集群发号器_分布式——分布式发号器

今天停电,所以springboot源码看不了,手头刚好有本书,学习了下分布式发号器

一、方案

1、UUID

无法满足业务特性。UUID虽然能保证ID的唯一性,但是无法满足业务要求的很多其他特性,如有序性+可反解性(没有提供反解方法,例如反解得到时间戳)+可制造性(手工生成、洗脏数据难度变大)

占用空间大。UUID比较长,利用JDK生成的一个UUID占用36字节(由于包含a-f,数据库类型varchar类型):8-4-4-4-12,如果建立B+树索引的话,导致单个索引节点包含关键字减少,索引节点变多,高度变高,性能下降

由于是无序的,作为InnoDB主键索引,可能会导致页分裂,降低插入性能同时,还产生了很多内存碎片

public classUUIDTest {public static voidmain(String[] args) {//JDK中uuid属于总共128个bit//time_low = 32bit//time_mid = 16bit//time_high_and_version= 16bit//variant_and_sequence= 16bit//node = 48bit//① 由于toString采用16进制(4bit)打印 8-4-4-4-12共36个字节//② version=4的uuid,随机数生成,没有机器码,所以分布式还是可能存在重复的

UUID uuid =UUID.randomUUID();

System.out.println(uuid.toString());//5db67001-7d8b-49a0-baa2-215629b00ae9

}

}

2、数据库生成

利用数据库作为发号器,有两种方法:表字段自增与自增序列sequence。虽然能保证id唯一性,但是会影响数据库的性能与伸缩性。

增加数据库压力,降低性能。Id的产生是一种频繁的操作,会频繁的请求数据库,导致数据库性能下降。

降低数据库审索引。无论表字段自增还是自增序列实现,都会影响数据库分表分库。

sequence是一种隐式字段,需要人工维护

--表字段自增 mysql中常用

id bigint(20) auto increment,--自增序列sequence oracle中常用

createsequence sequence_name

incrementby 1   --每次加的个数据

start with 1    --从1开始计数

nomaxvalue    --不设置最大值

nocycle      --一直累加,不循环

cache 10;

sequence.nextval;--自增sequence并返回

3、Snowflake——雪花算法

Snowflake是Twitter开源的分布式发号器,类似uuid使用bit位控制生成。结构如下:

--总共64bit,

-- 时间戳放在高位,保证毫秒级的有序性;

-- 机器号+序列号处于低位,单机有序的,多机无序的--1bit:无效位,其实可以作为版本号--41bit:时间戳位--10bit:机器号--12bit:序列号

简单实现,具体的肯定比这个复杂得多:

public classSnowFlakeIdGenerator {private final long version;//版本号 1bit

private final long STARTTIMESTAMP = 1585286065061L;//创建ID生成器的时间

private long timestamp;//时间戳毫秒级 41bit

private final long mcid;//机器id 10bit

private int seq = -1;//自增seq

private long lastTimstamp = -1;//记录毫秒级

private long lastSeql = 0;//记录同毫秒级的sql

public SnowFlakeIdGenerator(long version,longmcid){//版本号校验

if (version>1 || version <0){throw new IllegalArgumentException("version must in {0,1}");

}//机器号校验

if (mcid>1023 || mcid < 0){throw new IllegalArgumentException("mcid must in {0-1023}");

}this.version =version;this.mcid =mcid;

}/*** 同毫秒级seq自增,

* 不同毫秒级seq归零

*@return

*/

private long getSeq(longcurrent){if (lastTimstamp ==current){//同毫秒级校验seq是否超出范围

if (seq > 1022){throw new IllegalArgumentException("seq arrive max,generator failed!");

}

}else{//不同毫秒级seq归零

lastTimstamp =current;

seq= -1;

System.out.println("============== 分割线 =================");

}return ++seq;

}private longgenerator(){long timestamp = System.currentTimeMillis()-STARTTIMESTAMP;long id = 0L;//1 + 41 + 10 + 12 由于时间处于高位,所以毫秒间是有序的

id |= version << 63;

id|= timestamp << 22;

id|= mcid << 12;

id|=getSeq(timestamp);returnid;

}public static voidmain(String[] args) {

ExecutorService pool= Executors.newFixedThreadPool(2);//开两个线程模拟分布式两台机器//毫秒间是有序的//毫秒内是单机有序的,整体是无序的

pool.submit(newRunnable() {

@Overridepublic voidrun() {

SnowFlakeIdGenerator idGenerator= new SnowFlakeIdGenerator(0L,0L);for (int i = 0; i < 1000; i++) {

System.out.println(idGenerator.generator());

}

}

});

pool.submit(newRunnable() {

@Overridepublic voidrun() {

SnowFlakeIdGenerator idGenerator1= new SnowFlakeIdGenerator(0,1L);for (int i = 0; i < 1000; i++) {

System.out.println(idGenerator1.generator());

}

}

});

pool.shutdown();

}

}

雪花算法优点:

占用空间比uuid小。由于返回的是一个长整型long,所以数据库字段可使用bigint属性创建,实际最大8字节。

粗略有序。通过时间戳+机器号+seq自增的设计实现了粗略有序,毫秒级内单机有序,多机无序,毫秒级间有序。作为InnoDB主键索引的话,降低页分裂的可能。

没有依赖于数据库等中间件,不受中间件限制。

雪花算法的缺点:

趋势递增:由于seq放在末位,会暴露业务信息,例如竞争对手可以通过ID判断出每天大致的业务量。

时间同步:依赖于系统时间,电子时间是需要同步的,例如每4年同步一次闰秒,可能会影响ID的生成。

4、开源项目——vesta-id-generator

自定义设计主要学习《可伸缩服务架构-框架与中间件》中的具体代码实现,snowflake的优化版。

书上提供的开源项目地址删除了,从github上找到了一个地址

采用两种粒度模式的ID:最大峰值型(秒级有序)、最小峰值型(毫秒级有序)

--两种类型的最大区别是:时间+序列号占用位数不同--最大峰值型(秒级有序)

--版本 : 1bit 0或1,默认0,一个版本可坚持30年,两个就是60年了

--类型 : 1bit 0或1,控制粒度

--生成方式 : 2bit 00或01或10或11:标识三种发布模式。

--秒级时间 : 30bit 秒级时间从0-2^30-1,2^30/60/60/24/365=34,可使用30年,毫秒级也是。

--序列号 : 20bit

--机器号 : 10bit 将序列号调整到机器号之前,避免递增趋势--最小峰值型(毫秒级有序)

--版本 : 1bit

--类型 : 1bit

--生成方式 : 2bit

--毫秒级 : 40bit

--序列号 : 10bit

--机器号 : 10bit

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值