雪花算法生成id对MySQL影响_雪花算法(snowflake)生成Id重复问题

本文介绍了雪花算法的原理,包括其时间戳、工作机器ID和序列号的组成,以及如何在Java中实现。同时,讨论了在分布式部署中可能出现的ID重复问题,特别是当workerId超过预设位数时可能导致的问题。解决方案包括使用单例模式和线程安全的ID生成,以及动态分配workerId以避免冲突。
摘要由CSDN通过智能技术生成

前言

最近工作上遇到一个雪花算法生成Id重复导致数据库中表主键冲突,导致入库失败的问题,所以顺便学习了一下雪花算法,下面是学习的笔记以及讨论如果解决雪花算法在分布式部署中生成重复Id的问题。

基础概念

snowflake中文的意思是雪花,所以常被称为雪花算法

它是twitter用scala语言编写的一个用于简单规则运算就能高效生成唯一ID的算法,下面是源码地址:

网上还有各种其他语言的版本,思路基本上都是参考上述源码

特性

生成的ID不重复

生成性能高

基于时间戳,可以基本保证有序递增

设计原理

准备工作

bit与byte

bit(位):电脑中存储的最小单位,可以存储二进制中的0或1

byte(字节):一个byte由8个bit组成

如图:

71286e89e0c5

byte和bit.png

而在java中,每个数据类型存储所占的字节数不一样,常用的如下:

int:4 个字节。

short:2 个字节。

long:8 个字节。

byte:1 个字节。

float:4 个字节。

double:8 个字节。

char:2 个字节。

而雪花算法生成的数字,我们定义为long,所以就是8个byte,64bit

假设我们定义 long a = 1L;则在计算机中的存储如下:

71286e89e0c5

long类型的存储.png

也就是可表示的范围为:-9223372036854775808(-2的63次方) ~ 9223372036854775807(2的63次方-1),考虑到生成的唯一值用于数据库主键,所以理论值为0~9223372036854775807(2的63次方-1),容量上肯定能满足业务方了

组成原理

雪花算法生成的Id由:1bit 不用 + 41bit时间戳+10bit工作机器id+12bit序列号,如下图:

71286e89e0c5

雪花ID的组成

不用:1bit,因为最高位是符号位,0表示正,1表示负,所以这里固定为0

时间戳:41bit,服务上线的时间毫秒级的时间戳(为当前时间-服务第一次上线时间),这里为(2^41-1)/1000/60/60/24/365 = 49.7年

工作机器id:10bit,表示工作机器id,用于处理分布式部署id不重复问题,可支持2^10 = 1024个节点

序列号:12bit,用于离散同一机器同一毫秒级别生成多条Id时,可允许同一毫秒生成2^12 = 4096个Id,则一秒就可生成4096*1000 = 400w个Id

说明:上面总体是64位,具体位数可自行配置,如想运行更久,需要增加时间戳位数;如想支持更多节点,可增加工作机器id位数;如想支持更高并发,增加序列号位数

Java版本的具体实现

public class SnowflakeIdWorker {

/** 开始时间截 (建议用服务第一次上线的时间,到毫秒级的时间戳) */

private final long twepoch = 687888001020L;

/** 机器id所占的位数 */

private final long workerIdBits = 10L;

/** 支持的最大机器id,结果是1023 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */

private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

/** 序列在id中占的位数 */

private final long sequenceBits = 12L;

/** 机器ID向左移12位 */

private final long workerIdShift = sequenceBits;

/** 时间截向左移22位(10+12) */

private final long timestampLeftShift = sequenceBits + workerIdBits;

/** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)

* <

* */

private final long sequenceMask = -1L ^ (-1L << sequenceBits);

/** 工作机器ID(0~1024) */

private long workerId;

/** 毫秒内序列(0~4095) */

private long sequence = 0L;

/** 上次生成ID的时间截 */

private long lastTimestamp = -1L;

//===========================

使用雪花算法生成唯一整型ID主键的实现方法如下: 1.创建一个存储ID生成器状态的类SnowFlake,该类包含以下属性: - 起始的时间戳(用于计算时间戳部分)。 - 序列号(用于计算序列号部分)。 - 机器标识符(用于计算机器标识部分)。 2.在SnowFlake类中定义一个方法,用于生成ID。该方法包含以下步骤: - 获取当前时间戳。 - 计算时间戳部分。 - 计算机器标识部分。 - 计算序列号部分。 - 组合各部分生成最终的ID。 3.在需要生成ID的地方,创建一个SnowFlake实例,并调用其生成ID的方法即可。 示例代码如下: ```python import time class SnowFlake(object): def __init__(self, data_center_id, machine_id, epoch=1288834974657): self.data_center_id = data_center_id self.machine_id = machine_id self.sequence = 0 self.last_timestamp = -1 self.epoch = epoch def _gen_timestamp(self): return int(time.time() * 1000) def _gen_next_millis(self): timestamp = self._gen_timestamp() while timestamp <= self.last_timestamp: timestamp = self._gen_timestamp() return timestamp def _gen_sequence(self): self.sequence = (self.sequence + 1) & 4095 if self.sequence == 0: self.last_timestamp = self._gen_next_millis() return self.sequence def generate(self): timestamp = self._gen_timestamp() if timestamp < self.last_timestamp: raise ValueError('Clock moved backwards. Refusing to generate id for %d milliseconds' % (self.last_timestamp - timestamp)) elif timestamp == self.last_timestamp: sequence = self._gen_sequence() if sequence == 0: timestamp = self._gen_next_millis() else: self.sequence = 0 self.last_timestamp = timestamp timestamp -= self.epoch return (timestamp << 22) | (self.data_center_id << 17) | (self.machine_id << 12) | self.sequence # 示例使用 if __name__ == '__main__': snowflake = SnowFlake(1, 1) print(snowflake.generate()) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值