考虑分布式:如何有一个全局Id,保证Id唯一,考虑到分库分表之后,还可以保证这些全局Id的生成规则 保证一直性的。
一、基本要求:
1、全局唯一
2、趋向递增(满足mysql的索引的特点)
3、尽量保证下一个id比上一个高
4、信息安全方面;保证id不容易被猜到
5、包含时间戳(了解这个id的生成时间)
二、高性能需求:
1、高可用:不能出问题呀99.999%情况下要成功创建
2、低延迟:毫秒级
3、高并发:支持高QPS(Query Per Second) = 并发量/平均响应时间 ;同时10万的情况要生成id
三、雪花算法产生之前的的解决方法:
1、UUID: 32位的16进制,是JDK自带的工具类;唯一性可以实现,但是它无序
// UUID 36位的16进制的 0e8500ab-a173-4d54-9eaa-e81477ea4470
System.out.println(UUID.randomUUID());
无序可以导致什么问题呢?
TODO: 了解一下B+tree,会导致索引的分裂,索引不好创建,索引不好查询
Innodb存储引擎的特点主键可以实现聚簇索引,,数据存入索引叶子节点上,可以直接存储值,而且是从左向右递增的,但是UUID是无序的,就是导致索引出现分裂。
2、数据的自增主键: mysql 通过replace into实现,(replace into 表中索引如果存在就替换掉)
有序性,唯一性,递增,但是集群分布不可以不能实现并发执行,不合适大数据量,每次插入数据都需要读写数据库。
3、redis: redis6.0支持多线程,redis单线程的天生保证原子性,
但是需要设置不同的增长步长。但问题是,这里需要维护配置麻烦。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jwnx5GSW-1595769830530)(en-resource://database/3758:1)]
四、boss 雪花算法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4p8lv5rX-1595769830538)(en-resource://database/3762:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Mbj0knD-1595769830544)(en-resource://database/3764:1)]
64位Long类型的整形基础数据类型,
1、0符号位:最高位为0,表示取正数
2、41位时间戳:11111111111111111111111111111111111111111
3、转化成10进制:2199023255551L
public static void main(String[] args) {
Date date = new Date();
date.setTime(2199023255551L);
System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(date));
}
结果:2039-09-07
也就是可用到这个时候,这是当41位全部都满的情况
4、10位工作进程为:5+5也及时是workerId 和 datacenterId 机器码和机房码 也就是最好1024个节点(可以自己修改源码,根据业务修改对应的workerId和datacenterId的位数)
5、12位,序列号: 每台机器每毫秒内最多产生4096个序列号
PS:常见的使用:
1、HuTool
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.9</version>
</dependency>
/**
* @introduce: 使用huTool工具包,Id生成工具类
* @author: xingMeiLing
* @DATE: 2020/7/15
**/
public class Test3 {
private long workId= 0;
private long dataCenterId =1;
private Snowflake snowflake = IdUtil.createSnowflake(workId,dataCenterId);
@PostConstruct
public void init(){
try {
workId = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr());
} catch (Exception e){
workId = NetUtil.getLocalhostStr().hashCode();
}
}
public synchronized long snowflakeId(){
return snowflake.nextId();
}
public synchronized long snowflakeId(long workId,long dataCenterId){
Snowflake snowflake = IdUtil.createSnowflake(workId,dataCenterId);
return snowflake.nextId();
}
public static void main(String[] args) {
System.out.println(new Test3().snowflakeId());
}
}
优点:
• 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。
作为DB表的主键,索引效率高。
• 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。
高性能高可用:生成时不依赖于数据库,完全在内存中生成。
容量大:每秒中能生成数百万的自增ID。
• 可以根据自身业务特性分配bit位,非常灵活。
缺点:
• 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。
• 可能会出现不是全局递增的情况。
2、解决时钟回拨问题:
- 百度UidGenerator
- Leaf美团