【分布式系统】唯一性ID的实现
1、UUID(通用唯一标识符)
1、UUID本身
一种用于标识信息的标准化方法。一个128位的数字,常表示为32个十六进制数字,以连字符分隔成五组:8-4-4-4-12。
版本: UUID有不同的版本,最常见的是基于时间戳和随机数生成的版本1和版本4。
唯一性: 由于UUID的长度和生成机制,可以保证在大多数情况下生成的UUID是唯一的。
应用: 在各种系统中广泛应用,用于唯一标识实体、会话、交易等。
2、设计方法:
时间戳: 将当前时间戳作为UUID的一部分,以确保在同一时刻生成的UUID是唯一的。
节点标识: 在分布式系统中,将每个节点的唯一标识符(如机器ID)纳入UUID的生成过程,以防止在不同节点上生成相同的UUID。
随机数生成器: 将随机数作为UUID的一部分,以增加唯一性,但在分布式系统中要确保随机数生成器是足够随机的。
考虑时钟回拨问题: 如果使用时间戳作为UUID的一部分,需要考虑时钟回拨可能导致的重复UUID问题。可以采用一些机制来解决时钟回拨带来的潜在问题,比如使用递增序列号。
一致性哈希算法: 基于节点信息和数据内容计算哈希值,然后将哈希值转换为UUID。这样可以确保相同的数据在不同节点上生成的UUID是一致的。
时钟回拨问题:
指在分布式系统中,当某个节点的系统时间发生回拨(即向过去跳跃)时可能导致的一系列问题。
引起的原因:手动调整时间;网络时间协议(NTP)校准;系统重启或故障;虚拟机迁移;
解决方向:使用逻辑时钟;增加容错机制;使用稳定的时钟;时间校正算法;设计时避免依赖绝对时间;
package mainimport ("fmt""github.com/google/uuid"
)func main() {// 生成一个新的 UUIDnewUUID := uuid.New()fmt.Printf("Generated UUID: %s\n", newUUID)
}
2、数据库序列(自增ID)
简单,工作方式:基于中央数据库的序列生成器,如自增ID,每次请求时递增序列值。顺序性:保证了生成ID的顺序性和唯一性。
package mainimport ("database/sql""fmt"_ "github.com/mattn/go-sqlite3"
)func main() {// 连接到 SQLite 数据库db, err := sql.Open("sqlite3", "test.db")if err != nil {fmt.Println(err)return}defer db.Close()// 创建一个包含自增ID的表_, err = db.Exec("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)")if err != nil {fmt.Println(err)return}fmt.Println("Table created successfully")
}
//连接到 SQLite 数据库,并创建了一个名为 users 的表,该表包含一个自增的整数类型的ID列和一个文本类型的 name 列。
//不同的数据库(如 MySQL、PostgreSQL 等)可能有不同的语法和方式来实现自动递增的ID列
3、雪花算法(Twitter Snowflake)
Twitter开发的一种生成64位ID的服务,基于时间戳、机器ID和序列号。
时间戳(41位): 用于表示ID生成的时间戳,通常精确到毫秒级。
机器ID(10位): 标识生成ID的机器的唯一标识,通常使用数据中心ID与机器ID的组合。
序列号(12位): 在同一毫秒内,通过累加的方式生成序列号,确保在同一节点上生成的ID是唯一的。
雪花算法的优点包括高性能、高可用性和ID趋势递增。在实际应用中,可根据需求调整时间戳的位数、机器ID的位数和序列号的位数来适应不同的场景。
一般用于替代传统自增ID的方式。
package mainimport ("fmt""sync""time"
)const (epoch time.Duration = 1609459200000 // 2021-01-01 的时间戳,用于生成时间戳部分workerIDBits = 5 // 机器 ID 的位数sequenceBits = 12 // 序列号的位数maxWorkerID = -1 ^ (-1 << workerIDBits)maxSequence = -1 ^ (-1 << sequenceBits)
)type Snowflake struct {mu sync.MutexlastTime int64workerID uintsequence uint
}func NewSnowflake(workerID uint) *Snowflake {if workerID > maxWorkerID {panic("worker ID 超出范围")}return &Snowflake{lastTime: 0,workerID: workerID,sequence: 0,}
}func (s *Snowflake) GenerateID() uint64 {s.mu.Lock()defer s.mu.Unlock()currentTime := time.Now().UnixNano() / 1e6 // 获取当前时间的毫秒数if s.lastTime == currentTime {s.sequence = (s.sequence + 1) & maxSequenceif s.sequence == 0 {for currentTime <= s.lastTime {currentTime = time.Now().UnixNano() / 1e6}}} else {s.sequence = 0}s.lastTime = currentTimeid := uint64((currentTime-epoch)<<22 | int64(s.workerID)<<17 | int64(s.sequence))return id
}func main() {sf := NewSnowflake(1) // 设定一个机器 IDid := sf.GenerateID()fmt.Println(id)
}
4、使用Redis实现分布式ID生成
Redis是一个高性能的键值数据库,它可以用于生成分布式唯一标识符。
不同Redis实例通过配置不同的起始步长来区分。
这个的实现原理利用了Redis的原子操作。
package mainimport ("fmt""github.com/go-redis/redis/v8""log""time"
)var redisClient *redis.Clientfunc init() {redisClient = redis.NewClient(&redis.Options{Addr: "localhost:6379", // Redis 服务器地址Password: "", // Redis 密码,如果有的话DB: 0, // 选择使用的数据库})
}func generateID(key string) (int64, error) {val, err := redisClient.Incr(key).Result()if err != nil {return 0, err}return val, nil
}func main() {key := "distributed_id_generator" // Redis 中的键名// 生成 5 个分布式 IDfor i := 0; i < 5; i++ {id, err := generateID(key)if err != nil {log.Fatalf("Failed to generate ID: %v", err)}fmt.Printf("Generated ID: %d\n", id)time.Sleep(time.Millisecond) // 可选的延迟,以避免生成相同的 ID}
}
package mainimport ("fmt""github.com/go-redis/redis/v8""log""time"
)var redisClients map[string]*redis.Clientfunc init() {redisClients = make(map[string]*redis.Client)redisClients["instance1"] = redis.NewClient(&redis.Options{Addr: "localhost:6379", // Redis 实例1的地址Password: "", // Redis 密码,如果有的话DB: 0, // 选择使用的数据库})redisClients["instance2"] = redis.NewClient(&redis.Options{Addr: "localhost:6380", // Redis 实例2的地址Password: "", // Redis 密码,如果有的话DB: 0, // 选择使用的数据库})
}func generateID(key string, start int64) (int64, error) {val, err := redisClients[key].IncrBy(key, start).Result()if err != nil {return 0, err}return val, nil
}func main() {key1 := "distributed_id_generator_instance1" // Redis 实例1中的键名key2 := "distributed_id_generator_instance2" // Redis 实例2中的键名// 生成 5 个分布式 IDfor i := 0; i < 5; i++ {id1, err := generateID(key1, 1000) // 指定实例1的起始步长为1000if err != nil {log.Fatalf("Failed to generate ID: %v", err)}fmt.Printf("Instance 1 - Generated ID: %d\n", id1)id2, err := generateID(key2, 2000) // 指定实例2的起始步长为2000if err != nil {log.Fatalf("Failed to generate ID: %v", err)}fmt.Printf("Instance 2 - Generated ID: %d\n", id2)time.Sleep(time.Millisecond) // 可选的延迟,以避免生成相同的 ID}
}
5、使用数据库分段(Database Segment)
6、分布式键生成服务(如Zookeeper、etcd)
分布式协调服务在集群中生成唯一ID。
也是利用这些服务提供的分布式锁和原子性操作来生成唯一的ID。还有集群协调机制。
相关文章:
【分布式系统】唯一性ID的实现
1、UUID(通用唯一标识符) 1、UUID本身 一种用于标识信息的标准化方法。一个128位的数字,常表示为32个十六进制数字,以连字符分隔成五组:8-4-4-4-12。 版本: UUID有不同的版本,最常见的是基于时…...
哪里能找到好用的动物视频素材 优质网站推荐
想让你的短视频增添些活泼生动的动物元素?无论是搞笑的宠物瞬间,还是野外猛兽的雄姿,这些素材都能让视频更具吸引力。今天就为大家推荐几个超实用的动物视频素材网站,不论你是短视频新手还是老手,都能在这些网站找到心…...
SRAM芯片数据采集解决方案
SRAM芯片数据采集解决方案致力于提供一种高效、稳定且易于操作的方法,以确保从静态随机存取存储器SRAM芯片中准确无误地获取数据。 这种解决方案通常包括硬件接口和软件工具,它们协同工作,以实现对SRAM芯片的无缝访问和数据传输。 在硬件方…...
【贪心算法第七弹——674.最长连续递增序列(easy)】
目录 1.题目解析 题目来源 测试用例 2.算法原理 3.实战代码 代码分析 1.题目解析 题目来源 674.最长递增子序列——力扣 测试用例 2.算法原理 贪心思路 3.实战代码 class Solution { public:int findLengthOfLCIS(vector<int>& nums) {int n nums.size();in…...
[AI] 知之AI推出3D智能宠物:助力语言学习与口语提升的新选择
Hello! 知之AI官网 [AI] 知之AI推出3D智能宠物:助力语言学习与口语提升的新选择 随着人工智能技术的飞速发展,虚拟助手和智能设备不断进入我们的生活。近日,知之AI重磅推出了一款创新产品——3D智能宠物。这一产品不仅具备多国语言交流能力&…...
Android 14之HIDL转AIDL通信
Android 14之HIDL转AIDL通信 1、interface接口1.1 接口变更1.2 生成hidl2aidl工具1.3 执行hidl2aidl指令1.4 修改aidl的Android.bp文件1.5 创建路径1.6 拷贝生成的aidl到1和current1.7 更新与冻结版本1.8 编译模块接口 2、服务端代码适配hal代码修改2.1 修改Android.bp的hidl依…...
【R库包安装】R库包安装总结:conda、CRAN等
【R库包安装】R studio 安装rgdal库/BPST库 R studio 安装rgdal库解决方法 R studio 安装BPST库(github)解决方法方法1:使用devtools安装方法2:下载安装包直接在Rstudio中安装 参考 基础 R 库包的安装可参见另一博客-【R库包安装】…...
学习PMC要不要去培训班?
在当今快速变化的商业环境中,PMC作为供应链管理的核心环节之一,其重要性日益凸显。PMC不仅关乎产品的物料计划、采购、库存控制及物流协调,还直接影响到企业的生产效率、成本控制以及市场竞争力。面对这一专业领域的学习需求,许多…...
前端 用js封装部分数据结构
文章目录 Stack队列链表Setset 用来数组去重set用来取两个数组的并集set用来取两个数组的交集set用来取两个数组的差集 字典 Stack 栈,先进后出,后进先出。用数组来进行模拟,通过push存入,通过pop取出。 class Stack {// 带#表示…...
cocoscreator-doc-TS:目录
cocoscreator-doc-TS-脚本开发-访问节点和组件-CSDN博客 cocoscreator-doc-TS-常用节点和组件接口-CSDN博客 cocoscreator-doc-TS-脚本开发-创建和销毁节点-CSDN博客 cocoscreator-doc-TS-脚本开发-加载和切换场景-CSDN博客 cocoscreator-doc-TS-脚本开发-获取和设置资源-CS…...
理解Java集合的基本用法—Collection:List、Set 和 Queue,Map
本博文部分参考 博客 ,强烈推荐这篇博客,写得超级全面!!! 图片来源 Java 集合框架 主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合(单列…...
IOC容器实现分层解耦
文章开始之前,先引入软件开发的两个名词:耦合和内聚。耦合是指:衡量软件中各个层(三层架构)/各个模块的依赖关联程度;内聚是指:软件中各个功能模块内部的功能联系。三层架构中Controller、Servi…...
Flutter 共性元素动画
在 Flutter 中,共性元素动画(Shared Element Transitions)用于在页面导航或组件切换时创建视觉上更流畅和连贯的动画效果。这种动画可以使用户感受到两个界面之间的“物理联系”,比如图片从缩略图到全屏的扩大效果。 前置知识点整…...
K8s内存溢出问题剖析:排查与解决方案
文章目录 一、背景二、排查方案:1. 可能是数据量超出了限制的大小,检查数据目录大小2. 查看是否是内存溢出2.1 排查数据量(查看数据目录大小是否超过limit限制)2.2 查看pod详情发现问题 三、解决过程 一、背景 做redis压测过程中…...
乌班图单机(不访问外网)部署docker和服务的方法
面向对象:Ubuntu不能访问外网的机子,部署mysql、redis、jdk8、minio 过程: 1、安装docker(照着图去这里找对应的下载下来https://download.docker.com/linux/static/stable/),将7个docker官网下载的文件下载下来后,传上去服务器随便一个文件夹或者常用的opt或者/usr/lo…...
使用 pycharm 新建使用 conda 虚拟 python 环境的工程
1. conda 常见命令复习: conda env list // 查看 conda 环境列表 conda activate xxxenv // 进入指定 conda 环境2. 环境展示: 2.1. 我的物理环境的 Python 版本为 3.10.9: 2.2. 我的 conda 虚拟环境 env_yolov9_python_3_8 中的 pyth…...
Docker的save和export命令的区别,load和import的区别 笔记241124
Docker的save和export命令的区别,load和import的区别 解说1: Docker的save和export命令,以及load和import命令,在功能和使用场景上存在显著的区别。以下是对这两组命令的详细对比和解释: Docker save和export命令的区别 使用方式和目的&am…...
通俗理解人工智能、机器学习和深度学习的关系
最近几年人工智能成为极其热门的概念和话题,可以说彻底出圈了。但人工智能的概念在1955年就提出来了,可以说非常古老。我在上小学的时候《科学》课本上就有人工智能的概念介绍,至今还有印象,但那些年AI正处于“寒冬”,…...
使用 pycharm 新建不使用 python 虚拟环境( venv、conda )的工程
有时候我们发现一个好玩的 demo,想赶快在电脑上 pip install 一下跑起来,发现因为 python 的 venv、conda 环境还挺费劲的,因为随着时间的发展,之前记得很清楚的 venv、conda 的用法,不经常使用,半天跑不起…...
【大数据学习 | Spark-SQL】SparkSQL读写数据
我们使用sparksql进行编程,编程的过程我们需要创建dataframe对象,这个对象的创建方式我们是先创建RDD然后再转换rdd变成为DataFrame对象。 但是sparksql给大家提供了多种便捷读取数据的方式。 //原始读取数据方式 sc.textFile().toRDD sqlSc.createDat…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...
如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
Ubuntu系统复制(U盘-电脑硬盘)
所需环境 电脑自带硬盘:1块 (1T) U盘1:Ubuntu系统引导盘(用于“U盘2”复制到“电脑自带硬盘”) U盘2:Ubuntu系统盘(1T,用于被复制) !!!建议“电脑…...
LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用
中达瑞和自2005年成立以来,一直在光谱成像领域深度钻研和发展,始终致力于研发高性能、高可靠性的光谱成像相机,为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么?它的作用是什么? Spring框架的核心容器是IoC(控制反转)容器。它的主要作用是管理对…...
Ubuntu系统多网卡多相机IP设置方法
目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...
yaml读取写入常见错误 (‘cannot represent an object‘, 117)
错误一:yaml.representer.RepresenterError: (‘cannot represent an object’, 117) 出现这个问题一直没找到原因,后面把yaml.safe_dump直接替换成yaml.dump,确实能保存,但出现乱码: 放弃yaml.dump,又切…...
