原文网址:分布式--生成数据库全局唯一ID--方法/方案_IT利刃出鞘的博客-CSDN博客

简介

本文介绍分布式项目中如何生成全局的唯一ID,通常是将其作为数据库的主键来使用的。

生成全局唯一ID有这几种方案:数据库、UUID、Redis、雪花算法、百度-UidGenerator、美团Leaf。一般情况下,推荐使用雪花算法,如果对时钟回拨有很高的要求,则推荐使用百度-UidGenerator、美团Leaf;不推荐使用数据库、UUID、Redis这三种方法。下边将详细进行讲述。

此技术也是Java后端面试中经常会问到的问题。

问题由来

传统的单体架构的时候,我们基本是单库,业务单表的结构。每个业务表的ID一般我们都是从1增,通过AUTO_INCREMENT=1设置自增起始值。

分布式数据库的分库分表中的数据需要唯一标识来找到对应的数据,还有其他的一些要求全局唯一的场景都是非常重要的。如果多个库都是用自增的方式,会造成ID冲突,如下图所示:如果第一个订单存储在 DB1 上则订单 ID 为1,当一个新订单又入库了存储在 DB2 上订单 ID 也为1。

全局的 unique ID 要满足以下需求:

  1. 保证生成的 ID 全局唯一
  2. 今后数据在多个 Shards 之间迁移不会受到 ID 生成方式的限制
  3. 生成的 ID 中最好能带上时间信息, 例如 ID 的前 k 位是 Timestamp, 这样能够直接通过对 ID 的前 k 位的排序来对数据按时间排序
  4. 生成的 ID 最好不大于 64 bits
  5. 生成 ID 的速度有要求. 例如, 在一个高吞吐量的场景中, 需要每秒生成几万个 ID (Twitter 最新的峰值到达了 143,199 Tweets/s, 也就是 10万+/秒)
  6. 整个服务最好没有单点

数据库生成

简介

由于分布式数据库的起始自增值一样所以才会有冲突的情况发生,那么我们将分布式系统中数据库的同一个业务表的自增ID设计成不一样的起始值,然后设置固定的步长,步长的值即为分库的数量或分表的数量。

以MySQL举例,利用给字段设置 auto_increment_increment和 auto_increment_offset来保证ID自增。

  • autoincrementoffset:表示自增长字段从那个数开始,他的取值范围是1 .. 65535。
  • autoincrementincrement:表示自增长字段每次递增的量,其默认值是1,取值范围是1 .. 65535。

示例

假设有三台机器,则DB1中order表的起始ID值为1,DB2中order表的起始值为2,DB3中order表的起始值为3,它们自增的步长都为3,则它们的ID生成范围如下图所示:

优缺点

优点:

  • 依赖于数据库自身不需要其他资源;ID号单调自增,可以实现一些对ID有特殊要求的业务

缺点:

  • 强依赖DB,当DB异常时整个系统不可用;
  • 一致性难以保证:主从复制可增加可用性,但数据一致性在特殊情况下难保证:主从切换时的不一致可能会导致重复发号
  • ID发号性能瓶颈限制在单台MySQL的读写性能

UUID

简介

UUID (Universally Unique Identifier),通用唯一识别码的缩写。UUID是由一组32位数的16进制数字所构成(16个字节,128位),所以UUID理论上的总数为 16^32=2^128,约等于 3.4 x 10^38。也就是说若每纳秒产生1兆个UUID,要花100亿年才会将所有UUID用完。

生成的UUID是由 8-4-4-4-12格式的数据组成,其中32个字符和4个连字符' - ',一般我们使用的时候会将连字符删除 uuid.toString().replaceAll("-","")。

目前UUID的产生方式有5种版本,每个版本的算法不同,应用范围也不同。

  1. 随机UUID

    1. 根据随机数,或者伪随机数生成UUID。
    2. 这种UUID产生重复的概率是可以计算出来的,重复的可能性可以忽略不计,因此该版本也是被经常使用的版本。
    3. JDK有这个版本的实现:UUID.randomUUID()
  2. 基于时间的UUID
    1. 一般是通过当前时间,随机数,和本地Mac地址来计算出来,可以通过 org.apache.logging.log4j.core.util包中的 UuidUtil.getTimeBasedUuid()来使用或者其他包中工具。
    2. 由于使用了MAC地址,因此能够确保唯一性,但是同时也暴露了MAC地址,私密性不够好。
  3. 基于名字的UUID(MD5)
    1. 基于名字的UUID通过计算名字和名字空间的MD5散列值得到。
    2. 保证了:相同名字空间中不同名字生成的UUID的唯一性;不同名字空间中的UUID的唯一性;相同名字空间中相同名字的UUID重复生成是相同的。
    3. JDK有这个版本的实现:UUID.nameUUIDFromBytes(nbyte)
  4. 基于名字的UUID(SHA1)
    1. 和基于名字的UUID算法类似,只是散列值计算使用SHA1(Secure Hash Algorithm 1)算法。
  5. DCE安全的UUID。
    1. DCE(Distributed Computing Environment)安全的UUID和基于时间的UUID算法相同,但会把时间戳的前4位置换为POSIX的UID或GID。这个版本的UUID在实际中较少用到。

示例

Java中 JDK自带的 UUID产生方式就是版本4根据随机数生成的 UUID 和版本3基于名字的 UUID。

public static void main(String[] args) {//获取一个根据随机字节数组的UUID。UUID uuid = UUID.randomUUID();System.out.println(uuid.toString().replaceAll("-",""));//获取一个基于名称根据指定的字节数组的UUID。byte[] nbyte = {10, 20, 30};UUID uuidFromBytes = UUID.nameUUIDFromBytes(nbyte);System.out.println(uuidFromBytes.toString().replaceAll("-",""));
}

得到结果:

59f51e7ea5ca453bbfaf2c1579f09f1d
7f49b84d0bbc38e9a493718013baace6

优点

  • 不依赖第三方

缺点

  • 不利于存储:UUID太长,16字节128位,是长度为32的字符串。
  • 信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露,暴露使用者的位置。
  • 对MySQL索引不利:    如果作为数据库主键,在InnoDB引擎下,UUID的无序性可能会引起数据位置频繁变动,严重影响性能,可以查阅 Mysql 索引原理 B+树的知识。

Redis

简介

当使用数据库来生成ID的性能达不到要求时,我们可以尝试引入中间件来生成ID,比如常见的Mongodb、Redis等,这里我们介绍一下Redis生成ID的方案。

Redis实现分布式唯一ID主要是通过提供像 INCR 和 INCRBY 这样的自增原子命令,由于Redis自身的单线程的特点所以能保证生成的 ID 肯定是唯一有序的。(可以用redis的RedisAtomicLong生成自增的ID值)。

实现

@Autowired
private RedisTemplate<String,Serializable> mRedisTemp;/*** 获取自增长ID* @param key* @return*/
public long generate(String key) {RedisAtomicLong counter = new RedisAtomicLong(key,mRedisTemp.getConnectionFactory());return counter.incrementAndGet();
}/*** 获取有过期时间的自增长ID* @param key* @param expireTime* @return*/
public long generate(String key,Date expireTime) {RedisAtomicLong counter = new RedisAtomicLong(key, mRedisTemp.getConnectionFactory());counter.expireAt(expireTime);return counter.incrementAndGet();
}/*** 获取能按固定步长增长的有过期时间的ID* @param key* @param increment* @return*/
public long generate(String key,int increment,Date expireTime) {RedisAtomicLong counter = new RedisAtomicLong(key, mRedisTemp.getConnectionFactory());counter.expireAt(expireTime);return counter.addAndGet(increment);
}

优点

  • 性能好:           因为是用Redis来生成id,不依赖数据库,而且性能优于数据库。
  • ID天然有序:    对分页或者需要排序的结果很方便。

缺点

  • 若Redis没有做持久化,然后重启了Redis,会导致ID重复。
  • 需使用Redis

总结

  • 单机存在性能瓶颈,无法满足高并发的业务需求,所以可以采用集群的方式来实现。集群的方式又会涉及到和数据库集群同样的问题,所以也需要设置分段和步长来实现。
  • 为了避免长期自增后数字过大可以通过与当前时间戳组合起来使用,另外为了保证并发和业务多线程的问题可以采用 Redis + Lua的方式进行编码,保证安全。

分布式中Redis的使用性很普遍,所以如果其他业务已经引进了Redis集群,则完全可以使用Redis来实现。可以获得系统的需求,又方便维护

雪花算法

见:分布式--雪花算法--使用/原理/实例_IT利刃出鞘的博客-CSDN博客

百度-UidGenerator

见:分布式--雪花算法改进版--百度的UidGenerator_IT利刃出鞘的博客-CSDN博客

美团Leaf

见:分布式--雪花算法改进版--美团的Leaf_IT利刃出鞘的博客-CSDN博客

其他网址

分布式全局ID生成方案_数据库_KHOST的博客-CSDN博客
分布式应用:全局唯一ID生成策略_数据库_薛嘉涛的博客-CSDN博客
项目如何生成全局唯一的id主键_Java_fan_xiong的博客-CSDN博客
分布式系统如何生成全局唯一的ID_大数据_那个番薯-CSDN博客

分布式--生成数据库全局唯一ID--方法/方案相关推荐

  1. 全局唯一ID实现方案

    1. 数据库主键自增 1.1 实现方案 通过创建表时,设置数据库主键自增 alter table sec_user modify id integer auto_increment ; 1.2 优缺点 ...

  2. Spring Boot 工程集成全局唯一ID生成器 Vesta

    2019独角兽企业重金招聘Python工程师标准>>> 本文内容脑图如下: 文章共 760字,阅读大约需要 2分钟 ! 概 述 在前一篇文章 <Spring Boot工程集成全 ...

  3. java 唯一id生成算法_分布式全局唯一ID生成方案之snowflake算法

    已有的方案: 可大致分为: 完全依赖关系/非关系型数据库递增的方案 完全不依赖数据源作为生成因子的UUID 半依赖数据源作为生成因子的snowflake 为什么推荐snowflake? 这个问题,可以 ...

  4. mysql并发获取唯一数值_高并发分布式环境中获取全局唯一ID[分布式数据库全局唯一主键生成]...

    需求说明 在过去单机系统中,生成唯一ID比较简单,可以使用MySQL的自增主键或者Oracle中的sequence, 在现在的大型高并发分布式系统中,以上策略就会有问题了,因为不同的数据库会部署到不同 ...

  5. 如何在分布式场景下生成全局唯一 ID ?

    作者 l 会点代码的大叔(CodeDaShu) 在分布式系统中,有一些场景需要使用全局唯一 ID ,可以和业务场景有关,比如支付流水号,也可以和业务场景无关,比如分库分表后需要有一个全局唯一 ID,或 ...

  6. [分布式] ------ 全局唯一id生成之雪花算法(Twitter_Snowflake)

    雪花算法(Twitter_Snowflake) 我们知道,分布式全局唯一id的生成,一般是以下几种: 基于雪花算法生成 基于数据库 基于redis 基于zookeeper 本文说下雪花算法,后面附源码 ...

  7. 分布式全局唯一ID生成算法(改进的雪花算法——解决时钟回拨问题)

    改进的雪花算法--解决时钟回拨问题 原创 公众号: 软件设计活跃区 改进的雪花算法--姑且称为梨花算法吧(忽如一夜春风来,千树万树梨花开). 改进目标:解决雪花算法的时钟回拨问题:部分避免机器id重复 ...

  8. 游戏服务器生成全局唯一ID的几种方法

    在服务器系统开发时,为了适应数据大并发的请求,我们往往需要对数据进行异步存储,特别是在做分布式系统时,这个时候就不能等待插入数据库返回了取自动id了,而是需要在插入数据库之前生成一个全局的唯一id,使 ...

  9. 全局唯一ID生成方案

    2019独角兽企业重金招聘Python工程师标准>>> 全局唯一ID生成方案对比 - http://cenalulu.github.io/mysql/guid-generate/ 转 ...

最新文章

  1. jvm性能调优 - 08什么情况下对象会被GC
  2. 用友服务器系统版本低,客户端版本低于服务器端,请升级后再登录
  3. miui12 android系统耗电,miui12耗电严重怎么办,miui12续航优化方法
  4. 数据库连接失败报错com.mysql.cj.jdbc.exceptions.CommunicationsException
  5. 哈里王子启动可持续旅行倡议 携程作为创始成员入选
  6. 使用android开发移动学习平台_移动学习平台有几种开发方法,你造吗?
  7. Tensorflow Data Adapter Error: ValueError: Failed to find data adapter that can handle input
  8. python内置数据结构方法的时间复杂度
  9. bash取得相应行的数据
  10. 团购网站安全性普遍堪忧
  11. java 开根号函数_java如何开根号?
  12. Oracle中EXECUTE IMMEDIATE用法
  13. 学生HTML个人网页作业作品 基于HTML+CSS+JavaScript明星个人主页(15页)
  14. 网游线上活动的类型及特点
  15. java ftp 假死_FTPClient下载文件程序假死问题
  16. 图像情感分析常用数据集
  17. sqlserver知识---表的创建
  18. NeoCognitron
  19. Android 开发多摄像头 API
  20. 最新v6.0 tgroupon分销系统源码+TGROUPON卖货系统 ECSHOP+ECTOUCH内核

热门文章

  1. Dreamweaver 8 的注册码
  2. c语言双字节异或,C语言 按位异或实现加法
  3. 2021Matlab项目课题推荐
  4. Excel数据透视表的使用
  5. springmvc考研交流平台 java ssm mysql
  6. java并发包线程池原理分析锁的深度化
  7. 拉姆达表达式学习(2)
  8. 虚拟偶像2022:复制下一个A-SOUL?
  9. python flask项目结构_Flask项目结构
  10. 非学校用户如何免费下载论文