一种适合容器化部署的雪花算法ID生成器

雪花算法简介

SnowFlake 中文意思为雪花,故称为雪花算法。最早是 Twitter 公司在其内部用于分布式环境下生成唯一 ID。

image.png

雪花算法有以下几个优点:

  • 高并发分布式环境下生成不重复 id,每秒可生成百万个不重复 id。
  • 基于时间戳,以及同一时间戳下序列号自增,基本保证 id 有序递增。
  • 不依赖第三方库或者中间件。
  • 算法简单,在内存中进行,效率高。

雪花算法有如下缺点:

  • 依赖服务器时间,服务器时钟回拨时可能会生成重复 id。算法中可通过记录最后一个生成 id 时的时间戳来解决,每次生成 id 之前比较当前服务器时钟是否被回拨,避免生成重复 id。
  • 需要配置机器ID和服务器ID

[参考来源](SnowFlake 雪花算法详解与实现 - 掘金 (juejin.cn))

容器化部署雪花算法遇到的问题

  • 容器化无状态部署机器ID不可获取

    以前项目使用物理机器部署时,我们可以根据机器的IP分配对就的机器id,可是现在都是使用容器化部署,一般都是部署成无状态模式,无法获取workId;

  • 一个容器一般只部署一个服务,所有服务id可以不需要了。

解决思路

  • 将机器id和服务id合并
  • 项目启用时通过redis获取一个workId

RID的诞生

基本思路

  • 为每个微服务配置一个rid.redisKey,当作该服务在redis中的唯一标识服务
  • 在项目启用时,将rid.redisKey自增,获取到一个workId
  • 将wordId与maxWorkerId取余运算,得到单个服务的唯一workId

核心代码


/**
 * 基本Redis生成ID
 */
@Component
public class ID {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    private static Long REDIS_ID;
    /**
     * redis KEY 不同项目需要修改
     */
    @Value("${rid.redisKey}")
    private String ID_REDIS_KEY = "RID";
    /**
     * 起始时间戳
     */
    @Value("${rid.startStamp}")
    private Long startStamp = 1577808000000L;

    /**
     * 机器id所占的位数 最多机器节点2^5=32个
     */
    @Value("${rid.workerIdBits}")
    private final long workerIdBits = 5L;
    /**
     * 序列号所占的位数 决定单个容器每毫秒生成速度,默认每毫秒生成 2^7=128
     */
    @Value("${rid.sequenceBits}")
    private final long sequenceBits = 7L;
    /**
     * 时间戳位数 从startStamp开始可以  2^41/(1000606024365)=69,大概可以使用69年。
     */
    private final long timeStampBits = 41L;

    private Long workerId;

    @PostConstruct
    void init() {
        REDIS_ID = stringRedisTemplate.opsForValue().increment(ID_REDIS_KEY) % maxWorkerId;
    }


    /**
     * 时间戳最大值
     */
    private final long maxTimeStamp = ~(-1L << timeStampBits);
    /**
     * 机器id的最大值
     */
    private final long maxWorkerId = ~(-1L << workerIdBits);
    /**
     * 序列号的最大值
     */
    private final long maxSequence = ~(-1L << sequenceBits);


    private long sequence = 0L;
    private long lastTimeStamp = -1L;


    public synchronized long id() {
        long currentTimeStamp = timeGen();
        if (currentTimeStamp < lastTimeStamp) {
            throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimeStamp - currentTimeStamp));
        }
        if (lastTimeStamp == currentTimeStamp) {
            sequence = (sequence + 1) & maxSequence;
            if (sequence == 0) {
                currentTimeStamp = tilNextMillis(lastTimeStamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimeStamp = currentTimeStamp;
        return ((currentTimeStamp - startStamp) & maxTimeStamp) << (sequenceBits + workerIdBits) | (workerId << sequenceBits) | sequence;
    }


    private long tilNextMillis(long lastTimeStamp) {
        long timeStamp = timeGen();
        while (timeStamp <= lastTimeStamp) {
            timeStamp = timeGen();
        }
        return timeStamp;
    }


    private long timeGen() {
        return System.currentTimeMillis();
    }


    private static class SingletonHolder {
        private static ID ID;

        static {
            ID = new ID(REDIS_ID);
        }
    }

    public static ID getInstance() {
        return SingletonHolder.ID;
    }


    private ID() {
    }

    private ID(long workerId) {
        this.workerId = workerId;
    }
}
@Component
public class RID {

    public static Long generateId() {
        return ID.getInstance().id();
    }
}

测试代码

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Application.class})
public class SpringBootApplicationTests {

    @Test
    public void testId() {
        Set<Long> ids = new HashSet<>();
        int size = 1000000;
        long startTime=System.currentTimeMillis();
        for (int i = 0; i < size; i++) {
            ids.add(RID.generateId());
        }
        System.out.println(String.format("generate %d ids spend  %s ms",size,System.currentTimeMillis()-startTime));
        Assert.assertEquals(size, ids.size());
    }

}

优点

  • 无需配置机器ID,服务ID,可以用微服务的applicationId当作rid.redisKey,可无状态化部署
  • 单个微服务容器最大节点数量及生成速度可配置
  • ID大部分情况是自增了

存在问题

  • ID只能做到单个容器唯一,不可做到全局唯一
  • 2^workerIdBits> 2倍容器数据,容器节点重启会生成新的容器,然后替换原来老容器
  • 集群中如果有容器一直不重启,后面重启容器可能会分配到相当的workId导致ID重复
  • 生成的ID位数不固定 当前时间-startStamp 位数会随着时间增加,数据库不要用varchar类型
  • 服务器时间回拨,可能导致ID重复
  • ID不是严格全局自增,同一毫秒内rid.redisKey达到maxWorkerId后从0开始,可能导致生成ID比其它服务生成的小

最后

  • 这个ID生成器满足我们自己的需求,能不能满足你们的需求,自行评估
  • 个人能力有限,应该还是很多没考虑到的
  • 源码地址 一种适合容器化部署的雪花算法ID生成器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/6332.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

零编程经验,通过 GPT-4 十分钟开发了一个浏览器插件,并成功运行,实现了需求目标!

大佬蓝鸟ID: sundyme零编程经验&#xff0c;通过 GPT-4 十分钟开发了一个浏览器插件&#xff0c;并成功运行&#xff0c;实现了需求目标&#xff01;太不可思意了&#xff0c;真正体会到了自然语言编程的魅力&#xff01; 下一步是利用Pinterest 的 API 接口实现自动发图&#…

No.026<软考>《(高项)备考大全》【第10章】项目沟通和干系人管理(第2部分-干系人管理)

1 干系人管理部分相关 1.1 干系人ITO 1.2 干系人管理 过程过程的定义过程的作用识别干系人识别能影响项目决策、活动或结果的个人、群体或组织&#xff0c;以及被项目决策、活动或者结果影响的个人、群体或者组织&#xff0c;并分析和记录他们的相关信息的过程帮助项目经理建…

此战成硕,我成功上岸西南交通大学了~~~

友友们&#xff0c;好久不见&#xff0c;很长时间没有更一个正式点的文章了&#xff01; 是因为我在去年年底忙着准备初试&#xff0c;今年年初在准备复试&#xff0c;直到3月底拟录取后&#xff0c;终于可以写下这篇上岸贴&#xff0c;和大家分享一下考研至上岸的一个过程 文章…

游戏算法-游戏AI行为树,python实现

参考文章&#xff1a;Behavior trees for AI: How they work (gamedeveloper.com) 本文主要参考上述weizProject Zomboid 的开发者 Chris Simpson文章的概念&#xff0c;用伪代码实现代码例子 AI概述 游戏AI是对游戏内所有非玩家控制角色的行为进行研究和设计&#xff0c;使得游…

电子拣货标签9代系统简介

CK_Label_v9一、产品参数 产品型号 CK_Label_v9 LED 3&#xff08;红&黄&绿&#xff09;独立可控 供电方式 DC 24V 0.2A 通信方式 无线通信 蜂鸣器 支持 尺寸 D60 H307mm 二、革新点 配合标签拣货使用三个灯&#xff08;红黄绿&#xff09;都可以被独立控…

基于MATALB编程的BP神经网络手臂血管分类识别,基于BP神经网络的图像分类

目标 背影 BP神经网络的原理 BP神经网络的定义 BP神经网络的基本结构 BP神经网络的神经元 BP神经网络的激活函数&#xff0c; BP神经网络的传递函数 数据 神经网络参数 基于BP神经网络手臂血管识别的MATLAB代码 效果图 结果分析 展望 背影 随着人工智能的发展&#xff0c;智…

贪心算法-删数问题C++

目录 一、题目 二&#xff1a;思路 代码 运行结果 一、题目 有一个长度为n&#xff08;n < 240&#xff09;的正整数&#xff0c;从中取出k&#xff08;k < n&#xff09;个数&#xff0c;使剩余的数保持原来的次序不变&#xff0c;求这个正整数经过删数之后最小是多…

用Python绘制六种可视化图表,简直太好用了

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python资料、源码、教程: 点击此处跳转文末名片获取 可视化图表&#xff0c;有相当多种&#xff0c;但常见的也就下面几种&#xff0c;其他比较复杂一点&#xff0c;大都也是基于如下几种进行组合&#xff0c;变换出来的。 …

记录--CSS 如何实现羽化效果?

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 最近碰到这样一个问题&#xff0c;在一张封面上直接显示书名&#xff0c;可能会存在书名看不太清楚的情况(容易受到背景干扰)&#xff0c;如下 为了解决这个问题&#xff0c;设计师提了一个“究极”方…

算法 - 希尔排序

原理 首先将数组两两分组&#xff0c;分成n组数组&#xff0c;每组数组内部进行排序。再分成n/2组数组&#xff0c;每组数组内部进行排序。直至分成只剩一组&#xff0c;最后进行排序得到最后的数组。 代码 public static int[] shell(int[] arr) {int temp;for (int gra …

计算机图形学13:三维图形的几何变换

作者&#xff1a;非妃是公主 专栏&#xff1a;《计算机图形学》 博客地址&#xff1a;https://blog.csdn.net/myf_666 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录专栏推荐专栏系列文章序一、三维图形的几…

MongoDB综述【入门指南】

写这篇博客,正好是2023年4月5日15:29:31,是清明节,放假一天,我坐在我的小小租房室内,思考着没思考到啥,哈哈哈,感觉好着急啊!看完了一本《城南旧事》,但是就是不踏实,好吧~我来写一篇最近在学的一个技术 为了更优秀的自己~奥利给!! 首先,我们从最初级小白开始(因为自己也是小白…

卷麻了,00后测试用例写的比我还好,简直无地自容.....

前言 作为一个测试新人&#xff0c;刚开始接触测试&#xff0c;对于怎么写测试用例很头疼&#xff0c;无法接触需求&#xff0c;只能根据站在用户的角度去做测试&#xff0c;但是这样情况会导致不能全方位的测试APP&#xff0c;这种情况就需要一份测试用例了&#xff0c;但是不…

倾斜实景三维建模与BIM模型处理技术

倾斜实景三维建模与BIM模型处理技术 一、研究背景 ➢ 倾斜模型 ✓ 真实纹理&#xff0c;高分辨率 ✓ 真实坐标&#xff0c;高空间精度 ✓ 快速建模&#xff0c;低建模成本 ➢ BIM设计 BIM 技术作为建筑施工行业中的新技术&#xff0c;存在于建筑的全生命周期&#xff0c;服务…

机器学习算法:K近邻(k-nearest neighbors)初探

KNN的介绍和应用 KNN&#xff08;K-Nearest Neighbor&#xff09;算法是一种基于实例的学习算法&#xff0c;也是一种常见的分类算法。 如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别&#xff0c;则该样本也属于这个类别。 示例 &…

Python 3 基本数据类型,包含示例演示(初学友好)

嗨害大家好鸭~ 我是芝士❤ 有好好学习python吗&#xff1f; Python学习资料电子书 点击此处跳转文末名片获取 Python3 基本数据类型 Python 中的变量不需要声明。每个变量在使用前都必须赋值&#xff0c;变量赋值以后该变量才会被创建。 在 Python 中&#xff0c;变量就是变量…

MATLAB 求解定积分和不定积分

本文主要介绍如何通过matlab 去求解常见的定积分和不定积分的结果&#xff0c;使用matlab 内置函数 int。 语法&#xff1a; Fint(表达式&#xff0c;变量&#xff0c;变量上下限) 目录 例子1 单变量不定积分 例子2 多变量不定积分 例子3 单变量定积分 例子4 定积分近似求…

软件测试高频出现面试题-2(实用)

每日面试1、自我介绍2、简单介绍最近项目你是如何开展测试工作的呢&#xff1f;3、描述在这个项目里面你主要负责哪些模块的测试&#xff1f;选中一个业务复杂的讲述你是如何测试的呢&#xff1f;1、自我介绍 主要可以从三个方面准备&#xff1a;我的基础信息&#xff0c;我的…

OBCP第八章 OB运维、监控与异常处理-常见异常处理

问题排查概述&#xff1a;数据库连接问题排查 在遇到连接问题时&#xff0c;需要清楚整个系统的架构&#xff0c;对整个连接链路进行排查。通常情况下应用连接到数据库的完整链路是从应用服务器到 OBProxy 再到 OB 集群&#xff0c;此外还可能涉及负载均衡、DNS 解析、网络等。…

49天精通Java,第24天,Java链表、散列表、HashSet、TreeSet

目录一、链表二、散列表三、HashSet四、TreeSet五、TreeSet常用方法大家好&#xff0c;我是哪吒。 一、链表 从数组中间删除一个元素开销很大&#xff0c;其原因是向数组中插入元素时&#xff0c;此元素之后的所有元素都要向后端移动&#xff0c;删除时也是&#xff0c;数组中…