ID生成方案总结

分布式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 [email protected]: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=123456

leaf.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=/tinyid

batch.size.max=100000

#datasource.tinyid.names=primary
#如果希望数据库能够高可用,可以设置多个不同节点,两个节点上的数据保持一致。
#注意添加配置的时候,多个节点都要添加
datasource.tinyid.names=primary,secondary
datasource.tinyid.type=org.apache.tomcat.jdbc.pool.DataSource

datasource.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=10

datasource.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: 3

with batchSize:
curl 'http://localhost:9999/tinyid/id/nextIdSimple?bizType=test&token=0f673adf80504e2eaa552f5d791b644c&batchSize=10'
response: 4,5,6,7,8,9,10,11,12,13

Get 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 {
    @Test
    public 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生成方案汇总 

猜你喜欢

转载自blog.csdn.net/csdncjh/article/details/132112737