分布式唯一id的7种方案
背景
为什么需要使用分布式唯一id?
如果我们的系统是单体的,数据库是单库,那无所谓,怎么搞都行。
但是如果系统是多系统,如果id是和业务相关,由各个系统生成的情况下,那每个主机生成的主键id就是不可控的,多个主机就有可能会造成主键冲突的问题。
方案
1、数据库自增
1024表,不是依赖每一张表的自增主键,不同的表都从1开始累加id
专门搞一个库,搞一个表,专门用于生成全局唯一id,insert into插入一条数据,他会返回给你一个全局唯一id,然后你把这个id设置给数据,插入分表后的1024张表里去,全局唯一的
优点:超简单,落实起来非常方便,公司有一个统一的库和表,专门用于生成id;或者你自己的系统的库里你专门弄一张表,用来生成id
缺点:单库单表,并发抗不住,一旦达到每秒几千的高并发;不停的在表里插入数据获取id,表数据会越来越多,还得定期清理,很麻烦
适用场景:分库分表是因为数据量大,但是低并发低负载,而且数据库单机有高可用问题,必须上高可用方案,另外是单表数据一直增长也是个问题,一般不会直接投入生产,投入生产环境的时候会用下面说的flickr的数据库唯一id生成方案
2、UUID
优点:本地生成,没有所谓的并发压力
缺点:太长了!作为主键绝对是不靠谱的!数据库频繁页分裂问题!
适用场景:除数据库主键之外的其他唯一键场景,都适合,这个方案一般不考虑在分布式唯一ID生成里,在我们的主题里,其实可以忽略
3、Twitter开源的Snowflake方案
核心思想:64个bit位,41位放时间(最多使用69年),10位放机器标识(最多把snowflake程序部署在1024台机器上),12位放序号(每毫秒,每台机器,可以顺序生成4096个ID),最高位1个bit是0
snowflake程序分布式部署在多台机器上,每台机器生成的每个ID,都是这一毫秒、机器id、序号,每台机器每毫秒最多4096个ID,绝对够用了,分布式方案可以抗高并发,大不了加机器,最多1024台机器,纯基于内存生成,性能很高
优点:高性能,高并发,分布式,可伸缩,最多扩展1024台机器,ID绝对够用
缺点:光是开源算法还不用,还得考虑时钟回拨等一系列问题,如果要解决那堆问题,需要开发很多机制,开发完了还得独立部署,有独立部署和维护的成本
适用场景:中大型公司,有高并发生成唯一ID场景,基于snowflake算法自研,加入时钟回拨解决方案,多机房方案,等等,各种生产方案,有人力去维护,有少数大厂采用了这个方案,可以作为生产级方案,但是需要解决很多问题
4、Redis自增机制
核心思想:Redis单线程,绝对有序自增,incrby;集群部署,比如5台机器,那么每台机器的初始值依次为1、2、3、4、5,每台机器的自增步长是5,第1台机器就是1、6、11、16、21,第2台机器就是2、7、12、17、22,以此类推,直到第5台机器就是5、10、15、20、25
优点:不用额外开发,一般公司都提供redis集群,直接用就行
缺点:客户端需要自己封装,基于Jedis去封装,客户端里需要写死Redis机器数量,每次获取1个ID,都是找到一台机器,然后按步长去incrby,接着返回给系统;而且扩容麻烦,如果5台机器抗不住并发了怎么办?扩容的时候加机器,客户端需要修改代码,或者基于动态感知,这其实也有开发成本,另外扩容的时候,步长就会改变,那之前的ID怎么办?都得重新洗掉,全部从头开始计算,极为麻烦
适用场景:鉴于他的缺点,一般不用redis集群玩自增主键生成;分库分表了,然后每秒在万左右的高并发,但是可预见的不会达到几万以及十万级的并发,那么此时可以用Redis单机去生成自增主键,避免redis集群扩容的步长改变问题;但是还得部署Redis主从同步+哨兵高可用,可是主从同步是异步的,有id重复问题,所以最终生产一般不用
5、基于时间+业务id的组合
核心思想:比如打车软件,可以用时间戳+起点编号+车牌号作为一个id,业务组合上是不会有重复的;比如电商订单,可以用时间戳+用户id,一个用户在1毫秒内一般最多就下一个订单,一般不会重复,除非用户基于程序刷单,否则手点的情况下,这个组合id一般没问题,还可以加个下单渠道、第一个商品id等其他业务id组合起来
优点:实现简单,没额外成本,没并发之类的扩容问题
缺点:有的业务场景(比如订单之类的),还可以用这种方案,但是有的业务场景可能根本没法通过业务来组合,而且始终担心有重复问题
适用场景:很多大厂都用这个方案,做订单编号这些,但是分库分表不光是订单,还有什么用户、账号以及各种其他的业务场景,所以部分适用于生产
6、flickr(雅虎旗下的图片分享平台)公司的方案
CREATE TABLE uid_sequence (
id bigint(20) unsigned NOT NULL auto_increment,
stub char(1) NOT NULL default ‘’,
PRIMARY KEY (id),
UNIQUE KEY stub (stub)
) ENGINE=MyISAM;
REPLACE INTO uid_sequence (stub) VALUES (‘test’);
SELECT LAST_INSERT_ID();
replace into语法替代insert into,避免表行数过大,一张表就一行数据,然后再select获取这个表的最新id,last_insert_id()函数是connection级别的,就你这个连接的最近insert生成的id,多个客户端之间没影响
当然,其实也可以优化成这样,就是每次你一台机器要申请一个唯一id,你就REPLACE INTO uid_sequence (stub) VALUES (‘192.168.31.226’),用你自己机器的ip地址去replace into,那么就你自己机器会有id不停自增,完了用select id from table where stub=机器地址,就可以了
最多如果你要考虑到多线程并发问题,那么就在机器地址后加入线程编号,这样一台机器的不同线程,都是对自己的id在自增
这个方案本质跟第一个方案没区别,唯一优化就是用replace into替代了insert into,避免表数据量过大,缺点也在于数据库并发能力不高,所以适用场景,就是分库分表的时候,低并发,用这个方案生成唯一id,低并发场景下可以用于生产
而且一般会部署数据库高可用方案,两个库设置不同的起始位置和步长,分别是1、3、5,以及2、4、6
7、基于flickr方案的高并发优化
有一种变种方案,是基于flickr方案的高并发优化,他核心问题在于每一次生成id都得找数据库,所以这就是并发瓶颈,所以这里可以把数据库优化为号段,而不是id号,什么意思呢?一起来看看
每台机器都引入一个自己封装的客户端,只要一旦服务启动,就直接采用flickr方案获取一个id,但是他仅仅代表的是一个号段,什么意思呢?比如说,一个服务启动,通过flickr方案的replace into拿到一个id,假设是1吧
此时你的号段可以配置为一个号段是10000个id号,那么此时你这个号段的起始id就是1 * 10000,然后可以把起始id设置到AtomicLong里去,还可以保存一下号段的最大id,也就是(n + 1)* 10000,就是2 * 10000,20000
所以这个号段的id就是[10000, 20000,20000是不包含在内的
接着服务里如果要获取唯一id,直接找你封装的客户端,每次拿一个id,就是AtomicLong.incrementAndGet(),直接原子递增,这样你大部分的id获取,都是在内存里通过号段内递增实现的
高并发问题,解决了!!数据库仅仅用于维护号段罢了
如果拿到了号段里最大id,此时对获取id的请求得阻塞住,只要拿到的id大于等于了最大id,请求全部自己陷入阻塞,比如大家都去while循环阻塞,过一会儿再次获取id,跟最大id比较
发号器客户端的线程,定时轮询,一旦发现这个问题,此时就重新利用flickr方案获取一个号段,再次设置AtomicLong里的初始id以及更新最大id,在这个过程中别的任何一个线程来获取id都会发现AtomicLong自增值比最大id是大的
即使是发号器客户端线程,刚刚设置了AtomicLong的值,然后还没设置volatile的最大id值,此时别的线程在while循环过程中获取了id,AotmicLong自增值一定大于之前的最大id值,也会继续陷入阻塞的
只有当发号器客户端线程更新了volatile最大id值之后,其他线程才会在while循环之后,发现AtomicLong自增值是小于最大id值的,此时就可以继续工作了,这种情况通常是很少的,所以大部分情况下,各个服务都是基于本地的号段在内存里获取id,而且全局上还是唯一的,没有高并发问题,数据库的并发也是很低的
这个方案的唯一缺点就是,每次重启服务,就会浪费一个号段里还没自增到的大量id,重启后又是新的号段了,但是如果要优化,可以在spring销毁事件里,发号器内部设置一个volatile标识,不允许获取id了,接着把AtomicLong的值持久化到本地磁盘,下次服务重启后直接从本地磁盘里读取,就不会浪费了
其实这个优化以后的方案,就可以投入生产了,确实也有个别大厂是这么做的,也运行的很好。如果一定要说这个方案有什么弊端,那就是,归根结底,还是有一个数据库这么个外部依赖,其实如果方案真做好了,你还得考虑数据库的高可用方案这些东西,就是牵扯到了外部依赖,就容易做的很重
另外一个问题,就是对于这个方案,你还得去做步长的配置,那么到底允许多长的步长呢?是否允许用户自己配置呢?如果不允许,你固定一个步长,那个步长会不会在一些特殊高并发场景下,比如你1000作为步长,1000个号瞬间被秒光,一个服务每秒都得请求一次数据库获取新的号段,此时你有上千个服务实例,数据库不还是抗不住?
所以,这个方案适合一些没有特殊超高并发的场景,而且扩展性和灵活性不是很强,总是让人担心他的号段步长会出一些问题,但是在一些普通场景下,其实一般可能也没什么问题,所以有普通高并发场景的生产环境,还是可用的
基于数据库的方案就是flickr方案以及flickr高并发优化方案,但是没有snowflake生产级方案那么具备普适性,snowflake方案不涉及什么号段问题,也不会额外依赖数据库,不需要考虑数据库高可用之类的,他自己就是peer-to-peer的一个集群架构,随时可以扩容
时间戳+业务id,相当好用,推荐第一选择是他,能用时间戳+业务id的,就别搞分布式id生成,如果不行的,再考虑flickr方案或者snowflake方案
相关文章:
分布式唯一id的7种方案
背景 为什么需要使用分布式唯一id? 如果我们的系统是单体的,数据库是单库,那无所谓,怎么搞都行。 但是如果系统是多系统,如果id是和业务相关,由各个系统生成的情况下,那每个主机生成的主键id就…...
嵌入式物联网在医疗行业中的应用——案例分析
作者主页: 知孤云出岫 目录 嵌入式物联网在医疗行业中的应用——案例分析引言1. 智能病房监控1.1 实时患者监控系统 2. 智能医疗设备管理2.1 设备使用跟踪与维护 3. 智能药物管理3.1 药物分配与跟踪 4. 智能远程医疗4.1 远程患者监控与诊断 总结 嵌入式物联网在医疗行业中的应…...
C语言 底层逻辑详细阐述指针(一)万字讲解 #指针是什么? #指针和指针类型 #指针的解引用 #野指针 #指针的运算 #指针和数组 #二级指针 #指针数组
文章目录 前言 序1:什么是内存? 序2:地址是怎么产生的? 一、指针是什么 1、指针变量的创建及其意义: 2、指针变量的大小 二、指针的解引用 三、指针类型存在的意义 四、野指针 1、什么是野指针 2、野指针的成因 a、指…...
【人工智能大模型】文心一言介绍以及基本使用指令
目录 一、产品背景与技术基础 二、主要功能与特点 基本用法 指令的使用 注意事项 文心一言(ERNIE Bot)是百度基于其文心大模型技术推出的生成式AI产品。以下是对文心一言的详细介绍: 一、产品背景与技术基础 技术背景:百度…...
AI绘画入门实践|Midjourney 的模型版本
模型分类 Midjourney 的模型主要分为2大类: 默认模型:目前包括:V1, V2, V3, V4, V5.0, V5.1, V5.2, V6 NIJI模型:目前包括:NIJI V4, NIJI V5, NIJI V6 模型切换 你在服务器输入框中输入 /settings: 回车后…...
Web3时代的教育技术革新:智能合约在学习管理中的应用
随着区块链技术的发展和普及,Web3时代正在为教育技术带来前所未有的革新和机遇。智能合约作为区块链技术的核心应用之一,不仅在金融和供应链管理等领域展示了其巨大的潜力,也在教育领域中逐渐探索和应用。本文将探讨智能合约在学习管理中的具…...
云计算实训室的核心功能有哪些?
在当今数字化转型浪潮中,云计算技术作为推动行业变革的关键力量,其重要性不言而喻。唯众,作为教育实训解决方案的领先者,深刻洞察到市场对云计算技能人才的迫切需求,精心打造了云计算实训室。这一实训平台不仅集成了先…...
芯科科技第五届物联网开发者大会走进世界各地,巡回开启注册
中国,北京 – 2024年7月18日 – 致力于以安全、智能无线连接技术,建立更互联世界的全球领导厂商Silicon Labs(亦称“芯科科技”,NASDAQ:SLAB)今日宣布,其2024年Works With开发者大会现正开放注册…...
Python创建Excel表和读取Excel表的基础操作
下载openpyxl第三方库 winr打开命令行输入cmd 这个如果不行可以试试其他方法,在运行Python代码的软件里也有直接下载的地方,可以上网搜索 创建Excel表 示例代码:最后要记得保存,可以加一句提示语句。 import openpyxl lst[100,…...
JVM(day2)经典垃圾收集器
经典垃圾收集器 Serial收集 使用一个处理器或一条收集线程去完成垃圾收集工作,更重要的是强调在它进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束。 ParNew收集器 ParNew 收集器除了支持多线程并行收集之外,其他与 …...
华为od机试真题 — 分披萨(Python)
题目描述 “吃货”和“馋嘴”两人到披萨店点了一份铁盘(圆形)披萨,并嘱咐店员将披萨按放射状切成大小相同的偶数个小块。 但是粗心服务员将披萨切成了每块大小都完全不同奇数块,且肉眼能分辨出大小。 由于两人都想吃到最多的披萨,他们商量…...
ubuntu22.04 安装boost
下载boost压缩包,我这里上传了一份1_81_0版本tar -xzvf boost_1_81_0.tar.gzcd boost_1_81_0/sudo apt install build-essential g autotools-dev libicu-dev libbz2-dev -ysudo ./bootstrap.sh --prefix/usr/./b2sudo ./b2 install 上述7步完成后,相关…...
基于JAVA+SpringBoot+uniapp的心理小程序(小程序版本)
✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、SpringCloud、Layui、Echarts图表、Nodejs、爬…...
C语言 ——— 输入两个正整数,求出最小公倍数
目录 何为最小公倍数 题目要求 代码实现 方法一:暴力求解法(不推荐) 方法二:递乘试摸法(推荐) 何为最小公倍数 最小公倍数是指两个或者多个正整数(除了0以外)的最小的公共倍数…...
Langchain 对pdf,word,txt等不同文件的加载解析
项目中遇到各种数据资源想要加载近langchain构建本地知识ai系统,怎么加载对应的文件格式呢,一起研究下 引入Langchain from langchain.document_loaders import UnstructuredWordDocumentLoader,PyPDFium2Loader,DirectoryLoader,PyPDFLoader,TextLoad…...
BL201分布式I/O耦合器连接Profinet网络
钡铼技术的BL201分布式I/O耦合器是一个用于Profinet网络的设备,用于连接远程输入/输出(I/O)设备到控制系统,如可编程逻辑控制器(PLC),能够实现分布式的I/O连接和通信。 它支持标准Profinet IO …...
Pycharm 报错 Environment location directory is not empty 解
删除项目中ven文件夹(已存在的),然后再添加新的ven虚拟环境就可以了...
【Android】Intent基础用法及作用
文章目录 使用Intent在活动中穿梭组成显式Intent隐式Intent显式与隐式区别作用 活动间传递数据向下一个活动传递数据返回数据给上一个活动 使用Intent在活动中穿梭 Intent(意图)是一种重要的消息传递对象,用于在不同组件(如活动&…...
Web开发:ASP.NET CORE的后端小结(基础)
1.后端重定向到指定路由 public IActionResult Index(){return RedirectToAction("Index", "Main");//重定向>Main/Index} 【备注】如果在MainController的Index方法中return View();本质是 return View("Index"),返回和方法同名的…...
侧开知识点合集2
一、try .... catch.. AccessViolationException异常触发后,下列程序的输出结果为 static void Main(string[] args) { try { throw new AccessViolationException(); Console.WriteLine("error1"); } catch (Exception e) { Console.WriteLi…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...
【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL
ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...
快速排序算法改进:随机快排-荷兰国旗划分详解
随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...
用 FFmpeg 实现 RTMP 推流直播
RTMP(Real-Time Messaging Protocol) 是直播行业中常用的传输协议。 一般来说,直播服务商会给你: ✅ 一个 RTMP 推流地址(你推视频上去) ✅ 一个 HLS 或 FLV 拉流地址(观众观看用)…...
