分布式唯一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…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...

UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...

【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

Python环境安装与虚拟环境配置详解
本文档旨在为Python开发者提供一站式的环境安装与虚拟环境配置指南,适用于Windows、macOS和Linux系统。无论你是初学者还是有经验的开发者,都能在此找到适合自己的环境搭建方法和常见问题的解决方案。 快速开始 一分钟快速安装与虚拟环境配置 # macOS/…...
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里 脚本1 #!/bin/bash #定义变量 ip10.1.1 #循环去ping主机的IP for ((i1;i<10;i)) doping -c1 $ip.$i &>/dev/null[ $? -eq 0 ] &&am…...