当前位置: 首页 > 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…...

Qwen3.5-2B网络编程应用:构建基于WebSocket的实时多模态聊天服务

Qwen3.5-2B网络编程应用&#xff1a;构建基于WebSocket的实时多模态聊天服务 1. 实时聊天服务的价值与挑战 想象一下这样的场景&#xff1a;电商客服需要同时处理图片咨询和文字提问&#xff0c;在线教育平台要实时解答学生上传的题目截图&#xff0c;或是设计团队需要AI即时…...

Java 并发原子类完全指南:Atomic 全家桶、CAS/JMM、ABA、LongAdder、源码阅读路线与经典实战

多线程编程中&#xff0c;count 这样简单的操作都不是线程安全的。用 synchronized 能解决问题&#xff0c;但锁会带来阻塞和上下文切换开销。java.util.concurrent.atomic 包提供了一套基于 CAS&#xff08;Compare-And-Swap&#xff09;的无锁并发工具&#xff0c;在“单变量…...

Phi-4-mini-reasoning在中小学数学辅导中的应用:自动解题与答案验证

Phi-4-mini-reasoning在中小学数学辅导中的应用&#xff1a;自动解题与答案验证 1. 模型介绍 Phi-4-mini-reasoning是一款专注于推理任务的文本生成模型&#xff0c;特别擅长处理数学题、逻辑题等需要多步分析和简洁结论输出的场景。与通用聊天模型不同&#xff0c;它更专注于…...

SmallThinker-3B-Preview代码生成效果展示:Java八股文智能问答实例

SmallThinker-3B-Preview代码生成效果展示&#xff1a;Java八股文智能问答实例 最近在技术社区里&#xff0c;一个名为SmallThinker-3B-Preview的模型引起了我的注意。它主打代码生成和智能问答&#xff0c;特别是针对编程面试中那些经典的“八股文”问题。作为经常参与面试和…...

RVC语音转换案例分享:多种音色克隆效果展示与对比

RVC语音转换案例分享&#xff1a;多种音色克隆效果展示与对比 1. RVC语音转换技术概述 RVC&#xff08;Retrieval-based-Voice-Conversion&#xff09;是一种基于检索的语音转换技术&#xff0c;它能够通过深度学习模型实现高质量的语音音色克隆和转换。这项技术的核心价值在…...

SolidWorks 2019 + Fusion 360:手把手教你搞定复杂机械臂模型的URDF导出(附开源模型)

SolidWorks与Fusion 360协同工作流&#xff1a;机械臂模型URDF导出实战指南 当你在GitHub上发现一个设计精良的六轴机械臂模型&#xff0c;却因为格式兼容性问题无法直接使用时&#xff0c;这种挫败感每个机器人开发者都深有体会。上周我就遇到了这样的情况——一个基于Gluon架…...

Pagefind静态搜索库:10个关键技巧实现大规模网站的高效低带宽搜索

Pagefind静态搜索库&#xff1a;10个关键技巧实现大规模网站的高效低带宽搜索 【免费下载链接】pagefind Static low-bandwidth search at scale 项目地址: https://gitcode.com/gh_mirrors/pa/pagefind Pagefind是一款革命性的静态搜索库&#xff0c;专为大规模网站设计…...

ESPDateTime:面向ESP32/ESP8266的轻量级NTP时间同步库

1. 项目概述 ESPDateTime 是一款专为 ESP8266 和 ESP32 平台设计的轻量级日期时间管理库&#xff0c;其核心目标并非替代 POSIX time.h 的完整实现&#xff0c;而是解决嵌入式物联网设备在资源受限、无 RTC 硬件备份、网络连接不稳定等现实约束下&#xff0c; 可靠获取、同…...

ESPS USB MSC 调试全过程记录酪

背景 在软件开发的漫长旅途中&#xff0c;"构建"这个词往往让人又爱又恨。爱的是&#xff0c;一键点击&#xff0c;代码变成产品&#xff0c;那是程序员最迷人的时刻&#xff1b;恨的是&#xff0c;维护那一堆乱糟糟的构建脚本&#xff0c;简直是噩梦。 在很多项目中…...

初次学C语言编程(2)

上节课内容补充在上节课中的转义字符中\ddd 表示一个三个数字的八进制的数字 例如\130 十进制的ASCII是88 表示字符X\xdd表示的是一个两个数字的十六进制的数字 例如\x30 十进制ASCII是48 表示字符0\0表示null 没有字符 ASCII码是0&#xff0c;用于字符串的结束符号一、C…...