分布式ID生成方案(一):总体概述

为什么需要分布式ID

在复杂的业务系统中,往往需要对大量的数据和消息进行唯一标识。如电商系统中常见的订单号。

系统使用前期,可能数据量小,应用和数据库方面压力不大,这样单体架构的基础上,我们可以直接使用数据库自增ID及对应的单号生成规则即可满足。

但随着用户量的增加,数据量也随之增大,我们会随之调整为分布式系统架构,集群部署应用、数据库分库分表等改造,原来简单的数据库自增ID已经不能满足生成ID的全局唯一性。

分布式ID特性
  • 全局唯一性:不能出现重复的ID,最基本的要求
  • 趋势递增:MySQL InnoDB引擎使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上面我们应尽量使用有序的主键保证写入性能。
  • 单调递增:保证下一个ID一定大于上一个ID。
  • 信息安全:如果ID是连续递增的,恶意用户就可以很容易的窥见订单号的规则,从而猜出下一个订单号。所以在某些场景下,需要ID无规则。
分布式ID生成方案

1、UUID:通用唯一识别码,16个字节128位的长数字

组成部分:
当前时间和时间序列+全局唯一性网卡地址

代码实现:

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

UUID的生成简单到只有一行代码,但UUID却并不适用于实际的业务需求。

像用作订单号UUID这样的字符串没有丝毫的意义,看不出和订单相关的有用信息;而对于数据库来说用作业务主键ID,它不仅是太长还是字符串,存储性能差查询也很耗时,所以不推荐用作分布式ID。

优点:
代码实现简单,不占用宽带,数据迁移不受影响

缺点:
无序,无法保证趋势递增,查询慢,不可读

2、雪花算法:国外的twitter分布式下ID生成算法

组成部分:
1bit+41bit+10bit+10bit=62bit
高位随机+毫秒数+机器码(数据中心+机器id)+10的流水号

优点:
代码实现简单,不占用宽带,数据迁移不受影响,低位趋势递增

缺点:
多台服务器时间一定要一样,无序无法保证趋势递增要求

3、Mysql

(1)基于数据库的auto_increment自增ID

当我们需要一个ID的时候,向表中插入一条记录返回主键ID,但这种方式有一个比较致命的缺点,访问量激增时MySQL本身就是系统的瓶颈,用它来实现分布式服务风险比较大,不推荐!

优点:
代码实现方便,性能不错,数字排序,可读性很强

缺点:
受限数据库,扩展麻烦,插入数据库才能拿到ID,单点故障问题

(2)基于数据库集群模式

对方式(1)做高可用优化,换成主从模式集群,害怕一个主节点挂掉没法用,那就做双主模式集群,也就是两个Mysql实例都能单独的生产自增ID。

那这样还会有个问题,两个MySQL实例的自增ID都从1开始,会生成重复的ID怎么办?

解决方案:
设置起始值和自增步长

优点:
解决DB单点问题

缺点:
不利于后续扩容,而且实际上单个数据库自身压力还是大,依旧无法满足高并发场景。

(3)基于数据库的号段模式

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

由于多业务端可能同时操作,所以采用版本号version乐观锁方式更新,这种分布式ID生成方式不强依赖于数据库,不会频繁的访问数据库,对数据库的压力小很多。

4、Redis

Redis的所有命令操作都是单线程的,本身提供像 incr 和 increby 这样的自增原子命令,所以能保证生成的 ID 肯定是唯一有序的。

优点:
不依赖于数据库,灵活方便,且性能优于数据库。
数字ID天然排序,对分页或者需要排序的结果很有帮助。

缺点:
如果系统中没有Redis,还需要引入新的组件,增加系统复杂度。
需要占用网络资源,性能要比本地生成慢。

用redis实现还需要注意一点,要考虑到redis持久化的问题。redis有两种持久化方式RDB和AOF:

  • RDB会定时打一个快照进行持久化,假如连续自增但redis没及时持久化,而这会Redis挂掉了,重启Redis后会出现ID重复的情况。

  • AOF会对每条写命令进行持久化,即使Redis挂掉了也不会出现ID重复的情况,但由于incr命令的特殊性,会导致Redis重启恢复的数据时间过长。

5、百度(uid-generator)

uid-generator是由百度技术部开发,项目GitHub地址 https://github.com/baidu/uid-generator

uid-generator是基于Snowflake算法实现的,与原始的snowflake算法不同在于,uid-generator支持自定义时间戳、工作机器ID和 序列号 等各部分的位数,而且uid-generator中采用用户自定义workId的生成策略。

uid-generator需要与数据库配合使用,需要新增一个WORKER_NODE表。当应用启动时会向数据库表中去插入一条数据,插入成功后返回的自增ID就是该机器的workId数据由host,port组成。

6、美团(Leaf)

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

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

7、滴滴(Tinyid)

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

Tinyid是基于号段模式原理实现的与Leaf如出一辙,每个服务获取一个号段(1000,2000]、(2000,3000]、(3000,4000]

总结

以上基本列出了所有常用的分布式ID生成方式,其实大致分类的话可以分为两类:

一种是类DB型的,根据设置不同起始值和步长来实现趋势递增,需要考虑服务的容错性和可用性。

另一种是类snowflake型,这种就是将64位划分为不同的段,每段代表不同的涵义,基本就是时间戳、机器ID和序列数。

每种生成方式都有它自己的优缺点,具体如何使用还要看具体的业务需求。

猜你喜欢

转载自blog.csdn.net/u013034223/article/details/105633783