深入解析分布式ID生成机制

一、概述

背景:随着数据库数据量的增长, 基于性能原因需要进行分库分表,分库分表会导致主键ID重复问题。
特点:

  1. 全局唯一性[必须];
  2. 趋势递增[非必须]。由于互联网大部分采用Mysql的Innodb引擎,因此保持有序主键ID有利于insert的效率;
  3. 单调递增[非必须]。保证下一个ID大于上一个ID,可以保证事务版本号,排序等特殊场景需求;
  4. 信息安全[非必须]。如果ID是连续的,容易被爬虫爬取数据。

二、实现方案

2.1 Java原生UUID

Universally Unique Identifie,通用唯一标识码的简称。为了保证唯一性,通用UUID规范使用MAC地址、时间戳、名字空间以及随机数等元素,保证UUID的唯一性。UUID通常由32个16进制数字,以"-"分成5段,形式为8-4-4-4-12,因此UUID理论上总数为16^32个。
优点:性能非常高,本地生成,不依赖网络;
缺点:ID是随机生成的,不连续的,影响性能。

2.2 自增ID + 步长

利用数据库自增主键,不同机器设置不同步长,步长和机器数相同(参考主键生成策略)。
优点:满足全局ID唯一性,同时利用了数据库自增主键ID,提高了性能;
缺点:

  1. 只能趋势递增,无法保证单调递增;
  2. 机器数是固定死的,一旦需要扩容,那么需要重新设置步长。

2.3 号段模式

从数据库中获取一个号段范围,比如说[1,1000]生成1到1000的自增ID加载到内存中。需要额外设置一张表记录号段相关的信息。
优点:由比较成熟的方案。类似于百度Uidgenerator,美团Leaf
缺点:依赖于数据库实现。

2.4 Redis实现

通过INCR和INCRBY类似的自增原子命令,由于Redis单线程特点,可以保证ID的唯一性和有序性。
优点:Redis性能比较好,且可以保持唯一性和有序性
缺点:

  1. 在并发请求高的情况下,需要Redis集群部署,这就要求和Mysql类似,设置步长;
  2. 需要依赖Redis实现,引入Redis组件

2.5 雪花算法(SnowFlake)

前言:是Twitter开源的由64位整数组成分布式ID,性能较高,并且在单机上递增。
在这里插入图片描述
雪花算法总共64位,具体信息为:

  1. 1bit是符号位,始终是0,表示为正数;
  2. 41bit是时间戳,单位位毫秒。41位的二进制可以使用69年,且时间是永恒递增的,所以ID总趋势是递增的
  3. 10bit是机器标识。可以全部当作机器ID,也可以标识【机房ID + 机器ID】,最多可以表示位1024台机器;
  4. 12bit是计数序列号,即在同一台机器同一时间前提下,还可以生成不同的ID,12bit理论上可以生成4096个ID。

优化点1:

由于41位是时间戳,我们的时间计算是从1970年开始的,只能使用69年。因此,可以考虑将1970修改为项目上线时间,为了不浪费,其实我们可以用【时间的相对值】,也就是以项目开始的时间为基准时间,往后可以使用69年。获取唯一ID的服务,对处理速度要求比较高,所以我们全部使用【位运算以及位移】操作,获取当前时间可以使用System.currentTimeMillis()。

优化点2:时间回拨问题
什么是时间回拨问题呢?就是服务器上的时间突然倒退到之前的时间。

  1. 人为原因,把系统环境的时间改了。
  2. 有时候不同的机器上需要同步时间,可能不同机器之间存在误差,那么可能会出现时间回拨问题。

解决方案:

  1. 回拨时间小的时候,不生成 ID,循环等待到时间点到达;
  2. 如果间隔过大,阻塞等待,肯定是不可取的,因此要么超过一定大小的回拨直接报错,拒绝服务,或者有一种方案是利用拓展位,回拨之后在拓展位上加1就可以了,这样ID依然可以保持唯一。但是这个要求我们提前预留出位数,要么从机器id中,要么从序列号中,腾出一定的位,在时间回拨的时候,这个位置+1

优点:

1. 保证了全局唯一,且在单机器上趋势是递增的,并发场景下性能依然优异;
2. 可根据自身业务特性分配bit位,比较灵活。

缺点:

1. 强依赖时钟;
2. 分布式下虽然趋势递增,但不是单调递增的

三、总结

最佳实践:一般来说,继续采用Mysql的主键ID当作逻辑主键ID,没任何含义;使用分布式全局ID当做业务主键ID,定义为no_null和unique。

相关推荐

  1. 分布式id生成方案

    2024-03-20 09:16:04       44 阅读
  2. 分布式ID(8):分布式ID生成方法

    2024-03-20 09:16:04       39 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-20 09:16:04       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-20 09:16:04       101 阅读
  3. 在Django里面运行非项目文件

    2024-03-20 09:16:04       82 阅读
  4. Python语言-面向对象

    2024-03-20 09:16:04       91 阅读

热门阅读

  1. JVM简介

    JVM简介

    2024-03-20 09:16:04      42 阅读
  2. 【c语言篇】经典100例之第1-5例

    2024-03-20 09:16:04       45 阅读
  3. 企业产品网络安全建设实录日报规划

    2024-03-20 09:16:04       45 阅读
  4. 项目当中 签到功能 +多级缓存+介绍一下点赞功能

    2024-03-20 09:16:04       43 阅读
  5. python Jira库如何修改一个issue的status

    2024-03-20 09:16:04       41 阅读
  6. 爬虫基本原理介绍、实现以及问题解决

    2024-03-20 09:16:04       40 阅读
  7. 【Docker】Jaeger 容器化部署

    2024-03-20 09:16:04       42 阅读
  8. 单例模式:双重效验锁的懒汉实现方式

    2024-03-20 09:16:04       40 阅读
  9. opencv | 编译opencv卡在ADE: Download: v0.1.1f.zip

    2024-03-20 09:16:04       42 阅读
  10. PHP与Spring Boot在实现功能上的比较

    2024-03-20 09:16:04       45 阅读
  11. 关于sftp限制登录默认目录若干问题

    2024-03-20 09:16:04       32 阅读
  12. Go语言学习12-反射和Unsafe

    2024-03-20 09:16:04       46 阅读
  13. go 解决货币计算的难题:避免浮点数陷阱

    2024-03-20 09:16:04       31 阅读