为何重复

我们之前精通分布式,没听过SnowFlake?中提到,雪花算法在同一机器同一毫秒级,我们能生成4096个不同序列(12bit序列号情况下),即不同Id,但是如果我们使用的是微服务架构,那不同机器人是否会可能生成相同Id呢?

其实我们之前有提到工作机器Id的作用,就是用于解决分布式Id重复的问题,这个workerId是通过构造方法传入的,如果我们用10位来存储这个值,那就是最多支持1024个节点

    /*** 构造函数* @param workerId 工作ID (0~1023)*/public SnowflakeIdWorker(long workerId) {if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException(String.format("workerId can't be greater than %d or less than 0", maxWorkerId));}this.workerId = workerId;}

那么关键问题就回归到如何去把我们的服务器和workerId对应起来?如果不是容器化部署,部署是固定的机器,我们用机器的唯一名来做key,那我们可以对这些机器名和workerId建立一个对应关系,如果存在就用之前的workerId,不存在就往上累加比如我们用计算机名做key:

这样机器如果不断累加,最多支持1024台服务器,但是如果是容器化部署,需要支持动态增加节点,并且每次部署的机器不一定一样时,就会有问题,如果发现不同,就往上累加,经过多次发版,就可能会超过1023,这个时候生成雪花Id时,工作机器id左移12位后,当进行或运算时,时间戳的位置就会被影响,比如workerId=1024,我们拿之前的举例第1000ms,那它和第1001ms、workerId=0配置,可能生成重复的Id。来看看下面生成workerId的Demo:

        private static void Init(){if (worker == null){//初始化为1long workerId = 1;//得到服务器机器名称string hostName = System.Net.Dns.GetHostName();if (RedisHelper.Exists(hostName)){// 如果redis中存在改服务器名称,则直接取得workerIdworkerId =long.Parse(RedisHelper.Get(hostName));}else{//如果redis不存在,则用hashcode对32取模var code = hostName.GetHashCode();var Id = code % 32;//如果取模以后的Id,大于15,则从0~15中随机一个数字,也就是把16~31中转换到0~15,并存入redis//这里只给了4个bit存储workerId,所以只能支持0~15if (Id>15||Id<0){Id = new Random().Next(0, 15);}workerId = (long)Id;RedisHelper.Set(hostName, workerId);}//把workerId传入构造方法worker = new IdWorker(workerId);}}

上述代码有2个问题:

  1. hashcode对32取模,本身就可能会重复,比如460141958和3164804对32取模都是4,那生成的workerId就重复了
  2. 如果hashcode>15,随机取一个,那每次都有1/16的概率重复

如何解决重复

工作机器id:10bit,表示工作机器id,用于处理分布式部署id不重复问题,可支持2^10 = 1024个节点,我们只需要给同一个微服务分配不同的工作机器ID即可,在redis中存储一个当前workerId的最大值。每次生成workerId时,从redis中获取到当前workerId最大值,并+1作为当前workerId,并存入redis。如果workerId为1023,自增为1024,则重置0,作为当前workerId,并存入redis。

@Component
public class IdWorkConfig {private final RedisTemplate<String, Object> redisTemplate;private final String applicationName;public IdWorkConfig(RedisTemplate<String, Object> redisTemplate,@Value("${spring.application.name}") String applicationName) {this.redisTemplate = redisTemplate;this.applicationName = applicationName;}/*** 自定义workerId,保证该应用的ID不会重复** @return 新的id生成器*/@Beanpublic DefaultIdentifierGenerator defaultIdentifierGenerator() {String MAX_ID = applicationName + "-worker-id";Long maxId = this.getWorkerId(MAX_ID);String maxIdStr = Long.toBinaryString(maxId);// 将数据补全为10位maxIdStr = StringUtils.leftPad(maxIdStr, 10, "0");// 从中间进行拆分String datacenterStr = maxIdStr.substring(0, 5);String workerStr = maxIdStr.substring(5, 10);// 将拆分后的数据转换成dataCenterId和workerIdlong dataCenterId = Integer.parseInt(datacenterStr, 2);long workerId = Integer.parseInt(workerStr, 2);return new DefaultIdentifierGenerator(workerId, dataCenterId);}/*** LUA脚本获取workerId,保证每个节点获取的workerId都不相同** @param key 当前微服务的名称* @return workerId*/private Long getWorkerId(String key) {redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/redis_worker_id.lua")));redisScript.setResultType(Long.class);return redisTemplate.execute(redisScript, Collections.singletonList(key));}
}

lua脚本如下

local isExist = redis.call('exists', KEYS[1])
if isExist == 1 thenlocal workerId = redis.call('get', KEYS[1])workerId = (workerId + 1) % 1024redis.call('set', KEYS[1], workerId)return workerId
elseredis.call('set', KEYS[1], 0)return 0
end

雪花算法生成id重复问题相关推荐

  1. 线上使用雪花算法生成id重复问题

    项目中使用的是hutool工具类库提供的雪花算法生成id方式,版本使用的是5.3.1 <dependency><groupId>cn.hutool</groupId> ...

  2. mybatis-plus雪花算法生成Id使用详解

    文章目录 前言 一.mybatis-plus官网 二.雪花算法实战 1.建表 2.新建测试工程 3.单元测试 三.实现分析 四.为什么默认就是雪花算法 五.主动设置Id生成策略 六.内置的雪花算法工具 ...

  3. java 雪花算法生成ID

    一般情况,实现全局唯一ID,有三种方案,分别是通过中间件方式.UUID.雪花算法. 方案一,通过中间件方式,可以是把数据库或者redis缓存作为媒介,从中间件获取ID.这种呢,优点是可以体现全局的递增 ...

  4. 分布式系统中间件(Mogodb、ElasticSearch )雪花算法生成ID

    转载:https://www.cnblogs.com/jakeylove3/p/8446798.html 1.前言 关于如何在系统中生成唯一性ID的问题(如订单号.批次号等),一直困扰了许久.因为还要 ...

  5. windows、Linux两用Snowflake雪花算法生成ID,java工具类实现直接调用即可

    代码实现: public class SnowflakeManagerUtil {private static final long EPOCH_STAMP = 1262275200000L;priv ...

  6. DefaultIdentifierGenerator 雪花算法 生成 重复 id 解决办法

    DefaultIdentifierGenerator 雪花算法 生成 重复 id 前言 问题发生 排查原因 问题解决 前言 利用 mybatisplus 的 DefaultIdentifierGene ...

  7. 基于雪花算法生成用户id

    8.1 为啥这样做 1.全局唯一性,不会出现重复的id.如果通过id自增来保证id不重复,则该表 无法分表操作例如 服务器A的数据库的user表 数据如下1 小明 男2 小红 女2 张三 男此时 进行 ...

  8. python雪花算法生成id_理解分布式id生成算法SnowFlake

    分布式id生成算法的有很多种,Twitter的SnowFlake就是其中经典的一种. 概述 SnowFlake算法生成id的结果是一个64bit大小的整数,它的结构如下图: 1位,不用.二进制中最高位 ...

  9. 雪花算法及运用PHP,雪花算法生成全局唯一ID,参考了下网上雪花算法生成规则,机器ID和序列号自动获取 理论上毫秒可生成 1024*4096个唯一ID

    任务要求毫秒生成10000个唯一ID 研究了下twitter/snowflake的算法思想: 参考了下网上雪花算法生成规则,把数据中心和机器编号整合一起,变成10位机器ID, 机器ID和序列号自动获取 ...

最新文章

  1. SQL脚本--有关压缩数据库日志
  2. 旋转动画 rotate
  3. asp开发中存储过程应用全接触 _asp技巧
  4. 计组之总线:4、总线标准
  5. Google的YSlow——Page Speed(附插件下载)
  6. 蔚来ES8正式交付售价46.8万元起 与Model系相比扛打吗?
  7. queue的常见用法
  8. OpenCV图像几何变换——转置,镜像,倒置
  9. maven使用openjdk_openjdk8指定版本安装(maven指定版本安装)
  10. 家谱管理系统性能要求_应用在Mac上的家谱族谱工具
  11. MSDN 查找 步骤
  12. Hadoop 3.1.0 单机版伪分布式的搭建
  13. 创业公社:亦庄分中心开业 借好创业东风
  14. php设置pst时区,PHP时区标识符含义
  15. 关于js中获取div中的数据
  16. C语言:fscanf函数与fprintf函数——格式化读写函数
  17. 象yhoo相册那样在网页上操作图片(不完全)_1
  18. 2020牛客寒假算法基础集训营1总结
  19. Java APNS开源库apns4j-1.0.1发布
  20. Wireshark lua 插件简介

热门文章

  1. 《数据结构》03-树1 树的同构
  2. 2KAX五脚电源芯片,2KAX锂电池充电IC资料
  3. IPO前“紧急”分红7500万,上市对赌背后,这家功率半导体公司到底有多“硬”?...
  4. Coffee Movie= Happy Time!!
  5. 违反劳动法!去哪儿网因让员工加班被罚!
  6. android开发技术可行性,Flutter技术调研及可行性结论
  7. 制作一个简单的FLV播放器 【转】
  8. 5d6d论坛上插入音频视频的方法
  9. Gallery用法详解
  10. 【labview教程01】手把手教你安装labview2016