11. Leaf-segment 分布式ID

Spring Cloud 微服务系列文章,点击上方合集↑

1. 开头

当应用程序只使用单个数据库时,可以使用数据库自增的方式来生成id,这种方式既简单,查询又快。

然而,当应用程序需要进行分库分表时,即将数据分散到多个数据库和数据表中,使用数据库自增的方式会导致id在不同表中重复,那么就需要使用分布式id来确保不同表中id的唯一性。

分布式id通常有以下几种方式:

  • UUID
  • snowflake 雪花算法
  • 数据库生成(本文的方式)

关于分布式id的几种方式具体可以看美团技术的这篇文档:https://tech.meituan.com/2017/04/21/mt-leaf.html

2. Leaf-segment 数据库方式

这里用美团的 Leaf 数据库号段模式 进行了改造。

美团的 Leaf 号段模式源码地址:https://github.com/Meituan-Dianping/Leaf

选择此种方式的原因有如下:

  • 生成的 id 是连续递增的
  • 对于数据库索引更快
  • 使用更简单

为什么不用雪花算法?雪花算法有个时钟回拨的问题,为了解决这个问题还需要引入redis或zk。同样都是需要借助中间件,那就直接用数据库更方便。

为什么不用uuid?uuid是字符串查询速度慢,而且阅读不友好。

3. 原理分析

使用数据库来生成分布式id相当于一个一个发放编号,需要不断与数据库交互,速度较慢。而美团的号段方式则可以批量获取一段可用编号,避免了频繁的数据库交互,速度更快。可以类比为领取学号,一个一个领需要排队等待,批量发放可以省去排队的时间。

数据库号段模式生成 id 的具体原理如下:

  1. Leaf 服务器从数据库加载当前的号段信息,包括起始值和结束值。

  2. 当有业务系统需要生成 id 时,向 Leaf 服务器发送请求。

  3. Leaf 服务器根据请求判断是否需要重新加载号段。

  4. 如果当前号段已经用完,Leaf 服务器从数据库获取下一个号段。

  5. 获取到新的号段后,Leaf 服务器将其存储在内存,并更新数据库中的当前值。

  6. 返回给业务系统的 id 是号段中的连续整数序列。

通过这种方式,Leaf 可以快速生成一批连续的唯一 id,避免频繁与数据库交互,并保证生成的 id 是唯一的。同时,通过加载新的号段,Leaf 可以满足大规模、高并发的分布式 id 生成需求。

4. 使用

4.1 执行sql

生成leaf_alloc表。

CREATE TABLE `leaf_alloc`  (
  `biz_tag` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  `max_id` bigint(20) NOT NULL DEFAULT 1,
  `step` int(11) NOT NULL,
  `description` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `update_time` timestamp(0) NOT NULL DEFAULT current_timestamp(0) ON UPDATE CURRENT_TIMESTAMP(0),
  PRIMARY KEY (`biz_tag`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of leaf_alloc
-- ----------------------------
INSERT INTO `leaf_alloc` VALUES ('test', 1, 2000, '测试', '2022-11-14 17:18:12');

SET FOREIGN_KEY_CHECKS = 1;

各个业务不同的发号需求用biz_tag字段来区分,每个biz_tag的ID获取相互隔离,互不影响。

4.2 启动 id-service 服务

这里将生成分布式id做成了一个服务id-service,相关源码在仓库的这个目录下:/sourcecode/spring-cloud-demo/id-service

11. Leaf-segment 分布式ID_第1张图片

可以直接通过调用接口来测试获取生成的id。

接口地址:http://localhost:8070/id/generate?key=test

注意:对于不同的业务表,在leaf_alloc数据表使用不同的biz_tag,比如商品表就在leaf_alloc表中插入biz_tagproduct的一条记录,获取id传入参数就是key=product

4.3 服务调用

其它业务模块通过服务调用的方式获取生成的id。

@FeignClient(name = "id-service")
public interface IdService {

    @GetMapping("/id/generate")
    Long generateId(@RequestParam String key);
}
  • 注意不同的业务表传入不同的key。

5. 结语

本文通过Leaf-segment数据库号段的方式生成分布式id,这种方式可以让生成的id从0开始逐一递增,但是需要维护一个leaf_alloc数据表,对于新的业务表需要在leaf_alloc中插入一条新的记录。

有多少张业务表就需要在leaf_alloc表中插入多少条记录。

我们将生成分布式id做成了一个服务id-service,后面分库分表等其它服务会调用id-service这个服务来生成id。


11. Leaf-segment 分布式ID_第2张图片

Spring Cloud 微服务系列 完整的代码在仓库的sourcecode/spring-cloud-demo目录下。

gitee(推荐):https://gitee.com/cunzaizhe/xiaohuge-blog

github:https://github.com/tigerleeli/xiaohuge-blog

关注微信公众号:“小虎哥的技术博客”,让我们一起成为更优秀的程序员❤️!

你可能感兴趣的:(Spring,Cloud,微服务系列,spring,cloud,微服务,分布式id)