集群高并发情况下如何保证分布式唯一全局ID的生成之雪花算法(snowflake)(面试题)

集群高并发情况下如何保证分布式唯一全局ID的生成

1.ID生成规则部分硬性要求
1.1.全局唯一:不能出现重复id号,是唯一标识
1.2.趋势递增:在MySQL的InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用的Btree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能
1.3.单调递增:保证下一个Id一定大于上一个ID
1.4.信息安全:如果Id是连续的,恶意用户的扒取工作就非常 容易做了,直接按照顺序下载指定的URL即可;如果是订单号就更危险了,竞对可以直接知道我们一天的单量,所以在一些应用场景下,需要ID无规则不规则
1.5.含时间戳:在什么时间生成
2.ID号生成系统的可用性要求
2.1.高可用: 发一个获取分布式ID的请求,服务器就要保证99.999%的情况下给我创建 一个唯一分布式ID
2.2.低延迟:发一个获取分布式ID的请求,服务器就要快,极速
2.3.高QPS:假如并发一口气10万个创建分布式ID同时其你去进来,服务器要顶住压力并且创建10万个分布式Id

一般通用方案有三种
1.UUID
1.1 无序,无法预测他的生成顺序,不能生成递增有序的数字,首先分布式id一般作为主键,但是mysql官方推荐尽量越短越好,UUID每一个都很长,不推荐
1.2 ID作为主键时在特定环境会存在一些问题,比如DB主键场景下,UUID就不适用,mysql官方有明确建议主键尽量越短越好
1.3 索引,B+树索引分裂,既然分布式id是主键,然后主键时包含索引的.mysql的索引是通过b+树来实现的,每一次新的UUId数据的插入,为了查询的优化,都会对索引底层的b+树进行修改,因为UUID数据是无序的,所以每次UUID数据的插入都会对主键地域的b+树进行很大的修改,并且插入完全无序不但会导致一些中间节点产生分裂.也会白白创造很多不饱和的节点,大大降低数据库插入的性能
2.数据库自增主键
2.1. 单机可以
2.2. 集群分布式 :分布式中数据库的自增ID机制是和MySQL数据库的replace into实现的,它和insert功能类似,区别在于replace into 首先尝试插入数据列表中,如果发现表中已经有此行数据(根据主键或唯一索引进行判断)则先删除,再插入,否则直接插入新数据,但是由于系统水平扩展比较困难和数据库压力很大,所以不太合适作为分布式ID,
3.基于Redis生成全局id策略
3.1 redis5.0之前是单线的,天生保证原子性,可以使用原子操作INCR和INCRBY来实现,
3.2 集群式也不适合,因为在Redis集群情况下,同样和MySQL一样需要设置不同的增长步长,同时key一定要设置有效期,中小厂可以使用Redis集群来获取更高的吞吐量,但是维护Redis集群起来很麻烦

雪花算法
Twitter的分布式雪花算法Snowflake,经测试snowflake每秒能够产生26万个自增可排序的Id
1.Twitter的SnowFlake生成Id能够按照时间有序生成
2.SnowFlake算法生成id的结果是一个64bit大小的整数,为一个Long型(转换成字符串后长度最多19)
3.分布式系统内不会产生Id碰撞(由datecenter和workerld做区分)并且效率较高

核心组成部分结构:
1bit: 不用,因为二进制中最高位是符号位,1表示负数,0表示正数,生成的id一般都是整数,所以最高位固定为0
41bit时间戳位: 用来记录时间戳,毫秒级.
41位可以表示2^(41)-1个数字,如果用来表示正整数(计算机中正数包含0),可以表示的数值范围是: 0至 2(41)-1,减1是因为可表示的数值范围是从0开始计算的,而不是1,也就是说41位可以表示2(41)-1个毫秒的值,转换成单位年则是(2^(41)-1)/(1000606024365)=69年
10bit工作进程位: 用来记录工作机器id, 可以部署在2^{10} = 1024个节点,包括5位datecenterId和5位workerId, 5位(bit) 可以表示的最大正整数是2^{5}-1 = 31,即0. 1. 2. 3. …31这32个数字,来表示不同的datecenterId或者workerId
12bit序列号位: 序列号是用来记录铜毫秒内产生的不同id. 12位(bit) 可以表示的最大正整数是 2^{12}-1 = 4095,即可以用0 1 2 3…4094这4095个数字来表示统一机器同一时间戳(毫秒)内产生的4095个ID序号.

官网源码地址:https://github.com/twitter-archive/snowflake/releases/tag/snowflake-2010

用法:
1.糊涂工具包: https://github.com/dromara/hutool
2.springboot整合雪花算法

<dependency>
	<groupId>cn.hutool</groupId>
	<artifactId>hutool-all</artifactId>
	<version>5.0.1<version>
</dependency>

新建工具类

@Slf4j
@component
public class IdGeneratorSnowflake{
private long workerId = 0;
private long datacenterId = 1;
private cn.hutool.core.lang.Snowflake snowflake = cn.hutool.core.util.IdUtil.createSnowflake(workerId,datacenterId);
@import javax.annotation.PostConstruct;
public void init(){
	try{
	workerId = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr());
	Log.info("当前机器的workerId:{}" , workerId)
}catch(Exception e){
	e.printStackTrace();
	log.warn("当前机器的workerId获取失败" , e)
	workerId = NetUtil.getLocalhostStr().hashCode();
}
}
public synchronized long snowflakeId(){
	return snowflake.nextId();
}
public synchronized long snowflakeId(long workerId, long datacenterId){
cn.hutool.core.lang.Snowflake snowflake = cn.hutool.core.util.IdUtil.createSnowflake(workerId,datacenterId);
	return snowflake.nextId();
}
// 测试是否生效
public static void main(String[] args){
	new IdGeneratorSnowflake().snowflakeId();
}
}
// 在业务类中使用
@Service
public class OrderService{
	@Autowired
	private IdGeneratorSnowflake idGeneratorSnowflake;
	public String getIDBySnowFlake(){
		// 利用线程池,同时创建多个id
		ExecutorService threadPool = Executors.newFixedThreadPool(5);
		for(int i= 1; i<= 20 i++){
		threadPool.submit(() -> {
				system.out.printIn(idGeneratorSnowflake.snowflakeId());
});
}
	threadPool.shutdown();
	return "hello snowflake";	
}
}

优点:
毫秒数在高位,自增序列在低位,整个Id都是趋势递增的,不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成Id的性能也是非常高的,可以根据自身业务特性分配bit位,非常灵活.
缺点:
依赖机器时钟,如果机器时钟回拨,会导致重复id生成,在单机上是递增的,但是由于设计到分布式环境,每台机器上的时钟不可能完全同步,有时候会出现不是全局递增的情况,(此缺点可以认为无所谓,一般分布式Id只要求趋势递增,并不会严格要求递增,90%的需求都要求趋势递增)

如果遇到时钟回拨问题,有两个优化了雪花算法
1.百度开源的分布式唯一Id生成器 UidGenerator
2.Leaf – 美团点评分布式ID生成系统

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值