一、分布式ID简介

1、什么是分布式ID?

在我们业务数据量不大的时候,单库单表完全可以支撑现有业务,数据再大一点搞个MySQL主从同步读写分离也能对付。

但随着数据日渐增长,主从同步也扛不住了,就需要对数据库进行分库分表,但分库分表后需要有一个唯一ID来标识一条数据,数据库的自增ID显然不能满足需求;例如我们的订单,需要有一个全局唯一标识的订单号,这个订单号就是分布式ID

2、分布式ID需要满足那些条件?

  1. 全局唯一:必须保证ID是全局性唯一的,基本要求

    1. 高可用:
    1. 高性能:高可用低延时,ID生成响应要块,否则反倒会成为业务瓶颈
    1. 简单通用:拿来即用的原则

二、 分布式ID都有哪些生成方式?

  1. UUID
  1. 数据库自增ID
  1. 数据库多主模式
  1. 号段模式
  1. Redis
  1. 雪花算法(SnowFlake)
  1. 滴滴出品(TinyID)
  1. 百度 (Uidgenerator)
  1. 美团(Leaf)
  1. MongoDB的ObjectID

1、UUID

   ​String uuid = UUID.randomUUID().toString().replaceAll("-","");​

优点:

  • 足够简单,只需要上面一行代码即可生成一个全局唯一的ID,

缺点:

  • 因为UUID是随机的,没有具体的业务含义,如果用UUID作为订单号,是没有意义的,从这个UUID看不到跟订单有任何的关联关系,
  • UUID是一个无序的字符串,不具备趋势自增特性
  • 长度过长,存储以及查询对MySQL的性能消耗较大,如果用来作为主键,索引的性能会很差

2、数据库自增ID

基于数据库的​auto_increment​自增ID也可以生成一个分布式ID

优点:

  • 实现简单,ID单调自增,数值类型查询速度快

缺点:

  • 访问量激增时MySQL本身就是系统的瓶颈,用它来实现分布式服务风险比较大,不推荐!

3、数据库集群模式

为了解决第二种数据库单个DB的风险,可以采用数据库集群的模式来解决这个问题。

  • 可以使用主从模式解决单DB不满足高可用的问题,需要考虑的问题是:主从之间同步是存现时间延迟的,如果主DB挂了可能会有重复的ID生成(原因是什么? --  主从复制延时导致)
  • 如果担心一个主节点挂掉没法用,那就做双主模式集群,也就是两个Mysql实例都能单独的生产自增ID。需要考虑的问题是:1:两个MySQL实例的自增ID都从1开始,会生成重复的ID,怎么解决?2:后续DB扩容怎么办

4、基于数据库的号段模式

号段模式是当下分布式ID生成器的主流实现方式之一,号段模式可以理解为从数据库批量的获取自增ID,每次从数据库取出一个号段范围,例如 (1,1000] 代表1000个ID,具体的业务服务将本号段,生成1~1000的自增ID并加载到内存,典型的是TinyID。表结构如下:

​CREATE TABLE id_generator (​​  id int(10) NOT NULL,​​  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​字段做一次​update​操作,​update max_id= max_id + step​,update成功则说明新号段获取成功,新的号段范围是​(max_id ,max_id +step]​。

SQL语句:

​update id_generator set max_id = #{max_id+step} where biz_type = XXX​

为了解决并发更新的问题,使用乐观锁控制,优化后的SQL:

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

优点:

  • 这种分布式ID生成方式不强依赖于数据库,不会频繁的访问数据库,对数据库的压力小很多

5、基于Redis模式

Redis也同样可以实现,原理就是利用redis的 incr命令实现ID的原子性自增。

Redis是内存数据库,需要考虑持久化的问题,redis有两种持久化方式RDB和AOF

  • RDB:会定时打一个快照进行持久化,假如连续自增但redis没及时持久化,而这会Redis挂掉了,重启Redis后会出现ID重复的情况。
  • AOF:对每条写命令进行持久化,即使redis挂掉了也不会出现ID重复的情况,但由于incr命令的特殊性,会导致Redis重启恢复的数据时间过长。

6、基于雪花算法(Snowflake)模式

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

Snowflake生成的是Long类型的ID,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特。

Snowflake ID组成结构:​正数位​(占1比特)+ ​时间戳​(占41比特)+ ​机器ID​(占5比特)+ ​数据中心​(占5比特)+ ​自增值​(占12比特),总共64比特组成的一个Long类型。

  • 第一个bit位(1bit):Java中long的最高位是符号位代表正负,正数是0,负数是1,一般生成ID都为正数,所以默认为0。
  • 时间戳部分(41bit):毫秒级的时间,不建议存当前时间戳,而是用(当前时间戳 - 固定开始时间戳)的差值,可以使产生的ID从更小的值开始;41位的时间戳可以使用69年,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年
  • 工作机器id(10bit):也被叫做​workId​,这个可以灵活配置,机房或者机器号组合都可以。
  • 序列号部分(12bit),自增值支持同一毫秒内同一个节点可以生成4096个ID

Snowflake雪花算法最核心的是中间的10位工作机器ID的分配,做到自动生成workID,避免运维人员的去分配

优点:

  • 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。
  • 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。
  • 可以根据自身业务特性分配bit位,非常灵活。

缺点:

  • 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。

根据算法的思路,使用java语言可以直接生成一个工具类,进行本地调用用来生成全局唯一的ID,

参考github地址:https://github.com/beyondfengyu/SnowFlake

7、滴滴出品(TinyID)

​Tinyid​由滴滴开发,Github地址:https://github.com/didi/tinyid

​Tinyid​是基于号段模式每个服务获取一个号段(1000,2000]、(2000,3000]、(3000,4000]

1:具体实现原理:

  • tinyid是基于数据库发号算法实现的,简单来说是数据库中保存了可用的id号段,tinyid会将可用号段加载到内存中,之后生成id会直接内存中产生。
  • 可用号段在第一次获取id时加载,如当前号段使用达到一定量时,会异步加载下一可用号段,保证内存中始终有可用号段。当前号段使用完毕,下一号段会替换为当前号段。依次类推。

2:架构图

提供了两种接入方式:

  1. http接入,通过访问tinyid-server来生成ID,其中tinyid-server推荐部署到多个机房的多台机器,这种方式需要考虑网络的延时性
  1. 使用tinyid-client来获取id,

优点:

  • id为本地生成(调用AtomicLong.addAndGet方法),性能大大增加
  • client对server访问变的低频,减轻了server的压力,无须担心网络延迟
  • 即便所有server挂掉,因为client预加载了号段,依然可以继续使用一段时间

缺点:

  • 如果client机器较多频繁重启,可能会浪费较多的id(原因是什么?

8、百度 (Uidgenerator)

UidGenerator是Java实现的, 基于Snowflake算法的唯一ID生成器。Github地址:https://github.com/baidu/uid-generator

UidGenerator以组件形式工作在应用项目中, 支持自定义workerId位数和初始化策略, 从而适用于docker等虚拟化环境下实例自动重启、漂移等场景, UidGenerator通过借用未来时间来解决sequence天然存在的并发限制; 采用RingBuffer来缓存已生成的UID, 并行化UID的生产和消费。

具体算法实现:

Snowflake算法:

Snowflake算法描述:指定机器 & 同一时刻 & 某一并发序列,是唯一的。据此可生成一个64 bits的唯一ID(long)。默认采用上图字节分配方式:

  • sign(1bit)
  • 固定1bit符号标识,即生成的UID为正数。
  • delta seconds (28 bits)
  • 当前时间,相对于时间基点"2016-05-20"的增量值,单位:秒,最多可支持约8.7年
  • worker id (22 bits)
  • 机器id,最多可支持约420w次机器启动。内置实现为在启动时由数据库分配,默认分配策略为用后即弃,后续可提供复用策略。
  • sequence (13 bits)
  • 每秒下的并发序列,13 bits可支持每秒8192个并发。

9、美团(Leaf)

Leaf由美团开发,github地址:https://github.com/Meituan-Dianping/Leaf

同时支持号段模式和Snowflake算法模式,可以切换使用。

1:Leaf-segment号段模式:

在使用数据库的方案上,做了如下改变:

  1. 原方案每次获取ID都得读写一次数据库,造成数据库压力大。改为利用proxy server批量获取,每次获取一个segment(step决定大小)号段的值。用完之后再去数据库获取新的号段,可以大大的减轻数据库的压力
  2. 各个业务不同的发号需求用biz_tag字段来区分,每个biz-tag的ID获取相互隔离,互不影响

号段模式的具体实现思路同TinyID一样

2:Leaf-snowflake雪花算法:

完全沿用snowflake方案的bit位设计,即是“1+41+10+12”的方式组装ID号。为了解决集权下workID的问题,美团的Leaf-snowflake跟百度不一样,百度是通过数据库来生成workID,而美团是通过Zookeeper持久顺序节点的特性自动对snowflake节点配置wokerID,所以Leaf是依赖ZK服务的。

特点:

  1. 弱依赖ZooKeeper:除了每次会去ZK拿数据以外,也会在本机文件系统上缓存一个workerID文件。当ZooKeeper出现问题,恰好机器出现问题需要重启时,能保证服务能够正常启动。这样做到了对ZK的弱依赖
  2. 解决时钟问题:因为这种方案依赖时间,如果机器的时钟发生了回拨,那么就会有可能生成重复的ID号,需要解决时钟回退的问题。

九种分布式ID生成算法详解相关推荐

  1. 雪花算法snowflake分布式id生成原理详解,以及对解决时钟回拨问题几种方案讨论

    文章目录 一.前言 二.雪花算法snowflake 1.基本定义 2.snowflake的优缺点 三.Java代码实现snowflake 1.组装生成id 2.计算最大值的几种方式 3.反解析ID 4 ...

  2. 分布式ID生成算法——leaf算法

    leaf是美团在雪花算法的基础上提出的一种分布式ID生成算法,它具有全局唯一.高可用.高并发.低延迟.接入简单(http或公司内rpc)的优点. Twitter:世界上不存在两片一样的雪花. 美团:世 ...

  3. 美团技术分享:深度解密美团的分布式ID生成算法

    本文来自美团技术团队"照东"的分享,原题<Leaf--美团点评分布式ID生成系统>,收录时有勘误.修订并重新排版,感谢原作者的分享. 1.引言 鉴于IM系统中聊天消息I ...

  4. 美团分布式mysql_9种分布式ID生成之美团(Leaf)实战

    整理了一些Java方面的架构.面试资料(微服务.集群.分布式.中间件等),有须要的小伙伴能够关注公众号[程序员内点事],无套路自行领取javascript 更多优选java 引言 前几天写过一篇< ...

  5. 理解分布式id生成算法--雪花算法(SnowFlake)

    分布式ID生成算法的有很多种,Twitter的SnowFlake就是其中经典的一种. 注: 1B就是1个字节. Byte.KB.B.MB.GB之间的关系是: Bit--比特 : B --字节:KB-- ...

  6. python雪花算法生成id_理解分布式id生成算法SnowFlake

    分布式id生成算法的有很多种,Twitter的SnowFlake就是其中经典的一种. 概述 SnowFlake算法生成id的结果是一个64bit大小的整数,它的结构如下图: 1位,不用.二进制中最高位 ...

  7. 面试官:你会几种分布式 ID 生成方案???

    1. 为什么需要分布式 ID 对于单体系统来说,主键 ID 常用主键自动的方式进行设置.这种 ID 生成方法在单体项目是可行的,但是对于分布式系统,分库分表之后就不适应了.比如订单表数据量太大了,分成 ...

  8. java怎样生成32位全是整形的主键_你肯定会需要的分布式Id生成算法雪花算法(Java)...

    最近公司正好在做数据库迁移从oracle到mysql,因为之前oracle主键是使用的 SYS_GUID() 这个oracle提供的函数来生成全球唯一的标识符(原始值)由16个字节组成. 不过由于my ...

  9. 九种分布式ID生成方式

    一.为什么要用分布式ID? 在说分布式ID的具体实现之前,我们来简单分析一下为什么用分布式ID?分布式ID应该满足哪些特征? 1.什么是分布式ID? 拿MySQL数据库举个栗子:在我们业务数据量不大的 ...

最新文章

  1. java中的进制输出转换_Java I/O : Java中的进制详解
  2. POJ 3278 Catch That Cow(BFS)
  3. 如何对加载的数个模型只进行transform呢
  4. Just write about
  5. 微信sdk swift版_使用Swift 4的iOS版Google Maps SDK终极指南
  6. Option键竟然如此强大,99.9%的Mac用户不知道的高效秘技
  7. VC++2012编程演练数据结构《35》多路平衡归并
  8. 号称2020最轻薄的5G旗舰,这款手机 你不看看吗?
  9. -------------初识----------动态规划。--------------------------------------------
  10. linux/windows下查看目标文件.a/.lib的函数符号名称
  11. 精读《如何编译前端项目与组件》
  12. 3Y叔的clusterProfiler-book阅读Chapter 3 Universal enrichment analysis
  13. 设计模式(六) : 创建型模式--原型模式
  14. My97 DatePicker 选择时间后弹出选择的时间
  15. Microsoft Expression blend 3 新功能简介
  16. 解决AD不能复制粘贴
  17. 自己做的萌萌哒的js宠物挂件~
  18. 能耗监控 | FCU1101物联数采网关在电力能效管理系统中的应用
  19. Bootstrap Validate 下拉框验证
  20. MacOS技巧|Mac如何自定义触控栏Touch Bar?显示Touch Bar教程

热门文章

  1. 2.特定领域知识图谱融合方案:学以致用-问题匹配鲁棒性评测比赛验证【四】
  2. 习武与职场修炼的联系
  3. 手机扫码登录电脑QQ出现无法登录问题
  4. 跨境电商B2B风口上,阿里巴巴的海外布局
  5. Java获取任务管理器内存、各磁盘内存、CPU使用率数据
  6. macos多合一系统安装u盘制作器_重装系统——制作U盘启动盘
  7. 为什么很牛的讯飞输入法今天才火,还得靠罗永浩?
  8. ftb临时文件传输服务器,AirPortal - 临时文件传输服务
  9. 最新20句比尔盖茨名言警句大全
  10. Vue中编写老师页面