首页 > 其他分享 >分布式ID生成方案总结

分布式ID生成方案总结

时间:2023-04-03 15:40:31浏览次数:55  
标签:max 数据库 生成 ID id 分布式


什么是分布式 ID

分布式 ID 是指,在分布式环境下可用于对数据进行标识且易存储的全局唯一的 ID 标识。

为什么需要分布式 ID

对于单体系统来说,主键ID可能会常用主键自动的方式进行设置,这种ID生成方法在单体项目是可行的。

对于分布式系统,分库分表之后,就不适应了,比如订单表数据量太大了,分成了多个库,如果还采用数据库主键自增的方式,就会出现在不同库或表中id一致的情况。

分布式 ID 需要满足的条件

分布式 ID 是我们在非常多的场景下用到的组件,对其要求比较高,其一般需要满足以下条件:

  • 全局唯一性:ID是作为唯一的标识,不能出现重复
  • 高性能:高可用低延时,ID 生成速度要快,否则反倒会成为业务瓶颈
  • 高可用:尽量保证服务的可用性,多实例化,避免因一个实例挂掉影响整个业务应用的运行
  • 容易接入:要秉着拿来即用的设计原则,在系统设计和实现上要尽可能的简单,避免增加开发人员的使用成本
  • 趋势递增:互联网比较喜欢MySQL数据库,而MySQL数据库默认使用InnoDB存储引擎,其使用的是聚集索引,使用有序的主键ID有利于保证写入的效率
  • 单调递增:保证下一个ID大于上一个ID,这种情况可以保证事务版本号,排序等特殊需求实现
  • 信息安全:前面说了ID要递增,但是最好不要连续,如果ID是连续的,容易被恶意爬取数据,指定一系列连续的,所以ID递增但是不规则是最好的

常用分布式 ID 生成方案

下面列出的这几种方案都是生成 ID 的常用方法:

  • 使用 UUID 生成 ID
  • 使用数据库自增生成 ID
  • 使用数据库号段模式生成 ID
  • 使用 Redis 实现生成 ID
  • 使用 Zookeeper 生成 ID
  • 根据雪花算法(Snowflake)算法生成 ID
  • 百度Uidgenerator
  • 美团Leaf
  • 滴滴TinyID

使用 UUID 生成 ID

UUID 是通用唯一识别码的缩写(Universally Unique Identifier)。UUID 一般是由一组 32 位数的 16 进制数字所构成,以连字号分为五段,形式为8-4-4-4-12的36个字符,常包含时间戳和 MAC 地址信息这些元素,标准的 UUID 格式为:

xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

优点

  • 高性能
  • 实现简单
  • 不需要第数据库等第三方组件依赖

缺点

  • 并不是趋势递增,不方便排序
  • 生成的 ID 只能用字符串类型存储,占用空间大
  • 生成的串没有规律,出问题时不易根据 ID 进行排查

使用数据库自增生成 ID

在分布式系统中我们可以多部署几台机器,每台机器设置不同的初始值,且步长和机器数相等。比如有两台机器:设置步长step为2,Server1的初始值为1(1,3,5,7,9,11…)、Server2的初始值为2(2,4,6,8,10…)。

这种方案看起来是可行的,但是如果要扩容,步长step等要重新设置,假如只有一台机器,步长就是1,比如1,2,3,4,5,6,这时候如果要进行扩容,就要重新设置,机器2可以挑一个偶数的数字,这个数字在扩容时间内,数据库自增要达不到这个数的,然后步长就是2,机器1要重新设置step为2,然后还是以一个奇数开始进行自增。这个过程看起来不是很杂,但是,如果机器很多的话,那就要花很多时间去维护重新设置。

优点

  • 实现简单
  • 趋势递增

缺点

  • ID没有了单调递增的特性,只能趋势递增,有些业务场景可能不符合
  • 数据库压力还是比较大,每次获取ID都需要读取数据库,只能通过多台机器提高稳定性和性能
  • 水平扩展比较麻烦,需要手动调整集群数据库中的初始值与步长

使用数据库号段模式生成 ID

这种模式也是现在生成分布式ID的一种方法,在使用号码模式时,我们通常会先建立一张表用于记录上述的 ID 号段范围,一般表内容如下:

CREATE TABLE id_generator (
  id int(10) NOT NULL AUTO_INCREMENT,
  max_id bigint(20) NOT NULL COMMENT '当前最大id',
  step int(20) NOT NULL COMMENT '号段的布长',
  biz_type    int(20) NOT NULL COMMENT '业务类型',
  version int(20) NOT NULL COMMENT '版本号',
  PRIMARY KEY (`id`)
)

每次从数据库中获取号段 ID 的范围时,都会执行更新语句,其中计算新号段范围最大值 max_id 的公式是 max_id + step 组成,所以 SQL 中设置 max_id = max_id+step 来执行更新语句,更新数据库中这个范围最大值 max_id,然后再通过查询语句查询更新后 ID 最大值,再根据最大值 max_id 与步长 step 计算出待生成的 ID 的范围,其中操作的 SQL 如下:

update id_generator set max_id = #{max_id+step}, version = version + 1 where version = # {version} and biz_type = #{biz_type};


SELECT `max_id`, `step`, `version` FROM `myid` WHERE `biz_type` = #{biz_type};

优点

  • 使用缓存机制,容灾性高,即使数据库不可用还能撑一段时间。
  • 可以自定义每次扩展的大小,控制 ID 生成速度;
  • 可以设置生成 ID 的初始范围,方便业务从原有的 ID 方式上迁移过来。
  • 有比较成熟的方案,像百度Uidgenerator,美团Leaf

缺点

  • 依赖于数据库实现,数据库宕机会造成整个系统不可用。
  • ID 号码不够随机,可能够泄露发号数量的信息,不太安全。
     

使用 Redis 实现生成 ID

Redis分布式ID实现主要是通过提供像 INCR 和 INCRBY 这样的自增原子命令,由于Redis单线程的特点,可以保证ID的唯一性和有序性。

优点

  • 实现简单;
  • 有序递增,方便排序;

缺点

  • 强依赖于 Redis,可能存在单点问题;
  • 如果 Redis 超时,可能会对业务造成影响;
  • 占用宽带,而且需要考虑网络延时等问题带来地性能冲击。

使用 Zookeeper 生成 ID

在 Zookeeper 中主要通过节点数据版本号来生成序列号,可以生成 32 位和 64 位的数据版本号,客户端可以使用这个版本号来作为唯一的序列号。在 Zookeeper 中本身就是支持集群模式,所以能保证高可用性,且生成的 ID 为趋势递增且有序,不过在实际使用中很少用 Zookeeper 来充当 ID 生成器,因为 Zookeeper 中存在强一致性,在高并发场景下其性能可能很难满足需求。

分布式ID生成方案总结_自增

不过由于使用 Zookeeper 节点的版本号来充当 ID 号是比较繁琐,需要创建节点获取生成的 ID,然后去掉节点命令前缀,只截取数字部分,最后还要异步执行删除节点(启动新的线程执行删除节点操作,防止占用生成ID线程执行的实际)。过程比较耗时且繁琐,所以,在操作 Zookeeper 时经经常不会采用该方案,常使用 Curator 客户端提供的基于乐观锁的计数器来自增实现 ID 生成,这个过程和数据库自增生成 ID 类似。

优点

  • 高可用
  • 趋势递增

缺点

  • 性能差
  • 定期删除之前生成的节点,比较繁琐

根据雪花算法(Snowflake)算法生成 ID

Snowflake,雪花算法是由 Twitter 开源的分布式ID生成算法,以划分命名空间的方式将
64-bit位分割成多个部分,每个部分代表不同的含义,64位,在java中Long类型是64位的,所以java程序中一般使用Long类型存储
 

分布式ID生成方案总结_数据库_02

其结构组成:

  • 第一部分:第一位占用1bit,始终是0,是一个符号位,不使用
  • 第二部分:第2位开始的41位是时间戳。41-bit位可表示241个数,每个数代表毫秒,那么雪花算法可用的时间年限是(241)/(1000606024365)=69 年的时间
  • 第三部分:10-bit位可表示机器数,即2^10 = 1024台机器。通常不会部署这么多台机器
  • 第四部分:12-bit位是自增序列,可表示2^12 = 4096个数。觉得一毫秒个数不够用也可以调大点

优点:

  • 高性能
  • 趋势递增
  • 可以灵活调整结构
  • 不需要第数据库等第三方组件依赖

缺点:

  • 强依赖时钟,可能发生时钟回拨导致生成的 ID 重复

百度 Uidgenerator

百度的 UidGenerator 是百度开源基于Java语言实现的唯一ID生成器,是在雪花算法 snowflake 的基础上做了一些改进。

详细介绍请看官网

美团 Leaf

Leaf 提供两种生成的ID的方式:号段模式(Leaf-segment)和 snowflake 模式(Leaf-snowflake)。你可以同时开启两种方式,也可以指定开启某种方式,默认两种方式为关闭状态。

详细介绍请看官网

滴滴 TinyID

Tinyid 是用Java开发的一款分布式id生成系统,基于数据库号段算法实现。Tinyid 扩展了 leaf-segment 算法,支持了多数据库和 tinyid-client。

详细介绍请看官网

参考

SmileNicky的博客

myf008的博客

标签:max,数据库,生成,ID,id,分布式
From: https://blog.51cto.com/u_15856116/6166500

相关文章

  • scrapy爬虫框架(四)Downloader Middleware的使用
      DownloaderMiddleware是处于Engine和Downloader之间的模块,其重要作用就是处理schduler调度器发送到Engine的Request和经过Downloader响应后的response返回至Engine过程中的处理。如图所示:  也就是说,DownloaderMiddlerware在整个架构中起到作用的位置是以下两个:Engine......
  • Android开发-Android常用组件-SeekBar拖动条
    4.9 SeekBar拖动条android:max滑动条的最大值android:progress滑动条的当前值android:secondaryProgress二级滑动条的进度android:thumb滑块的drawable 接着要说下SeekBar的事件了,SeekBar.OnSeekBarChangeListener我们只需重写三个对应......
  • 成品直播源码推荐,Android 禁止下拉菜单栏
    成品直播源码推荐,Android禁止下拉菜单栏1.屏蔽非锁屏下的下拉菜单栏这种Android系统其实是提供了方法的,只不过是隐藏的,只给系统应用,也就是用mk编译的apk使用。如果第三方应用想要使用,或者Androidstuido编译方式的想要使用。则可以使用反射。 /** *Allowsanapptoco......
  • 搭建直播平台,android 如何得到本地视频的缩略图
    搭建直播平台,android如何得到本地视频的缩略图 publicclassVideoThumbUtils{  /**   *得到视屏的缩略图   *   *@paramvideoPath   *@paramwidth   *@paramheight   *@return   */  publicstaticBitmapgetVideoThum......
  • 介绍一下requestAnimationFrame和requestIdleCallback
    当我们需要执行动画或其他高性能操作时,常常会遇到以下问题:-任务的执行频率过高,对CPU和内存造成了大量的压力。-任务的优先级较高,导致其他任务无法及时得到处理。为了解决这些问题,JavaScript提供了两个调度API:requestAnimationFrame和requestIdleCallback。 request......
  • idea run控制台中文乱码
    教程:https://blog.csdn.net/weiwu13/article/details/121417404解决方式方式1:设置虚拟机参数(-Dfile.encoding=UTF-8)......
  • Android AVB中的几种Descriptor
    avbtoolinfo_image查看img信息./android/external/avb/avbtoolinfo_image--imageout/evb/download_images/emmc/vbmeta.imgMinimumlibavbversion:1.0HeaderBlock:256bytesAuthenticationBlock:576bytesAuxiliaryBlock:3456byte......
  • 分布式计算ECHO算法(IT部落格)
    packageorg.ustc.scst.dc.simulation.algorithms.echo;importjava.awt.Color;importorg.ustc.scst.dc.simulation.algorithms.echo.IntMessage;importorg.ustc.scst.dc.simulation.model.Message;importorg.ustc.scst.dc.simulation.model.Node;/***Thiss......
  • redis使用setnx+lua实现分布式锁
    在Redis中,使用SETEX命令(对应RedisTemplate的setIfAbsent方法)可以实现一个最简易的分布锁。SETEX命令当key不存在的话,才会设置key的值,如果可以已经存在,就不做任何操作。为了避免锁无法被释放,就给这个key(也就是锁)设置一个过期时间。为了保证解锁操作的原子性,使用Lua脚本进行释放锁......
  • 恢复lazarus使用anchorDocking和dockedformeditor后出现ide乱的步骤
    恢复lazarus使用anchorDocking和dockedformeditor后,很容易因使用时不小心拖动form后出现越调越的情况,经模索发现将environmentoptions.xml删除后,再运行lazarus,当出现这提示时,重新设置一下       ......