分布式ID生成方案详解

                         ✨✨ 祝屏幕前的您天天开心 ,每天都有好运相伴。我们一起加油!✨✨ 
                                           🎈🎈作者主页: 喔的嘛呀🎈🎈

目录

引言

一. UUID(Universally Unique Identifier)

UUID版本

版本1 UUID

版本4 UUID

UUID用途

二、数据库自增ID

三. 基于Redis的方案

四. Twitter的snowflake算法

五、百度UidGenerator

结语


引言

在分布式系统中,生成唯一标识符(ID)是一个常见的需求。在这篇博客中,我们将介绍几种常见的分布式ID生成方案,包括UUID、Snowflake算法、基于数据库的方案和基于Redis的方案。我们将深入探讨每种方案的原理、优缺点,并提供相应的代码示例。

一. UUID(Universally Unique Identifier)

UUID(Universally Unique Identifier)是一种标准化的128位数字(16字节)格式,通常用32个十六进制数字表示。UUID的目的是让分布式系统中的多个节点生成的标识符在时间和空间上都是唯一的。

UUID通常由以下几部分组成:

  1. 时间戳:占据前32位,表示生成UUID的时间戳。
  2. 时钟序列号:占据接下来的16位,保证在同一时刻生成的UUID的唯一性。
  3. 全局唯一的节点标识符:占据最后的48位,通常是机器的MAC地址。

UUID的生成方法有多种,其中比较常见的是基于当前时间戳和随机数生成。Java中可以使用java.util.UUID类来生成UUID,示例如下:

import java.util.UUID;public class UUIDGenerator {public static void main(String[] args) {UUID uuid = UUID.randomUUID();System.out.println("Generated UUID: " + uuid.toString());}
}

这段代码将生成一个类似于550e8400-e29b-41d4-a716-446655440000的UUID。由于UUID的唯一性和随机性,通常用于分布式系统中的唯一标识符,例如作为数据库表的主键。 

二、数据库自增ID


使用数据库的id自增策略,如 MySQL 的 auto_increment。并且可以使用两台数据库分别设置不同
步长,生成不重复ID的策略来实现高可用。
优点:数据库生成的ID绝对有序,高可用实现方式简单
缺点:需要独立部署数据库实例,成本高,有性能瓶颈

在许多关系型数据库中,自增ID是一种常见的用于唯一标识表中记录的方式。下面我将以MySQL为例,介绍如何在数据库中使用自增ID。

首先,我们需要创建一个带有自增ID的表。以下是一个简单的示例表的创建语句:

CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(50) NOT NULL,email VARCHAR(100) NOT NULL
);

在这个例子中,id 列被定义为自增列,并且被指定为主键。每次向表中插入一条记录时,id 列都会自动递增,确保每个记录都有唯一的ID。

接下来,我们可以通过插入数据来演示自增ID的工作原理:

INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com');
INSERT INTO users (name, email) VALUES ('Charlie', 'charlie@example.com');

查询表中的数据:

SELECT * FROM users;

输出应该类似于:

+----+---------+------------------+
| id | name    | email            |
+----+---------+------------------+
| 1  | Alice   | alice@example.com|
| 2  | Bob     | bob@example.com  |
| 3  | Charlie | charlie@example.com|
+----+---------+------------------+

每次插入一条记录时,id 列都会自动递增。这就是自增ID的基本工作原理。

三. 基于Redis的方案

Redis的所有命令操作都是单线程的,本身提供像 incr 和 increby 这样的自增原子命令,所以能保
证生成的 ID 肯定是唯一有序的。
优点:不依赖于数据库,灵活方便,且性能优于数据库;数字ID天然排序,对分页或者需要排
序的结果很有帮助。
缺点:如果系统中没有Redis,还需要引入新的组件,增加系统复杂度;需要编码和配置的工作
量比较大。
考虑到单节点的性能瓶颈,可以使用 Redis 集群来获取更高的吞吐量。假如一个集群中有5台
Redis。可以初始化每台 Redis 的值分别是1, 2, 3, 4, 5,然后步长都是 5。

在 Redis 中生成自增 ID 通常可以通过使用 INCR 命令实现。INCR 命令会将存储在指定键中的数字递增 1,并返回递增后的值。你可以利用这个特性来实现一个简单的自增 ID 生成器。以下是一个基本的示例:

import redis.clients.jedis.Jedis;public class RedisIdGenerator {private Jedis jedis;public RedisIdGenerator() {this.jedis = new Jedis("localhost");}public long getNextId(String key) {return jedis.incr(key);}public static void main(String[] args) {RedisIdGenerator idGenerator = new RedisIdGenerator();String key = "my_id_counter";// 使用示例for (int i = 0; i < 5; i++) {long id = idGenerator.getNextId(key);System.out.println("Generated ID: " + id);}}
}

在这个示例中,我们首先创建了一个 RedisIdGenerator 类,该类包含一个 getNextId 方法,用于生成下一个自增 ID。在 main 方法中,我们创建了一个实例,并连续调用 getNextId 方法来生成 ID。

需要注意的是,这只是一个简单的示例。在实际应用中,你可能需要考虑并发访问时的线程安全性,以及如何处理 Redis 连接的创建和关闭等问题。

四. Twitter的snowflake算法

Twitter的Snowflake算法是一种用于生成分布式唯一ID的算法,它可以在分布式系统中生成全局唯一的ID。Snowflake算法的核心思想是将一个64位的long型的ID分成多个部分,包括时间戳、机器ID和序列号。具体来说,Snowflake算法的ID结构如下:

 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| unused |   timestamp   |   worker ID  | sequence
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  • 位表示未使用的位,可根据需要保留或用于其他用途。
  • 41位表示时间戳,可以表示的时间范围为2^41 / 1000 / 60 / 60 / 24 = 69年左右。
  • 10位表示机器ID,可以用来区分不同的机器。
  • 12位表示序列号,可以用来区分同一机器同一时间戳内生成的不同ID。

Snowflake算法生成ID的过程如下:

  1. 获取当前时间戳,单位是毫秒。
  2. 使用配置的机器ID。
  3. 如果当前时间戳与上一次生成ID的时间戳相同,则使用序列号加1;否则序列号重置为0。
  4. 将时间戳、机器ID和序列号合并生成最终的ID。

Snowflake算法的优点是生成的ID是递增的、趋势递增的,并且可以根据需要提取出生成ID的时间戳和机器ID。然而,Snowflake算法也有一些缺点,例如在高并发情况下可能会出现ID重复的情况,需要适当的措施来避免这种情况的发生。

Snowflake 算法是 Twitter 开源的一种分布式唯一 ID 生成算法,用于生成全局唯一的 ID。它的核心思想是将 ID 分为不同的部分,包括时间戳、机器 ID 和序列号。下面是一个详细的实现:

public class SnowflakeIdGenerator {private final long twepoch = 1288834974657L; // 起始时间戳,可以根据实际需求调整private final long workerIdBits = 5L; // 机器 ID 的位数private final long datacenterIdBits = 5L; // 数据中心 ID 的位数private final long maxWorkerId = -1L ^ (-1L << workerIdBits); // 最大机器 IDprivate final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); // 最大数据中心 IDprivate final long sequenceBits = 12L; // 序列号的位数private final long workerIdShift = sequenceBits; // 机器 ID 左移位数private final long datacenterIdShift = sequenceBits + workerIdBits; // 数据中心 ID 左移位数private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; // 时间戳左移位数private final long sequenceMask = -1L ^ (-1L << sequenceBits); // 序列号掩码private long workerId;private long datacenterId;private long sequence = 0L;private long lastTimestamp = -1L;public SnowflakeIdGenerator(long workerId, long datacenterId) {if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException("Worker ID 必须介于 0 和 " + maxWorkerId + " 之间");}if (datacenterId > maxDatacenterId || datacenterId < 0) {throw new IllegalArgumentException("Datacenter ID 必须介于 0 和 " + maxDatacenterId + " 之间");}this.workerId = workerId;this.datacenterId = datacenterId;}public synchronized long nextId() {long timestamp = timeGen();if (timestamp < lastTimestamp) {throw new RuntimeException("时钟回拨发生在 " + (lastTimestamp - timestamp) + " 毫秒内");}if (timestamp == lastTimestamp) {sequence = (sequence + 1) & sequenceMask;if (sequence == 0) {timestamp = tilNextMillis(lastTimestamp);}} else {sequence = 0L;}lastTimestamp = timestamp;return ((timestamp - twepoch) << timestampLeftShift)| (datacenterId << datacenterIdShift)| (workerId << workerIdShift)| sequence;}private long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}private long timeGen() {return System.currentTimeMillis();}public static void main(String[] args) {SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);// 使用示例for (int i = 0; i < 5; i++) {long id = idGenerator.nextId();System.out.println("Generated ID: " + id);}}
}

在这个实现中,我们首先定义了 Snowflake 算法中需要用到的各种参数和位移操作。然后,我们实现了一个 nextId 方法来生成下一个 ID。在 main 方法中,我们创建了一个 SnowflakeIdGenerator 实例,并连续调用 nextId 方法来生成 ID。

需要注意的是,Snowflake 算法中的时间戳部分可以根据实际需求进行调整,以确保生成的 ID 在不同时间内仍然是唯一的。

五、百度UidGenerator

百度的 UIDGenerator 是一个分布式唯一 ID 生成器,类似于 Twitter 的 Snowflake 算法,但在细节上有所不同。以下是一个简化的实现,展示了其基本原理:

import java.util.concurrent.atomic.AtomicLong;public class BaiduUidGenerator {private final long twepoch = 1288834974657L; // 起始时间戳,可以根据实际需求调整private final long workerIdBits = 10L; // 机器 ID 的位数private final long sequenceBits = 12L; // 序列号的位数private final long workerIdShift = sequenceBits; // 机器 ID 左移位数private final long timestampLeftShift = sequenceBits + workerIdBits; // 时间戳左移位数private final long sequenceMask = -1L ^ (-1L << sequenceBits); // 序列号掩码private final long workerId;private volatile long lastTimestamp = -1L;private volatile long sequence = 0L;public BaiduUidGenerator(long workerId) {if (workerId < 0 || workerId >= (1 << workerIdBits)) {throw new IllegalArgumentException("Worker ID 必须介于 0 和 " + ((1 << workerIdBits) - 1) + " 之间");}this.workerId = workerId;}public synchronized long nextId() {long timestamp = timeGen();if (timestamp < lastTimestamp) {throw new RuntimeException("时钟回拨发生在 " + (lastTimestamp - timestamp) + " 毫秒内");}if (timestamp == lastTimestamp) {sequence = (sequence + 1) & sequenceMask;if (sequence == 0) {timestamp = tilNextMillis(lastTimestamp);}} else {sequence = 0L;}lastTimestamp = timestamp;return ((timestamp - twepoch) << timestampLeftShift)| (workerId << workerIdShift)| sequence;}private long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}private long timeGen() {return System.currentTimeMillis();}public static void main(String[] args) {BaiduUidGenerator uidGenerator = new BaiduUidGenerator(1);// 使用示例for (int i = 0; i < 5; i++) {long id = uidGenerator.nextId();System.out.println("Generated ID: " + id);}}
}

在这个实现中,我们首先定义了 BaiduUidGenerator 类,其中包含了与 Snowflake 算法类似的参数和位移操作。然后,我们实现了一个 nextId 方法来生成下一个 ID。在 main 方法中,我们创建了一个 BaiduUidGenerator 实例,并连续调用 nextId 方法来生成 ID。

需要注意的是,这只是一个简化的实现,实际应用中可能需要根据具体需求进行调整和优化。

结语

以上是几种常见的分布式ID生成方案,每种方案都有其适用的场景,开发人员可以根据实际需求选择合适的方案。

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

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

相关文章

企业文件防泄密软件哪种软件比较好?——推荐@德人合科技 | 天锐绿盾终端数据安全防泄密管理系统!

德人合科技提供的天锐绿盾终端数据安全防泄密管理系统是一款比较好的企业文件防泄密软件。 ——首页———— www.drhchina.com—— 以下是一些关于该软件的优点&#xff1a; 强大的防泄密功能&#xff1a;该软件采用动态加解密技术&#xff0c;能够在文件创建、编辑存盘时自动…

代码随想录算法训练营day24

题目&#xff1a;77. 组合 参考链接&#xff1a;代码随想录 回溯法理论基础 回溯三部曲&#xff1a;回溯函数模板返回值以及参数、回溯函数终止条件、回溯搜索的遍历过程。 模板框架&#xff1a; void backtracking(参数) {if (终止条件) {存放结果;return;}for (选择&…

【Android】反编译APK及重新打包

1.下载 APK 反编译工具 首先&#xff0c;需要下载一个 APK 反编译工具&#xff0c;例如 Apktool。可以在官网&#xff08;https://apktool.org/docs/install&#xff09;上下载最新版本的 Apktool&#xff0c;也可以使用包管理器来安装。 2.反编译 APK 文件 将要修改包名的 …

Ubuntu中matplotlib显示中文的方法

其实有很多朋友已经总结得很好了&#xff1a;Ubuntu下让matplotlib显示中文字体_ubuntu matplot 使用汉字-CSDN博客 这里我就是简单补充一下&#xff1a; 按照上面这篇博客&#xff0c;下载&#xff1a;GitHub - tracyone/program_font: fonts for programmer 然后运行&#…

CSS选中第2个table

css如何选择body下的第2个table_已解决_博问_博客园 th, td { text-align:center; } table:nth-of-type(2) td:first-child { text-align:left; }

Redis7

摘录 https://github.com/Romantic-Lei/Learning-in-practice/blob/master/Redis/ 官网地址: 英文&#xff1a;Redis 中文&#xff1a;CRUG网站 redis中文文档 安装包&#xff1a;https://redis.io/download/&#xff0c;选择redis7.0版本即可 Redis在线测试地址(不用下载也…

Qt中常见的JS类和函数(二): 全局对象

Qt专栏&#xff1a; http://t.csdnimg.cn/YLlEd 相关系列文章 Qt中字符串转换为JS的函数执行 Qt中常见的JS类和函数(一): 全局对象 Qt中常见的JS类和函数(二): 全局对象 目录 2.3.构造函数属性 2.3.1.Object 2.3.2.Function 2.3.3.Array 2.3.4.ArrayBuffer 2.3.5.String …

等保测评与商用密码共铸工控安全“双评合规”新篇章

最近听说了一个段子&#xff1a;“网络安全就像美女的内衣&#xff0c;等保和密评就是最贴身的内衣两件套&#xff0c;上下身一件都不能少。否则你的魔鬼身材&#xff08;核心数据&#xff09;就有可能被色狼&#xff08;黑客&#xff09;一览无余&#xff08;数据泄漏&#xf…

linux系统消息中间件安装配置负载均衡器

负载均衡器 安装并配置负载均衡器安装并配置负载均衡器HA在其中一台服务器安装HAProxy修改 /etc/haproxy/haproxy.cfg 常见命令插件管理virtual_host管理用户管理其他使用命令&#xff1a;RabbitMQ 错误处理 安装并配置负载均衡器 注意&#xff1a;如果使用阿里云&#xff0c;可…

Ubuntu上Jenkins自动化部署Gitee上SpringBoot项目

文章目录 安装安装JDK安装Maven安装GitNodeJS安装&#xff08;可选&#xff09;安装Jenkins 配置Jenkins为Jenkins更换插件源设置jenkins时区安装插件全局工具配置添加Gitee凭证Gitee项目配置 部署后端1.新建任务2.配置源码管理3.构建触发器4.到Gitee中添加WebHook5.构建环境6.…

详解POCV/SOCV的时序报告

​POCV/SOCV的时序报告中有如下变量&#xff1a; Mean: 高斯分布中的μ值&#xff08;平均值&#xff09; Sensit: sensitivity&#xff0c;也就是1个Sigma的值&#xff1b; Corner: Sigma边界的最差值 cell的delay Delay mean N * Delay sigma; cell 的Transition Sl…

力扣 300. 最长递增子序列

题目来源&#xff1a;https://leetcode.cn/problems/longest-increasing-subsequence/description/ C题解1&#xff1a;动态规划 用两个循环&#xff0c;每到一个元素&#xff0c;就找它之前的最长递增子序列。 dp[i]表示第i个元素的最长递增子序列&#xff0c;里层遍历寻找之…

【python基础学习2】python可迭代对象iterator的特点,以及相关函数:zip(), map(), join() 和strip()方法等

目录 1 python里的可迭代对象 1.1 什么是可迭代对象 1.2 python里的可迭代对象 1.3 可迭代对象如何遍历 1.3.1 可迭代方法 1.3.2 迭代器的测试 1.4 python里的可迭代对象都不是向量&#xff0c;加法等是合并规则 1.5 可迭代对象不支持减法操作 1.6 可迭代器可以直接用…

STM32自学☞输入捕获测频率和占空比案例

本文是通过PA0口输出PWM波&#xff0c;然后通过PA6口捕获PWM波的频率和占空比&#xff0c;最终在oled屏上显示我们自己设置的频率和占空比。由于和前面的pwm呼吸灯代码有重合部分所以本文中的代码由前者修改而来&#xff0c;对于文件命名不要在意。 pwm_led.c文件 /* 编写步…

RubyMine 2023:让Ruby编程变得更简单 mac/win版

JetBrains RubyMine 2023是一款专为Ruby开发者打造的强大集成开发环境&#xff08;IDE&#xff09;。这款工具集成了许多先进的功能&#xff0c;旨在提高Ruby编程的效率和生产力。 RubyMine 2023软件获取 RubyMine 2023的智能代码编辑器提供了丰富的代码补全和提示功能&#…

使用vuetify实现全局v-alert消息通知

前排提示&#xff0c;本文为引流文&#xff0c;文章内容不全&#xff0c;更多信息前往&#xff1a;oldmoon.top 查看 简介 使用强大的Vuetify开发前端页面&#xff0c;结果发现官方没有提供简便的全局消息通知组件&#xff08;像Element中的ElMessage那样&#xff09;&#xf…

机器学习之特征缩放

特征缩放&#xff08;Feature Scaling&#xff09;是机器学习数据预处理中的一种方法&#xff0c;旨在将不同量级的数据变换到相同的尺度。这一步骤对于很多机器学习算法来说非常重要&#xff0c;因为算法的性能可能会因为特征的量级不同而受到影响。特征缩放可以提高算法的收敛…

【web】nginx+php环境搭建-关键点(简版)

一、nginx和php常用命令 命令功能Nginxphp-fpm启动systemctl start nginxsystemctl start php-fpm停止systemctl stop nginxsystemctl stop php-fpm重启systemctl restart nginxsystemctl restart php-fpm查看启动状态systemctl status nginxsystemctl status php-fpm开机自启…

Go命令源码文件

Go命令源码文件 命令源码文件的用途&#xff0c;怎样编写它&#xff1f; 命令源码文件是程序的运行入口&#xff0c;是每个可独立运行的程序必须拥有的。如果一个源码文件声明属于 main 包&#xff0c;并且包含一个无参数声明且无结果声明的 main 函数&#xff0c;那么它就是…

[HackmyVM]靶场 Azer

kali:192.168.56.101 主机发现 arp-scan -l靶机:192.168.56.103 端口扫描 nmap -p- 192.168.56.103 开启了80 3000端口 看一下80端口 一直在那转&#xff0c;看源码也没什么有用的东西 扫一下目录 扫不到什么东西 看看另一个端口 是个登录界面 输入admin/admin测试 错误…