分布式ID生成方案
- UUID
- 数据库自增
- 号段模式
- Redis实现
- 雪花算法(SnowFlake)
- 百度Uidgenerator
- 美团Leaf
- 滴滴TinyID
本文重点介绍能够ID自增的Leaf和TinyID
号段模式
这种模式也是现在生成分布式ID的一种方法,实现思路是会从数据库获取一个号段范围,比如[1,1000],生成1到1000的自增ID加载到内存中,建表结构如:
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`)
)
biz_type :不同业务类型
max_id :当前最大的id
step :代表号段的步长
version :版本号,就像MVCC一样,可以理解为乐观锁
等ID都用了,再去数据库获取,然后更改最大值
update id_generator set max_id = #{max_id+step}, version = version + 1 where version = # {version} and biz_type = XXX
优点:有比较成熟的方案,像百度Uidgenerator,美团Leaf
缺点:依赖于数据库实现
美团Leaf
Leaf 提供两种生成的ID的方式:号段模式(Leaf-segment)和snowflake模式(Leaf-snowflake)。你可以同时开启两种方式,也可以指定开启某种方式,默认两种方式为关闭状态。
git位置
GitHub - Meituan-Dianping/Leaf: Distributed ID Generate Service
介绍文档
https://github.com/Meituan-Dianping/Leaf/blob/master/README_CN.md
创建数据表
CREATE DATABASE leaf
CREATE TABLE `leaf_alloc` (`biz_tag` varchar(128) NOT NULL DEFAULT '',`max_id` bigint(20) NOT NULL DEFAULT '1',`step` int(11) NOT NULL,`description` varchar(256) DEFAULT NULL,`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`biz_tag`)
) ENGINE=InnoDB;insert into leaf_alloc(biz_tag, max_id, step, description) values('leaf-segment-test', 1, 2000, 'Test leaf Segment Mode Get Id')
获取项目
git clone git@github.com:Meituan-Dianping/Leaf.git
升级mysql驱动
如果连接的是mysql1.8以上,需要升级mysql驱动,分别是
Leaf/poe.xml以及 leaf_core/poe.xml
# Leaf/poe.xml
<mybatis-spring.version>1.2.5</mybatis-spring.version>#leaf_core/poe.xml<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.25</version></dependency>
修改配置
下面是号段模式的配置
leaf_server/resouces/leaf.properties
leaf.name=com.sankuai.leaf.opensource.test
leaf.segment.enable=true
leaf.jdbc.url=jdbc:mysql://127.0.0.1:3306/leaf?useSSL=false
leaf.jdbc.username=root
leaf.jdbc.password=123456leaf.snowflake.enable=false
#leaf.snowflake.zk.address=
#leaf.snowflake.port=
打包
cd leaf
mvn clean install -DskipTests
运行
cd leaf-server
#mvn方式
mvn spring-boot:run
#脚本方式
sh deploy/run.sh
测试
#segment
curl http://localhost:8080/api/segment/get/leaf-segment-test
#snowflake
curl http://localhost:8080/api/snowflake/get/test
监控页面
号段模式:http://localhost:8080/cache#注意添加一个biz_type以后,10s以后生效。
滴滴TinyID
github位置
GitHub - didi/tinyid: ID Generator id生成器
介绍文档
Home · didi/tinyid Wiki · GitHub
获取项目
git clone https://github.com/didi/tinyid.git
创建数据表
tinyid-server/db.sql
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=1 DEFAULT CHARSET=utf8 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=1 DEFAULT CHARSET=utf8 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');
修改配置
cd tinyid-server/src/main/resources/offline
vi application.properties
server.port=9999
server.context-path=/tinyidbatch.size.max=100000#datasource.tinyid.names=primary
#如果希望数据库能够高可用,可以设置多个不同节点,两个节点上的数据保持一致。
#注意添加配置的时候,多个节点都要添加
datasource.tinyid.names=primary,secondary
datasource.tinyid.type=org.apache.tomcat.jdbc.pool.DataSourcedatasource.tinyid.primary.driver-class-name=com.mysql.jdbc.Driver
datasource.tinyid.primary.url=jdbc:mysql://localhost:3306/db1?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8
datasource.tinyid.primary.username=root
datasource.tinyid.primary.password=123456
#datasource.tinyid.primary.testOnBorrow=false
#datasource.tinyid.primary.maxActive=10datasource.tinyid.secondary.driver-class-name=com.mysql.jdbc.Driver
datasource.tinyid.secondary.url=jdbc:mysql://localhost:3306/db2?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8
datasource.tinyid.secondary.username=root
datasource.tinyid.secondary.password=123456
datasource.tinyid.secondary.testOnBorrow=false
datasource.tinyid.secondary.maxActive=10
打包
cd tinyid
mvn clean install -DskipTests
#或者
cd tinyid-server/
sh build.sh offline
运行
cd tinyid-server/
#sh build.sh offline
java -jar output/tinyid-server-xxx.jar
测试
nextId:
curl 'http://localhost:9999/tinyid/id/nextId?bizType=test&token=0f673adf80504e2eaa552f5d791b644c'
response:{"data":[2],"code":200,"message":""}nextId Simple:
curl 'http://localhost:9999/tinyid/id/nextIdSimple?bizType=test&token=0f673adf80504e2eaa552f5d791b644c'
response: 3with batchSize:
curl 'http://localhost:9999/tinyid/id/nextIdSimple?bizType=test&token=0f673adf80504e2eaa552f5d791b644c&batchSize=10'
response: 4,5,6,7,8,9,10,11,12,13Get nextId like 1,3,5,7,9...
bizType=test_odd : delta is 2 and remainder is 1
curl 'http://localhost:9999/tinyid/id/nextIdSimple?bizType=test_odd&batchSize=10&token=0f673adf80504e2eaa552f5d791b644c'
response: 3,5,7,9,11,13,15,17,19,21
客户端使用
客户端打包
cd tinyid
mvn clean install -DskipTests
#tinyid-client\target\tinyid-client-0.1.0-SNAPSHOT.jar就是可以使用的客户端
客户端配置文件
tinyid_client.properties
将tinyid_client.properties放在resouces下面
tinyid.server=localhost:9999
tinyid.token=0f673adf80504e2eaa552f5d791b644c
使用
public class ClientTest {@Testpublic void testNextId() {for (int i = 0; i < 100000; i++) {Long id = TinyId.nextId("test_odd");System.out.println("current id is: " + id);}}
}
注意:每次重启程序,id就会往前加一个step,不管你有没有用完,如果程序经常重启,step不宜设置过大。但是step不宜设置过小,否则与数据库交互过于频繁。
来源:
8种分布式ID生成方案汇总