Spring Cloud 微服务系列文章,点击上方合集↑
当应用程序只使用单个数据库时,可以使用数据库自增的方式来生成id,这种方式既简单,查询又快。
然而,当应用程序需要进行分库分表时,即将数据分散到多个数据库和数据表中,使用数据库自增的方式会导致id在不同表中重复,那么就需要使用分布式id来确保不同表中id的唯一性。
分布式id通常有以下几种方式:
关于分布式id的几种方式具体可以看美团技术的这篇文档:https://tech.meituan.com/2017/04/21/mt-leaf.html
这里用美团的 Leaf 数据库号段模式 进行了改造。
美团的 Leaf 号段模式源码地址:https://github.com/Meituan-Dianping/Leaf
选择此种方式的原因有如下:
为什么不用雪花算法?雪花算法有个时钟回拨的问题,为了解决这个问题还需要引入redis或zk。同样都是需要借助中间件,那就直接用数据库更方便。
为什么不用uuid?uuid是字符串查询速度慢,而且阅读不友好。
使用数据库来生成分布式id相当于一个一个发放编号,需要不断与数据库交互,速度较慢。而美团的号段方式则可以批量获取一段可用编号,避免了频繁的数据库交互,速度更快。可以类比为领取学号,一个一个领需要排队等待,批量发放可以省去排队的时间。
数据库号段模式生成 id 的具体原理如下:
Leaf 服务器从数据库加载当前的号段信息,包括起始值和结束值。
当有业务系统需要生成 id 时,向 Leaf 服务器发送请求。
Leaf 服务器根据请求判断是否需要重新加载号段。
如果当前号段已经用完,Leaf 服务器从数据库获取下一个号段。
获取到新的号段后,Leaf 服务器将其存储在内存,并更新数据库中的当前值。
返回给业务系统的 id 是号段中的连续整数序列。
通过这种方式,Leaf 可以快速生成一批连续的唯一 id,避免频繁与数据库交互,并保证生成的 id 是唯一的。同时,通过加载新的号段,Leaf 可以满足大规模、高并发的分布式 id 生成需求。
生成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获取相互隔离,互不影响。
这里将生成分布式id做成了一个服务id-service
,相关源码在仓库的这个目录下:/sourcecode/spring-cloud-demo/id-service
可以直接通过调用接口来测试获取生成的id。
接口地址:http://localhost:8070/id/generate?key=test
注意:对于不同的业务表,在leaf_alloc
数据表使用不同的biz_tag
,比如商品表就在leaf_alloc
表中插入biz_tag
为product
的一条记录,获取id传入参数就是key=product
。
其它业务模块通过服务调用的方式获取生成的id。
@FeignClient(name = "id-service")
public interface IdService {
@GetMapping("/id/generate")
Long generateId(@RequestParam String key);
}
本文通过Leaf-segment数据库号段的方式生成分布式id,这种方式可以让生成的id从0开始逐一递增,但是需要维护一个leaf_alloc
数据表,对于新的业务表需要在leaf_alloc
中插入一条新的记录。
有多少张业务表就需要在leaf_alloc
表中插入多少条记录。
我们将生成分布式id做成了一个服务id-service
,后面分库分表等其它服务会调用id-service
这个服务来生成id。
Spring Cloud 微服务系列 完整的代码在仓库的sourcecode/spring-cloud-demo
目录下。
gitee(推荐):https://gitee.com/cunzaizhe/xiaohuge-blog
github:https://github.com/tigerleeli/xiaohuge-blog
关注微信公众号:“小虎哥的技术博客”,让我们一起成为更优秀的程序员❤️!