写在前面 本篇来学习9种常用的分布式ID生成方案,掌握这些对于提升自身能力有极大帮助。
为什么使用分布式ID 分布式ID的定义 这里以MySQL数据库为例进行说明,当我们的业务数据量不大时,单库单表完全可以支撑现有业务,数据再大一点时,使用MySQL主从同步、读写分离模式也能应付。但是当数据日益增长,达到亿级别的时候,就需要对数据库进行分库分表操作,而分库分表后需要有一个唯一的ID来标识每条数据,显然数据库的自增ID无法满足需求,那么这个 全局唯一ID 就叫 分布式ID 。
分布式ID满足的条件 一般来说,分布式ID需要满足如下五个要求:
全局唯一:必须保证ID是全局性唯一;
高性能:高可用低延时,ID生成响应快,否则会成为业务瓶颈;
高可用:100%可用无法做到,但是要无限接近于100%的可用;
好接入:秉承拿来即用的设计原则,在系统设计和实现上要尽可能的简单;
趋势递增:最好有趋势递增,不过要结合实际业务进行分析。分布式ID的生成方式 分布式ID的生成方式有很多种,此处列举常用的9种方式,如下所示: (1)UUID; (2)数据库自增ID; (3)数据库多主模式; (4)号段模式; (5)Redis; (6)SnowFlake(雪花算法); (7)Uidgenerator (百度); (8)TinyID(滴滴); (9)Leaf(美团)。 本篇文章将学习以上9种分布式ID的生成方式,并学习其原理和优缺点,以便在实际开发过程中能有针对性的进行选择使用。UUID 由于笔者使用的是Java语言,而Java自带了一个能生成全球唯一ID的UUID类,如下所示:1 2 3 4 public static void main(String[] args) { String str = UUID.randomUUID().toString().replace("-", ""); System.out.println(str); }
执行上述方法,输出结果为:15751784476b47719021f39dffe49003。实际上生成的UUID可以作为分布式ID,但是并不建议使用它。因为生成的UUID体现不出业务的含义,而且对于数据库而言,UUID的长度过长且是字符串,存储和查询性能较差。
优点:
生成逻辑简单,一行代码搞定;
本地生成无网络消耗且具有唯一性。
缺点:
无序的字符串,不具备趋势自增特性;
无法体现具体的业务含义;
长度过长且是字符串,存储和查询性能较差。数据库自增ID 这里以MySQL数据库为例进行说明,可以使用MySQL的 auto_increment 自增ID来充当分布式ID。
举个例子,下面是一个表,使用它的自增主键ID作为分布式ID,对应的建表语句如下:
1 2 3 4 5 6 7 CREATE TABLE sequence_id ( id bigint(20) unsigned NOT NULL auto_increment, value char(10) NOT NULL default '', PRIMARY KEY (id), ) ENGINE=InnoDB; insert into sequence_id(value) VALUES ('zhangsan');
当我们需要一个ID时,只需往该表中插入一条记录,即可返回该记录的主键ID,不过这种方式仅适用于并发量不大的情况,在分布式系统中不建议使用。
优点:
实现简单且ID单调自增;
采用数值类型,因此查询速度快。
缺点:
数据库存在宕机故障,无法抗住高并发场景。数据库集群模式 既然单点数据库方式存在高并发问题,那么我们可对上述方式进优化,采用主从模式集群模式。考虑到单主节点挂掉,无法高可用,那么可采用双主模式集群,即两个Mysql实例都能单独生成自增ID。
不过这里还有一个问题,就是两个MySQL实例的自增ID都从1开始,肯定会生成重复的ID,此时可以考虑错开设置起始值以及采用自增步长。
举个例子,假设这里有两个MySQL实例,接下来我们尝试对其做如下配置: (1)MySQL_1的配置如下:
1 2 set @@auto_increment_offset = 1; -- 起始值 set @@auto_increment_increment = 2; -- 步长
(2)MySQL_2的配置如下:
1 2 set @@auto_increment_offset = 2; -- 起始值 set @@auto_increment_increment = 2; -- 步长
通过上面的配置,那么这两个MySQL实例的自增ID分别如下:
1 2 MySQL_1:1、3、5、7、9 MySQL_2:2、4、6、8、10
当然了,如果使用此集群后,性能还是不佳,无法扛住高并发,可考虑对MySQL集群进行扩容处理,即增加节点,不过这个操作比较复杂: 可以看到,由于上述数据库集群支持水平扩展,因此有利于解决数据库单点压力的问题,同时为了ID生成特性,需要将自增步长按照机器数量来设置。
这个操作其实比较复杂,我们仅仅是往集群中添加了一个实例,就需要对上述三个实例的起始值和步长都进行调整,将第三台机器的ID起始值调整为比现有最大自增ID的值都大,而且必须在前面两台机器还没有增长到第三台机器的起始ID值时才能操作,否则自增ID就重复了,这是非常严重的问题,必要时还可能需要停机修改。
优点:
缺点:
水平扩容较为复杂,且实际上单个数据库压力还是很大,依旧无法满足高并发场景。数据库号段模式 数据库号段模式是当前分布式ID生成器的主流实现方式之一,号段模式可以这样理解:首先从数据库批量获取自增ID,然后每次从数据库取出一个号段范围,如 (1,1000] 代表1000个ID,接着某一业务微服务将本号段生成1~1000的自增ID并加载到内存中。对应的表结构如下:1 2 3 4 5 6 7 8 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`) )
简单解释一下上述字段的含义: (1)max_id ,表示当前可用的最大id; (2)step,表示号段的步长; (3)biz_type,表示不同的业务类型; (4)version ,一个乐观锁,注意每次对数据进行更新操作,都需要更新version字段,保证并发时数据的正确性。
下面是一条示例,如下所示: 后续等这批号段ID用完了,可再次向数据库申请新号段,只需对 max_id 字段做一次 update 操作,执行如下语句:
1 2 3 4 5 update id_generator set max_id = max_id + step, version = version + 1 where biz_type = #{bizType} and max_id = #{maxId} and version = #{version}
上述语句执行成功,则说明新号段获取成功,此时新的号段范围为 (max_id , max_id +step] 。考虑到多个业务可能同时操作,因此引入版本号 version 字段,采用乐观锁方式更新。
优点:
不强依赖数据库,且不会频繁的访问数据库,对数据库的压力很小。
缺点:
当存在多个业务,且业务使用频率差距较大时,会造成有些业务号段不够用,有些使用率不高的情况。Redis模式 Redis提供了incr命令,因此通过该命令也可以实现分布式ID的生成功能。举个例子,如下所示:1 2 3 4 127.0.0.1:6379> set sequence_id 1 // 初始化自增ID为1 OK 127.0.0.1:6379> incr sequence_id // 增加1,并返回递增后的数值 (integer) 2
请注意,在使用Redis实现时需要考虑Redis的持久化问题。我们知道Redis有两种持久化方式:RDB和AOF。下面说一下这两种情况分别可能存在的问题: (1)RDB方式是定时生成一个快照,进而进行持久化。这样就有一个问题,假如连续自增的Redis没及时进行持久化就挂掉了,那么重启后就会出现ID重复的情况。 (2)AOF方式是针对每条写命令进行持久化,这样即使Redis挂掉也不会出现ID重复的情况。但是这样会有一个情况,由于incr命令的特殊性,它会导致Redis重启恢复数据的时间较长。
优点:
缺点:
RDB方式可能出现ID重复、AOF方式会导致Redis重启恢复数据的时间较长。雪花算法(Snowflake)模式 雪花算法(Snowflake)是twitter公司内部分布式项目采用的ID生成算法,各大公司在此基础上开发出了各具特色的分布式ID生成器。下图是雪花算法生成的Long类型ID的组成结构示意图: Snowflake 生成的是一个Long类型的ID,而一个Long类型占8个字节,每个字节又占8比特,即一个Long类型占64 个比特。
该Long类型的ID由64个比特组成,结构如下:正数位(占1比特)+ 时间戳(占41比特)+ 机器ID(占5比特)+ 数据中心(占5比特)+ 自增值(占12比特)。
第一个bit位(1bit):Java中Long的最高位是符号位,代表正负,其中正数是0,负数是1,一般生成ID都为正数,所以默认是0。
时间戳部分(41bit):毫秒级的时间,不建议存当前时间戳,而是用(当前时间戳 - 固定开始时间戳)的差值,这样可使产生的ID从更小的值开始。41位的时间戳可以使用69年,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年。
工作机器id(10bit):也被叫做 workId ,这个可灵活配置,一般是机器ID+数据中心ID组成。
序列号部分(12bit):自增值支持同一毫秒时间内,同一个节点可生成4096个ID。
根据雪花算法的逻辑,开发者只需使用Java语言将其实现出来,并封装为一个工具方法,然后在各个业务系统中直接使用该工具方法来获取分布式ID,此时只需保证每个业务都有自己的工作机器id,不需要单独搭建一个获取分布式ID的系统。
下面是使用Java实现的雪花算法代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 /** * Twitter的SnowFlake算法,使用SnowFlake算法生成一个整数,然后转化为62进制变成一个短地址URL * * https://github.com/beyondfengyu/SnowFlake */ public class SnowFlakeShortUrl { /** * 起始的时间戳 */ private final static long START_TIMESTAMP = 1480166465631L; /** * 每一部分占用的位数 */ //序列号占用的位数 private final static long SEQUENCE_BIT = 12; //数据中心占用的位数 private final static long DATA_CENTER_BIT = 5; //机器标识占用的位数 private final static long MACHINE_BIT = 5; /** * 每一部分的最大值 */ //序列号的最大值 private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT); //数据中心的最大值 private final static long MAX_DATA_CENTER_NUM = -1L ^ (-1L << DATA_CENTER_BIT); //机器标识的最大值 private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT); /** * 每一部分向左的位移 */ //机器标识向左的位移 private final static long MACHINE_LEFT = SEQUENCE_BIT; //数据中心向左的位移 private final static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; //时间戳向左的位移 private final static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT; //数据中心 private long dataCenterId; //机器标识 private long machineId; //序列号 private long sequence = 0L; //上一次时间戳 private long lastTimeStamp = -1L; private long getNextMill() { long mill = getNewTimeStamp(); while (mill <= lastTimeStamp) { mill = getNewTimeStamp(); } return mill; } private long getNewTimeStamp() { return System.currentTimeMillis(); } /** * 根据指定的数据中心ID和机器标志ID生成指定的序列号 * * @param dataCenterId 数据中心ID * @param machineId 机器标志ID */ public SnowFlakeShortUrl(long dataCenterId, long machineId) { if (dataCenterId > MAX_DATA_CENTER_NUM || dataCenterId < 0) { throw new IllegalArgumentException("DtaCenterId can't be greater than MAX_DATA_CENTER_NUM or less than 0!"); } if (machineId > MAX_MACHINE_NUM || machineId < 0) { throw new IllegalArgumentException("MachineId can't be greater than MAX_MACHINE_NUM or less than 0!"); } this.dataCenterId = dataCenterId; this.machineId = machineId; } /** * 生成下一个ID */ public synchronized long nextId() { long currTimeStamp = getNewTimeStamp(); if (currTimeStamp < lastTimeStamp) { throw new RuntimeException("Clock moved backwards. Refusing to generate id"); } if (currTimeStamp == lastTimeStamp) { //相同毫秒内,序列号自增 sequence = (sequence + 1) & MAX_SEQUENCE; //同一毫秒的序列数已经达到最大 if (sequence == 0L) { currTimeStamp = getNextMill(); } } else { //不同毫秒内,序列号置为0 sequence = 0L; } lastTimeStamp = currTimeStamp; return (currTimeStamp - START_TIMESTAMP) << TIMESTAMP_LEFT //时间戳部分 | dataCenterId << DATA_CENTER_LEFT //数据中心部分 | machineId << MACHINE_LEFT //机器标识部分 | sequence; //序列号部分 } public static void main(String[] args) { SnowFlakeShortUrl snowFlake = new SnowFlakeShortUrl(2, 3); for (int i = 0; i < (1 << 4); i++) { //10进制 System.out.println(snowFlake.nextId()); } } }
Uidgenerator (百度) uid-generator 是百度基于 Snowflake 算法实现的,项目 GitHub地址 。与原始的 snowflake 算法不同,uid-generator 支持 自定义时间戳、工作机器ID 和 序列号 等各部分的位数,且 uid-generator 中采用用户自定义 workId 的生成策略。
uid-generator 需要与数据库配合来使用,数据库中需要新建一个名为 WORKER_NODE 的表。当系统启动时,会往数据库表中插入一条数据,插入成功后会返回自增的ID,该ID就是该机器的 workId 数据。workId 由host和port组成。
请注意,uid-generator ID的组成结构与原始的Snowflake 算法生成的ID不同,uid-generator ID的workId占用22个bit位,而时间占用28个bit位,序列化占用13个bit位,还有它的时间单位是秒,不是毫秒。同时workId 也不一样,且同一应用每次重启就会消费一个 workId 。关于 Uidgenerator中文说明文档可以点击 GitHub地址 进行阅读。
TinyID(滴滴) TinyID是滴滴基于号段模式原理开发的,项目 GitHub地址 。每个服务获取一个号段,如(1000,2000]、(2000,3000]、(3000,4000],之后各服务使用号段进行业务调用: Tinyid 提供两种方式来进行接入:http 和 tinyid-client,下面将分别进行学习。
http方式接入 第一步,使用如下命令来下载tinyid的源码:
1 git clone https://github.com/didi/tinyid.git
第二步,创建对应的数据库表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 CREATE TABLE `tiny_id_info` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键', `biz_type` varchar(63) NOT NULL DEFAULT '' COMMENT '业务类型,唯一', `begin_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '开始id,仅记录初始值,无其他含义。初始化时begin_id和max_id应相同', `max_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '当前最大id', `step` int(11) DEFAULT '0' COMMENT '步长', `delta` int(11) NOT NULL DEFAULT '1' COMMENT '每次id增量', `remainder` int(11) NOT NULL DEFAULT '0' COMMENT '余数', `create_time` timestamp NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '创建时间', `update_time` timestamp NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '更新时间', `version` bigint(20) NOT NULL DEFAULT '0' COMMENT '版本号', PRIMARY KEY (`id`), UNIQUE KEY `uniq_biz_type` (`biz_type`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='id信息表'; CREATE TABLE `tiny_id_token` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id', `token` varchar(255) NOT NULL DEFAULT '' COMMENT 'token', `biz_type` varchar(63) NOT NULL DEFAULT '' COMMENT '此token可访问的业务类型标识', `remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注', `create_time` timestamp NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '创建时间', `update_time` timestamp NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '更新时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='token信息表'; INSERT INTO `tiny_id_info` (`id`, `biz_type`, `begin_id`, `max_id`, `step`, `delta`, `remainder`, `create_time`,`update_time`, `version`) VALUES (1, 'test', 1, 1, 100000, 1, 0, '2018-07-21 23:52:58', '2018-07-22 23:19:27', 1); INSERT INTO `tiny_id_info` (`id`, `biz_type`, `begin_id`, `max_id`, `step`, `delta`, `remainder`, `create_time`,`update_time`, `version`) VALUES (2, 'test_odd', 1, 1, 100000, 2, 1, '2018-07-21 23:52:58', '2018-07-23 00:39:24', 3); INSERT INTO `tiny_id_token` (`id`, `token`, `biz_type`, `remark`, `create_time`, `update_time`) VALUES (1, '0f673adf80504e2eaa552f5d791b644c', 'test', '1', '2017-12-14 16:36:46', '2017-12-14 16:36:48'); INSERT INTO `tiny_id_token` (`id`, `token`, `biz_type`, `remark`, `create_time`, `update_time`) VALUES (2, '0f673adf80504e2eaa552f5d791b644c', 'test_odd', '1', '2017-12-14 16:36:46', '2017-12-14 16:36:48');
第三步,配置tinyid-server项目offline包下的application.properties配置文件为如下所示:
1 2 3 4 5 server.port=8086 datasource.tinyid.primary.driver-class-name=com.mysql.cj.jdbc.Driver datasource.tinyid.primary.url=jdbc:mysql://localhost:3306/sequence_id?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8 datasource.tinyid.primary.username=root datasource.tinyid.primary.password=root
注意笔者使用的MySQL版本为8系列。 第四步,启动tinyid-server项目,开始进行测试。访问如下链接,来获取单个的ID:
1 http://localhost:8086/tinyid/id/nextIdSimple?bizType=test&token=0f673adf80504e2eaa552f5d791b644c
返回结果为:2。
如果要批量获取ID,可使用如下链接:
1 http://localhost:8086/tinyid/id/nextIdSimple?bizType=test&token=0f673adf80504e2eaa552f5d791b644c&batchSize=3
返回结果为:3,4,5。
Java客户端方式接入 第一步,创建对应的数据库表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 CREATE TABLE `tiny_id_info` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键', `biz_type` varchar(63) NOT NULL DEFAULT '' COMMENT '业务类型,唯一', `begin_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '开始id,仅记录初始值,无其他含义。初始化时begin_id和max_id应相同', `max_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '当前最大id', `step` int(11) DEFAULT '0' COMMENT '步长', `delta` int(11) NOT NULL DEFAULT '1' COMMENT '每次id增量', `remainder` int(11) NOT NULL DEFAULT '0' COMMENT '余数', `create_time` timestamp NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '创建时间', `update_time` timestamp NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '更新时间', `version` bigint(20) NOT NULL DEFAULT '0' COMMENT '版本号', PRIMARY KEY (`id`), UNIQUE KEY `uniq_biz_type` (`biz_type`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='id信息表'; CREATE TABLE `tiny_id_token` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id', `token` varchar(255) NOT NULL DEFAULT '' COMMENT 'token', `biz_type` varchar(63) NOT NULL DEFAULT '' COMMENT '此token可访问的业务类型标识', `remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注', `create_time` timestamp NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '创建时间', `update_time` timestamp NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '更新时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='token信息表'; INSERT INTO `tiny_id_info` (`id`, `biz_type`, `begin_id`, `max_id`, `step`, `delta`, `remainder`, `create_time`,`update_time`, `version`) VALUES (1, 'test', 1, 1, 100000, 1, 0, '2018-07-21 23:52:58', '2018-07-22 23:19:27', 1); INSERT INTO `tiny_id_info` (`id`, `biz_type`, `begin_id`, `max_id`, `step`, `delta`, `remainder`, `create_time`,`update_time`, `version`) VALUES (2, 'test_odd', 1, 1, 100000, 2, 1, '2018-07-21 23:52:58', '2018-07-23 00:39:24', 3); INSERT INTO `tiny_id_token` (`id`, `token`, `biz_type`, `remark`, `create_time`, `update_time`) VALUES (1, '0f673adf80504e2eaa552f5d791b644c', 'test', '1', '2017-12-14 16:36:46', '2017-12-14 16:36:48'); INSERT INTO `tiny_id_token` (`id`, `token`, `biz_type`, `remark`, `create_time`, `update_time`) VALUES (2, '0f673adf80504e2eaa552f5d791b644c', 'test_odd', '1', '2017-12-14 16:36:46', '2017-12-14 16:36:48');
第二步,配置tinyid-server项目offline包下的application.properties配置文件为如下所示:
1 2 3 4 5 server.port=8086 datasource.tinyid.primary.driver-class-name=com.mysql.cj.jdbc.Driver datasource.tinyid.primary.url=jdbc:mysql://localhost:3306/sequence_id?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8 datasource.tinyid.primary.username=root datasource.tinyid.primary.password=root
第三步,创建一个项目,然后在POM文件中新增如下依赖:
1 2 3 4 5 <dependency> <groupId>com.xiaoju.uemc.tinyid</groupId> <artifactId>tinyid-client</artifactId> <version>${tinyid.version}</version> </dependency>
第四步,通过阅读源码可知,需要新建一个名为 tinyid_client.properties 的配置文件,然后往里面新增如下配置信息,后面两个配置项可以不配置:
1 2 3 4 tinyid.token=0f673adf80504e2eaa552f5d791b644c tinyid.server=localhost:8086 tinyid.readTimeout= tinyid.connectTimeout=
第五步,启动项目,开始进行测试。通过对源码分析,可以看到实际上我们只需通过 TinyId 这个工具类提供的方法就能获取到生成的ID: 如果你想获取单个的ID,可调用TinyId的 nextId(String bizType) 方法:
返回结果为:2。
如果想要批量获取ID,可调用TinyId的 nextId(String bizType, Integer batchSize) 方法:
1 TinyId.nextId("test",3);
返回结果为:3,4,5。请注意,上面的bizType和token必须对应起来,否则数据是无法查询出来的。
Leaf(美团) Leaf 由美团开发,同时支持号段模式和 snowflake 算法模式(可切换使用)的分布式ID生成器,项目 GitHub地址 。
号段模式接入 第一步,使用如下命令来下载 Leaf 的源码:
1 git clone https://github.com/Meituan-Dianping/Leaf
第二步,创建对应的数据库表:
1 2 3 4 5 6 7 8 9 10 DROP TABLE IF EXISTS `leaf_alloc`; CREATE TABLE `leaf_alloc` ( `biz_tag` varchar(128) NOT NULL DEFAULT '' COMMENT '业务标签,唯一', `max_id` bigint(20) NOT NULL DEFAULT '1' COMMENT '当前最大id', `step` int(11) NOT NULL COMMENT '步长', `description` varchar(256) DEFAULT NULL COMMENT '描述信息', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', PRIMARY KEY (`biz_tag`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='号段分配表';
第三步,修改项目的一些依赖版本,具体如下: (1)mysql-connector-java,由 5.1.38 调整为 8.0.33; (2)druid,由 1.0.18 调整为 1.1.10。 第四步,修改 leaf-server 项目中的leaf.properties文件内容,我们这里使用号段模式,配置信息如下:
1 2 3 4 5 6 7 leaf.name=com.sankuai.leaf.opensource.test leaf.segment.enable=true leaf.jdbc.url=jdbc:mysql://localhost:3306/sequence_id?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8 leaf.jdbc.username=root leaf.jdbc.password=root leaf.snowflake.enable=false
既然我们这里使用了号段模式,因此需要关闭雪花算法模式。 第五步,启动 leaf-server 项目的入口类 LeafServerApplication,即可启动项目。然后往数据表leaf_alloc中插入一条数据,如下所示: 接着在浏览器地址栏中访问[http://localhost:8080/api/segment/get/test_order](http://localhost:8080/api/segment/get/test_order)
链接,即可获取到生成的ID。 实际上,Leaf还提供了一个接口[http://localhost:8080/cache](http://localhost:8080/cache)
来监控号段模式,界面如下所示:
雪花算法模式接入 Leaf和原始的雪花算法主要区别在 workId 的生成上。我们知道 Leaf 的雪花算法模式依赖ZooKeeper,它的 workId 是基于ZooKeeper的顺序Id来生成的。每个应用在使用 Leaf 的雪花模式接入时,在启动的时候,都会在Zookeeper中生成一个顺序Id,就相当于一台机器对应一个顺序节点,也就是一个workId。
前面三步和之前的一样,第四步配置信息有点差异,如下所示:
1 2 3 4 5 leaf.segment.enable=false leaf.snowflake.enable=true leaf.snowflake.zk.address=127.0.0.1 leaf.snowflake.port=2187
之后启动 leaf-server 项目的入口类 LeafServerApplication,即可启动项目。然后在浏览器地址栏中访问[http://localhost:8080/api/snowflake/get/test_order](http://localhost:8080/api/segment/get/test_order)
链接,即可获取到生成的ID。