当前位置: 首页 > news >正文

分布式主键ID生成方式-snowflake雪花算法

这里写自定义目录标题

  • 一、业务场景
  • 二、技术选型
    • 1、UUID方案
    • 2、Leaf方案-美团(基于数据库自增id)
    • 3、Snowflake雪花算法方案
  • 总结

一、业务场景

大量的业务数据需要保存到数据库中,原来的单库单表的方式扛不住大数据量、高并发,需要分库分表;这样原来的数据库自增id作为主键就不能满足业务需求,需要有一个在分库分表中的唯一标识id作为主键,这个id需要有如下要求:

  • 全局唯一性
  • 趋势递增:在MySQL的InnoDB中使用的是聚焦索引,用的是B-tree的数据结构来存储索引数据,所以要尽量选用有序的主键来保证写入性能
  • 信息安全:不能通过主键看出业务信息

二、技术选型

1、UUID方案

uuid是32位数的16进制数字所构成,以连字号分为五段,总共有 36个字符(即三十二个英数字母和四个连字号),550e8400-e29b-41d4-a716-446655440000,所以理论上uuid的总数有16^32,基本用不完。
优点: 性能非常强,本地内存生成。
缺点: 36个字符串存储,太长了,而且生成的id是无序的,MySQL要求主键越短越好,同时要有序,保证索引的写入性能。

2、Leaf方案-美团(基于数据库自增id)

  1. 在数据库中设计一张表用于生成自增id
    | biz_tag | maxId | step
    | user_tab | 2000 | 1000
    | home_tab | 3000 | 2000
    biz_tag是业务表名用来区分业务,maxId是目前所被分配的id号段的最大值,step是每次分配的长度。
  2. Leaf微服务从数据库中一次取step个号码端,比如step为1000,则每次取1000个到Leaf服务内存中,用于应用层调用接口获取主键id,每调一次加一,内存中的1000个用完后,Leaf再去数据库取一次号码段(取的时候maxId也会相应更新)。
  3. 这样Leaf服务和数据库交互频率就大大减少,性能瓶颈就不在数据库,而在于Leaf微服务,而Leaf服务是无状态的,因此可以根据实际需求横向扩展,可以部署多个Leaf微服务用于获取主键id
    架构图
    优点:
  • 扩展性好,可以随着业务的发展线性扩展多个Leaf服务
  • 生成的主键id是趋势递增的8byte的64位数,符合数据库存储的主键id要求
  • 容灾性好,即使DB宕机一会,Leaf服务内存中缓存的号码段可以支撑一段时间等待DB恢复
  • maxId可以自定义大小,方便其他业务ID迁移到Leaf服务。
    缺点:
  • ID不够随机,安全性不够
  • TP999性能波动大,当某一时刻,多个Leaf服务的号码段都使用到999最后一个时,同时调用数据库获取号码端,会造成偶尔的突刺,导致获取主键ID延迟。
  • DB数据库长时间宕机会导致整个服务不可用。
    优化:
    争对TP999,可以采用提前获取号码段(双buffer)的方式,当Leaf内存中还剩下指定的号码时(eg:800),就提前获取下1000个号码段放到内存中,即Leaf服务内部有两个号段缓存区segment,这样当数据库调用延迟,需要等待时,Leaf服务还有号码段可以对外提供服务。
    DB数据库可以采用一主两从的方式,或者用多机房,提高容灾性。

3、Snowflake雪花算法方案

Snowflake算法可以生成64位的ID,刚好可以用Long型存储,并且生成的ID有大致的顺序;它以划分命名空间的方式,讲64-bit位划分为4个部分:
0 - 41位时间戳 - 10位机器id - 12位序列号
示意图

  1. 第一位是符号位,不用。
  2. 第二部分是41位的时间戳,可以表示2^41个数,每个数代表毫秒(ms)。
  3. 第三部分是10位的机器id,即2^10=1024台机器,实际中用不到这么多机器,可以进一步细分,加上机房信息,或者业务信息。
  4. 第四部分是12位的自增序列,2^12=4096个数,即理论上1ms内一台机器支持4096个请求。

Java实现:

/*** twitter的snowflake算法 -- java实现* */
public class SnowFlake {/*** 每一部分占用的位数*/private final static long SEQUENCE_BIT = 12; //序列号占用的位数private final static long MACHINE_BIT = 5;   //机器标识占用的位数private final static long DATACENTER_BIT = 5;//数据中心占用的位数/*** 每一部分的最大值*/private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);/*** 每一部分向左的位移*/private final static long MACHINE_LEFT = SEQUENCE_BIT;private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;private long datacenterId;  //数据中心private long machineId;     //机器标识private long sequence = 0L; //序列号private long lastStmp = -1L;//上一次时间戳public SnowFlake(long datacenterId, long machineId) {if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");}if (machineId > MAX_MACHINE_NUM || machineId < 0) {throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");}this.datacenterId = datacenterId;this.machineId = machineId;}/*** 产生下一个ID** @return*/public synchronized long nextId() {long currStmp = getNewstmp();if (currStmp < lastStmp) {// 时钟回拨问题怎么处理?throw new RuntimeException("Clock moved backwards.  Refusing to generate id");}if (currStmp == lastStmp) {//相同毫秒内,序列号自增sequence = (sequence + 1) & MAX_SEQUENCE;//同一毫秒的序列数已经达到最大if (sequence == 0L) {currStmp = getNextMill();}} else {//不同毫秒内,序列号置为0sequence = 0L;}lastStmp = currStmp;return (currStmp << TIMESTMP_LEFT) //时间戳部分| (datacenterId << DATACENTER_LEFT)       //数据中心部分| (machineId << MACHINE_LEFT)             //机器标识部分| sequence;                             //序列号部分}private long getNextMill() {long mill = getNewstmp();while (mill <= lastStmp) {mill = getNewstmp();}return mill;}private long getNewstmp() {return System.currentTimeMillis();}public static void main(String[] args) {SnowFlake snowFlake = new SnowFlake(1, 1);for (int i = 0; i < (1 << 12); i++) {System.out.println(snowFlake.nextId());}}
}

时钟回拨问题:
当某台机器的时间出现了问题,回到了前几秒,则调用该机器雪花算法时,会生成重复的id,因为前几秒是时间已经生成过id了。
根据实际的业务和机器的情况不同,有几种解决方案:

  1. 当回拨的时间不长,比如不到100ms,小于接口调用超时时间,则可以用sleep方法等待时间正常。
  2. 当回拨时间适中,比如100ms~1s内,等待的话会接口超时,这种情况,可以将前一秒的每个ms的最大序列号维护在缓存中,然后maxId+1。
  3. 当回拨时间比较长,可以重试调用其他机器生成id,等过一段时间再调用该机器。或者直接把这个机器下线掉。

雪花算法生成架构:
在这里插入图片描述
部署多个服务,通过服务发现被应用服务调用,同时机器id可以动态通过zookeeper(可以生成自增序列)获取。

总结

分布式主键id的生成方式有很多种,最重要的是根据自己的实际业务情况来选择最合适自己的一种方式。

相关文章:

分布式主键ID生成方式-snowflake雪花算法

这里写自定义目录标题 一、业务场景二、技术选型1、UUID方案2、Leaf方案-美团&#xff08;基于数据库自增id&#xff09;3、Snowflake雪花算法方案 总结 一、业务场景 大量的业务数据需要保存到数据库中&#xff0c;原来的单库单表的方式扛不住大数据量、高并发&#xff0c;需…...

深入理解感知机(Perceptron)算法

深入理解感知机(Perceptron)算法 1. 引言 感知机是神经网络和深度学习的基石,由Frank Rosenblatt在1957年提出。它模拟了生物神经元的基本特征,是一个简单但重要的二分类线性分类器。本文将从数学原理到实际应用,全面介绍感知机算法。 2. 数学基础 2.1 定义 感知机是一…...

操作系统——死锁与饥饿

死锁的概念 死锁产生的条件 前三种条件可能会产生死锁&#xff0c;第四种条件&#xff08;环路&#xff09;可能会产生死锁 机器检测是否死锁是——检测是否有环路 解决死锁 以上预防死锁的方法不太实用&#xff0c;低效 银行家算法 P2运行完后可用队列就变成了 6 2 3…...

【算法】字符串算法技巧系列

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 引入&#xff1a;字符串相关算法技巧 1&#xff1a;字符串转数组 2&#xff1a;子字符串 3&#xff…...

Vue中el-tree结合vuedraggable实现跨组件元素拖拽

实现效果&#xff1a; 左侧el-tree: <template><el-treeclass"filter-tree":data"treeData":props"defaultProps":filter-node-method"filterNode"node-key"id"draggable:allow-drop"allowDrop"node-dr…...

湘潭大学人机交互复习

老师没给题型也没划重点&#xff0c;随便看看复习了 什么是人机交互 人机交互&#xff08;Human-Computer Interaction&#xff0c;HCI&#xff09;是关于设计、评价和实现供人们使用的交互式计算机系统&#xff0c;并围绕相关的主要现象进行研究的学科。 人机交互研究内容 …...

基于ADAS 与关键点特征金字塔网络融合的3D LiDAR目标检测原理与算法实现

一、概述 3D LiDAR目标检测是一种在三维空间中识别和定位感兴趣目标的技术。在自动驾驶系统和先进的空间分析中&#xff0c;目标检测方法的不断演进至关重要。3D LiDAR目标检测作为一种变革性的技术&#xff0c;在环境感知方面提供了前所未有的准确性和深度信息. 在这里&…...

Kivy App开发之UX控件DropDown下拉列表

怎样在kivy中实现下拉列表的功能? 在kivy中,下拉列表的定位是自动的,即列表展开的位置根据上下方是否有控件自动调整,且可以包含其他控件,如按钮,图片等。 在应用中,需要使用base包下的runTouchApp类,用于触发下拉框。 DropDown控件常见的属性如下 属性相关说明auto_…...

机器学习模型评估指标

模型的评估指标是衡量一个模型应用于对应任务的契合程度&#xff0c;常见的指标有&#xff1a; 准确率&#xff08;Accuracy&#xff09;: 正确预测的样本数占总样本数的比例。适用于类别分布均衡的数据集。 精确率&#xff08;Precision&#xff09;: 在所有被预测为正类的样…...

C# 特性

总目录 C# 语法总目录 C# 特性 特性1. 特性类自定义格式2. 特性的位置参数和命名参数3. 特性的目标4. 指定多个特性5. 调用者信息特性 特性 1. 特性类自定义格式 自定义特性类需要继承自Attribute类&#xff0c;特性使用通常都会省略名字后面的Attribute&#xff0c;会自动识…...

Reactor测试框架之StepVerifier

Reactor测试框架之StepVerifier 测试步骤1、创建StepVerifier实例2、添加断言3、执行验证 代码实例 在响应式编程中&#xff0c;Reactor框架提供了StepVerifier测试类&#xff0c;用于对响应式序列进行断言和验证。StepVerifier主要用于对Publisher发出的元素序列进行逐步的、精…...

k8s helm部署kafka集群(KRaft模式)——筑梦之路

添加helm仓库 helm repo add bitnami "https://helm-charts.itboon.top/bitnami" --force-update helm repo add grafana "https://helm-charts.itboon.top/grafana" --force-update helm repo add prometheus-community "https://helm-charts.itboo…...

unity action委托举例

using System; using UnityEngine; public class DelegateExample : MonoBehaviour { void Start() { // 创建委托实例并添加方法 Action myAction Method1; myAction Method2; myAction Method3; // 调用委托&#xff0c;会依次执…...

conda 批量安装requirements.txt文件

conda 批量安装requirements.txt文件中包含的组件依赖 conda install --yes --file requirements.txt #这种执行方式&#xff0c;一遇到安装不上就整体停止不会继续下面的包安装。 下面这条命令能解决上面出现的不执行后续包的问题&#xff0c;需要在CMD窗口执行&#xff1a; 点…...

Flutter:封装一个自用的bottom_picker选择器

效果图&#xff1a;单列选择器 使用bottom_picker: ^2.9.0实现&#xff0c;单列选择器&#xff0c;官方文档 pubspec.yaml # 底部选择 bottom_picker: ^2.9.0picker_utils.dart AppTheme&#xff1a;自定义的颜色 TextWidget.body Text() <Widget>[].toRow Row()下边代…...

Group3r:一款针对活动目录组策略安全的漏洞检测工具

关于Group3r Group3r是一款针对活动目录组策略安全的漏洞检测工具&#xff0c;可以帮助广大安全研究人员迅速枚举目标AD组策略中的相关配置&#xff0c;并识别其中的潜在安全威胁。 Group3r专为红蓝队研究人员和渗透测试人员设计&#xff0c;该工具可以通过将 LDAP 与域控制器…...

支持向量机算法(一):像讲故事一样讲明白它的原理及实现奥秘

1、支持向量机算法介绍 支持向量机&#xff08;Support Vector Machine&#xff0c;SVM&#xff09;是一种基于统计学习理论的模式识别方法&#xff0c; 属于有监督学习模型&#xff0c;主要用于解决数据分类问题。SVM将每个样本数据表示为空间中的点&#xff0c;使不同类别的…...

力扣-数组-35 搜索插入位置

解析 时间复杂度要求&#xff0c;所以使用二分的思想&#xff0c;漏掉了很多问题&#xff0c;这里记录 在left-right1时&#xff0c;已经找到了插入位置&#xff0c;但是没有赋值&#xff0c;然后break&#xff0c;所以导致一直死循环。 if(right - left 1){result right;b…...

List ---- 模拟实现LIST功能的发现

目录 listlist概念 list 中的迭代器list迭代器知识const迭代器写法list访问自定义类型 附录代码 list list概念 list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。list的底层是双向链表结构&#xff0c;双向链表中每个元素…...

HashMap和HashTable区别问题

并发&#xff1a;hashMap线程不安全&#xff0c;hashTable线程安全&#xff0c;底层在put操作的方法上加了synchronized 初始化&#xff1a;hashTable初始容量为11&#xff0c;hashmap初始容量为16 阔容因子&#xff1a;阔容因子都是0.75 扩容比例&#xff1a; 补充 hashMap…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案&#xff1a;Java 字节码技术实战分享&#xff08;仅供学习&#xff09; 一、Aspose.PDF 简介二、说明&#xff08;⚠️仅供学习与研究使用&#xff09;三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...

C++.OpenGL (20/64)混合(Blending)

混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...

scikit-learn机器学习

# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...