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

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...

10-Oracle 23 ai Vector Search 概述和参数

一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI&#xff0c;使用客户端或是内部自己搭建集成大模型的终端&#xff0c;加速与大型语言模型&#xff08;LLM&#xff09;的结合&#xff0c;同时使用检索增强生成&#xff08;Retrieval Augmented Generation &#…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...