分布式主键生成设计策略

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 Tair(兼容Redis),内存型 2GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
简介: 分布式主键生成设计策略

1 写作目的


这几天被虾皮裁员的消息刷屏了,实话实说互联网的行情确实很差,各处都在裁员。而且最近在忙试用期转正答辩,还不错,光荣的成为了一个大厂的正式员工,很庆幸在这么不稳定的情况下还能有自己的一寸方土。还扯别的干什么呢?开卷吧。


注意:本文只讲分布式主键的设计实现原理,不讲具体代码的实现细节。默认都不是单节点。


2 为什么要用分布式主键


在我们业务量不大的时候,单库单表完全可以支持现在的业务,数据再大一点读写分离也算OK。但是随着数据量的增长,单库单表终究是抗不住的。那就需要分库分表。分库分表后肯定不能依赖分表中的自增主键。因此需要一个 生成全局唯一ID的 主键生成策略。


3 分布式主键的基本要求


全局唯一:不管什么主键,都需要全局唯一。

高性能高可用:分布式主键服务本身就是一个底层的服务,很多服务都依赖于 这个服务,如果底层服务都不稳定,那么上游的服务就更谈不上稳定。

递增:大部分数据存储使用MySQL存储数据,为了快速存储和检索,分布式主键生成策略总趋势要求是递增的。


4 常见的分布式主键生成策略


4.1 UUID


在Java中自带的UUID就具有唯一性。如下面代码所示。生成的结果不是数字,是字符串。那


    public static void main(String[] args) {
        String uuid = UUID.randomUUID().toString().replaceAll("-","");
        System.out.println(uuid);//e48126eb0e6646638b043fca85f38818
    }


优点


  • 简单,没有网络消耗。


缺点


  • 不保证大趋势递增,不利于检索,对索引的构建和维护成本比较大。
  • 长度过长,不利于存储。
  • 没有具体的业务含义。


4.2 MySQL


4.2.1 自增主键


4.2.1.1单点MySQL(单Master)


基于数据库的自增主键充当分布式ID服务器。


具体实现:


1)创建一个SEQUENCE_ID表(id,value)

2)每次请求该分布式主键主键服务时向SEQUENCE_ID 插入一条数据,并返回自增主键作为分布式主键。


优点


  实现简单,自增有序。


缺点


  DB单点存在宕机风险,扛不住高并发场景。


4.2.1.2集群MySQL(多Master)


集群也是基于数据库的自增实现分布式ID服务器。

具体实现: 设置起始值和步长


MySQL_1 配置:


set @@auto_increment_offset = 1;     -- 起始值
set @@auto_increment_increment = 2;  -- 步长


MySQL_2 配置:


set @@auto_increment_offset = 2;     -- 起始值
set @@auto_increment_increment = 2;  -- 步长


设计方式如下图所示。那么整体还是有序的。不过每一个DB承担的流量就是1/2。


9.png


优点


  • 多Master相比单Master会好很多


缺点


  • 不利于后期扩容


4.2.2 区间号段(☆☆☆☆☆)


我们以单节点为例,在数据库中维护一个SEQUENCE_ID表。表中定义的是一个order主键生成规则和user主键生成规则。简单对表做一下解释,biz_code表示一种主键,max_id表示已经分配的最大ID,step表示步长,用于每次生成一批主键。


微信截图_20230225222510.png


每个节点的内存中存的是一批主键ID,当时候完毕后再去申请一批数据,效率极高。


10.png


优点


  并发压力不会再MySQL主键服务器侧。

  容灾性好,我一次给你一批,即使主键服务器挂了,Node内存中的主键可以继续使用,Nod不应该order_node对外提供服务。


缺点


   每当请求主键服务器申请一批主键时,TP999数据会偶尔出现尖刺。因为当Clinet大量的请求到Order_Node时因没有主键去申请主键时会导致这一批的请求时间会hang住。


11.png


双Buffer优化


针对 区间号段 中出现尖刺的情况,使用双Buffer进行优化。如下图所示,我首先申请一批主键,当使用到该批次的10%时,后台起一个线程申请下一批主键,这样一种预加载的情况可以时主键串联起来,有效解决上面的请求RT抖动情况。


12.png


4.3 Redis


原理就是利用redis的 incr命令实现ID的原子性自增。


127.0.0.1:6379> set seq_id 1     // 初始化自增ID为1
OK
127.0.0.1:6379> incr seq_id      // 增加1,并返回递增后的数值
(integer) 2


优点


  • 极大降低了主键服务器MySQL的流量


缺点


  • Redis如果使用RDB进行持久化,那么数据会存在丢失的风险。即 incr 后的数据丢失,则再次生成的主键会重复
  • 依赖第三方服务,系统的复杂性会增加


4.4 SnowFlake雪花算法


算法简介


雪花算法(Snowflake)是twitter公司内部分布式项目采用的ID生成算法,开源后广受国内大厂的好评,在该算法影响下各大公司相继开发出各具特色的分布式生成器,不过设计思路还是相同和想通的。


13.png


如上图(图片源自网络,如有侵权联系删除)所示。整个ID由4部分组成。


●第一个bit位(1bit):一般生成ID都为正数,所以默认为0。

●时间戳部分(41bit):毫秒级的时间,不建议存当前时间戳,而是用(当前时间戳 - 固定开始时间戳)的差值,可以使产生的ID从更小的值开始;41位的时间戳可以使用69年,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年

●工作机器id(10bit):也被叫做workId,这个可以灵活配置,机房或者机器号组合都可以。

●序列号部分(12bit),自增值支持同一毫秒内同一个节点可以生成4096个ID


核心思想


多种唯一的值 进行拼接,使其更唯一。


Snowflake的第二部分是时间戳,时间戳是唯一的,但是如果多个节点同时生产则会参数相同的时间戳,那怎么办?接着加唯一的值,工作机器的ID唯一呀。那么就出现了第三部分的值。但是如果在一个进程中可能两个线程同时请求,那么会产生相同的(时间戳+工作机器ID),那就继续加唯一值,在加上最后的序列号,从而保证全局唯一。


●第一个bit位(1bit):一般生成ID都为正数,所以默认为0。

●时间戳部分(41bit):保证单节点下请求唯一,但是多节点内请求会生成相同的时间戳。

●工作机器id(10bit):时间戳 + 工作机器 保证多节点同同时请求可以生成不同的主键ID。但是多节点下多线程还是存在重复。

●序列号部分(12bit),解决多节点下多线程生成ID重复的问题。


其中这种思想还挺常见的。

比如我们使用配置文件或者注册中心都有namespce的概念,namespcae和配置文件名称公共确定一个文件。再比如java中的包名。

都是通过多个唯一拼接成 一个唯一。


优点


   高性能,生成的主键贼多


缺点


   生成的主键之间跨度大,不密集,比如我想看一天的订单量,那么根据主键ID相减就没办法


   该算法强依赖时间,存在时钟回拨问题


时钟回拨问题常见的解决方案


其实很多大厂基于雪花算法开源的分布式ID解决方案一方面偏重于64的设计,另一方面偏重于时钟回拨出现后的解决方案优化。


●回拨时间很短(<100ms)

当请求系统时如果发现这个时钟回拨的时间段很小,则可以使其睡一会而到达之前最新的时间节点而继续往下执行。


●回拨时间适中(>100ms && < 1s)

维护最近的一秒(1000毫秒)每毫秒请求的最大值到Redis中(time,maxId),如果发生时钟回拨则取该毫秒的最大值+1。


●回拨时间较长(>1s && < 5s)

如下图所示,当请求Snowflake1时发现时钟回拨,则可以抛一个异常给客户端,客户端则进行其他节点的访问,负载均衡去实现。


14.png


回拨时间很长(>5s )
直接报警下线吧,都不能用了


5 总结


阿里的TDDL和美团的Leaf 有一部分是基于MySQL的区间号段实现的。目前主流的分布式主键方案主要有两种:


  1. 1.基于MySQL的区间号段实现
  2. 2.基于雪花算法SnowFlake基础之上的一些开源框架方案
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
6月前
|
监控 安全 Java
应对Go语言在分布式系统中挑战的策略
【2月更文挑战第20天】Go语言在分布式系统中的应用日益广泛,但随之而来的挑战也不容忽视。本文将从内存管理、性能优化、安全性与可靠性等方面,探讨应对Go语言在分布式系统中挑战的策略,旨在为开发人员提供实用的解决方案和思路。
|
1月前
|
存储 缓存 数据处理
深度解析:Hologres分布式存储引擎设计原理及其优化策略
【10月更文挑战第9天】在大数据时代,数据的规模和复杂性不断增加,这对数据库系统提出了更高的要求。传统的单机数据库难以应对海量数据处理的需求,而分布式数据库通过水平扩展提供了更好的解决方案。阿里云推出的Hologres是一个实时交互式分析服务,它结合了OLAP(在线分析处理)与OLTP(在线事务处理)的优势,能够在大规模数据集上提供低延迟的数据查询能力。本文将深入探讨Hologres分布式存储引擎的设计原理,并介绍一些关键的优化策略。
97 0
|
4月前
|
canal 缓存 NoSQL
Redis常见面试题(一):Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;先删除缓存还是先修改数据库,双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
Redis常见面试题(一):Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
|
3月前
|
机器学习/深度学习 资源调度 PyTorch
面向大规模分布式训练的资源调度与优化策略
【8月更文第15天】随着深度学习模型的复杂度不断提高,对计算资源的需求也日益增长。为了加速训练过程并降低运行成本,高效的资源调度和优化策略变得至关重要。本文将探讨在大规模分布式训练场景下如何有效地进行资源调度,并通过具体的代码示例来展示这些策略的实际应用。
379 1
|
3月前
|
存储 负载均衡 中间件
构建可扩展的分布式数据库:技术策略与实践
【8月更文挑战第3天】构建可扩展的分布式数据库是一个复杂而具有挑战性的任务。通过采用数据分片、复制与一致性模型、分布式事务管理和负载均衡与自动扩展等关键技术策略,并合理设计节点、架构模式和网络拓扑等关键组件,可以构建出高可用性、高性能和可扩展的分布式数据库系统。然而,在实际应用中还需要注意解决数据一致性、故障恢复与容错性以及分布式事务的复杂性等挑战。随着技术的不断发展和创新,相信分布式数据库系统将在未来发挥更加重要的作用。
|
4月前
|
缓存 自然语言处理 负载均衡
理解大模型在分布式系统中的应用和优化策略
理解大模型在分布式系统中的应用和优化策略
|
4月前
|
弹性计算 运维 负载均衡
构建高可用性的分布式系统:技术与策略
【7月更文挑战第1天】构建高可用分布式系统涉及负载均衡、容错处理和数据一致性等关键技术,遵循冗余、模块化及异步设计原则,并通过监控告警、自动化运维和弹性伸缩策略确保稳定性。
|
4月前
|
缓存 NoSQL Java
在Spring Boot中实现分布式缓存策略
在Spring Boot中实现分布式缓存策略
|
4月前
|
缓存 NoSQL Java
在Spring Boot中实现分布式缓存策略
在Spring Boot中实现分布式缓存策略
|
4月前
|
存储 Java 开发工具
Spring Cloud中的分布式配置管理策略
Spring Cloud中的分布式配置管理策略

热门文章

最新文章