0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

滴滴的分布式ID生成器,好用的一批!

jf_ro2CN3Fa 来源:芋道源码 2023-09-14 15:42 次阅读


Tinyid是滴滴开发的一款分布式ID系统,Tinyid是在美团(Leaf)leaf-segment算法基础上升级而来,不仅支持了数据库多主节点模式,还提供了tinyid-client客户端的接入方式,使用起来更加方便。但和美团(Leaf)不同的是,Tinyid只支持号段一种模式不支持雪花模式。

Tinyid的特性

  • 全局唯一的long型ID
  • 趋势递增的id
  • 提供 http 和 java-client 方式接入
  • 支持批量获取ID
  • 支持生成1,3,5,7,9...序列的ID
  • 支持多个db的配置

适用场景 :只关心ID是数字,趋势递增的系统,可以容忍ID不连续,可以容忍ID的浪费

不适用场景 :像类似于订单ID的业务,因生成的ID大部分是连续的,容易被扫库、或者推算出订单量等信息

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

Tinyid原理

Tinyid是基于号段模式实现,再简单啰嗦一下号段模式的原理:就是从数据库批量的获取自增ID,每次从数据库取出一个号段范围,例如 (1,1000] 代表1000个ID,业务服务将号段在本地生成1~1000的自增ID并加载到内存.。

Tinyid会将可用号段加载到内存中,并在内存中生成ID,可用号段在首次获取ID时加载,如当前号段使用达到一定比例时,系统会异步的去加载下一个可用号段,以此保证内存中始终有可用号段,以便在发号服务宕机后一段时间内还有可用ID。

原理图大致如下图:

dcb852c4-52c5-11ee-a25d-92fbcf53809c.pngTinyid原理图

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

Tinyid实现

Tinyid的GitHub地址 :https://github.com/didi/tinyid.git

Tinyid提供了两种调用方式,一种基于Tinyid-server提供的http方式,另一种Tinyid-client客户端方式。不管使用哪种方式调用,搭建Tinyid都必须提前建表tiny_id_infotiny_id_token

CREATETABLE`tiny_id_info`(
`id`bigint(20)unsignedNOTNULLAUTO_INCREMENTCOMMENT'自增主键',
`biz_type`varchar(63)NOTNULLDEFAULT''COMMENT'业务类型,唯一',
`begin_id`bigint(20)NOTNULLDEFAULT'0'COMMENT'开始id,仅记录初始值,无其他含义。初始化时begin_id和max_id应相同',
`max_id`bigint(20)NOTNULLDEFAULT'0'COMMENT'当前最大id',
`step`int(11)DEFAULT'0'COMMENT'步长',
`delta`int(11)NOTNULLDEFAULT'1'COMMENT'每次id增量',
`remainder`int(11)NOTNULLDEFAULT'0'COMMENT'余数',
`create_time`timestampNOTNULLDEFAULT'2010-01-010000'COMMENT'创建时间',
`update_time`timestampNOTNULLDEFAULT'2010-01-010000'COMMENT'更新时间',
`version`bigint(20)NOTNULLDEFAULT'0'COMMENT'版本号',
PRIMARYKEY(`id`),
UNIQUEKEY`uniq_biz_type`(`biz_type`)
)ENGINE=InnoDBAUTO_INCREMENT=1DEFAULTCHARSET=utf8COMMENT'id信息表';

CREATETABLE`tiny_id_token`(
`id`int(11)unsignedNOTNULLAUTO_INCREMENTCOMMENT'自增id',
`token`varchar(255)NOTNULLDEFAULT''COMMENT'token',
`biz_type`varchar(63)NOTNULLDEFAULT''COMMENT'此token可访问的业务类型标识',
`remark`varchar(255)NOTNULLDEFAULT''COMMENT'备注',
`create_time`timestampNOTNULLDEFAULT'2010-01-010000'COMMENT'创建时间',
`update_time`timestampNOTNULLDEFAULT'2010-01-010000'COMMENT'更新时间',
PRIMARYKEY(`id`)
)ENGINE=InnoDBAUTO_INCREMENT=1DEFAULTCHARSET=utf8COMMENT'token信息表';

INSERTINTO`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-212358','2018-07-222327',1);

INSERTINTO`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-212358','2018-07-230024',3);


INSERTINTO`tiny_id_token`(`id`,`token`,`biz_type`,`remark`,`create_time`,`update_time`)
VALUES
(1,'0f673adf80504e2eaa552f5d791b644c','test','1','2017-12-141646','2017-12-141648');

INSERTINTO`tiny_id_token`(`id`,`token`,`biz_type`,`remark`,`create_time`,`update_time`)
VALUES
(2,'0f673adf80504e2eaa552f5d791b644c','test_odd','1','2017-12-141646','2017-12-141648');

tiny_id_info表是具体业务方号段信息数据表

dcff08cc-52c5-11ee-a25d-92fbcf53809c.png

max_id :号段的最大值

step:步长,即为号段的长度

biz_type:业务类型

号段获取对max_id字段做一次update操作,update max_id= max_id + step,更新成功则说明新号段获取成功,新的号段范围是(max_id ,max_id +step]

tiny_id_token是一个权限表,表示当前token可以操作哪些业务的号段信息。

dd1d31ee-52c5-11ee-a25d-92fbcf53809c.png

修改tinyid-serverofflineapplication.properties 文件配置数据库,由于tinyid支持数据库多master模式,可以配置多个数据库信息。启动 TinyIdServerApplication 测试一下。

datasource.tinyid.primary.driver-class-name=com.mysql.jdbc.Driver
datasource.tinyid.primary.url=jdbc//127.0.0.1:3306/xin-master?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8
datasource.tinyid.primary.username=junkang
datasource.tinyid.primary.password=junkang
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//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

1、Http方式

tinyid内部一共提供了四个http接口来获取ID和号段。

packagecom.xiaoju.uemc.tinyid.server.controller;

/**
*@authordu_imba
*/
@RestController
@RequestMapping("/id/")
publicclassIdContronller{

privatestaticfinalLoggerlogger=LoggerFactory.getLogger(IdContronller.class);
@Autowired
privateIdGeneratorFactoryServeridGeneratorFactoryServer;
@Autowired
privateSegmentIdServicesegmentIdService;
@Autowired
privateTinyIdTokenServicetinyIdTokenService;
@Value("${batch.size.max}")
privateIntegerbatchSizeMax;

@RequestMapping("nextId")
publicResponse>nextId(StringbizType,IntegerbatchSize,Stringtoken){
Response>response=newResponse<>();
try{
IdGeneratoridGenerator=idGeneratorFactoryServer.getIdGenerator(bizType);
Listids=idGenerator.nextId(newBatchSize);
response.setData(ids);
}catch(Exceptione){
response.setCode(ErrorCode.SYS_ERR.getCode());
response.setMessage(e.getMessage());
logger.error("nextIderror",e);
}
returnresponse;
}



@RequestMapping("nextIdSimple")
publicStringnextIdSimple(StringbizType,IntegerbatchSize,Stringtoken){
Stringresponse="";
try{
IdGeneratoridGenerator=idGeneratorFactoryServer.getIdGenerator(bizType);
if(newBatchSize==1){
Longid=idGenerator.nextId();
response=id+"";
}else{
ListidList=idGenerator.nextId(newBatchSize);
StringBuildersb=newStringBuilder();
for(Longid:idList){
sb.append(id).append(",");
}
response=sb.deleteCharAt(sb.length()-1).toString();
}
}catch(Exceptione){
logger.error("nextIdSimpleerror",e);
}
returnresponse;
}

@RequestMapping("nextSegmentId")
publicResponsenextSegmentId(StringbizType,Stringtoken){
try{
SegmentIdsegmentId=segmentIdService.getNextSegmentId(bizType);
response.setData(segmentId);
}catch(Exceptione){
response.setCode(ErrorCode.SYS_ERR.getCode());
response.setMessage(e.getMessage());
logger.error("nextSegmentIderror",e);
}
returnresponse;
}

@RequestMapping("nextSegmentIdSimple")
publicStringnextSegmentIdSimple(StringbizType,Stringtoken){
Stringresponse="";
try{
SegmentIdsegmentId=segmentIdService.getNextSegmentId(bizType);
response=segmentId.getCurrentId()+","+segmentId.getLoadingId()+","+segmentId.getMaxId()
+","+segmentId.getDelta()+","+segmentId.getRemainder();
}catch(Exceptione){
logger.error("nextSegmentIdSimpleerror",e);
}
returnresponse;
}

}

nextIdnextIdSimple都是获取下一个ID,nextSegmentIdSimplegetNextSegmentId是获取下一个可用号段。区别在于接口是否有返回状态。

nextId:
'http://localhost:9999/tinyid/id/nextId?bizType=test&token=0f673adf80504e2eaa552f5d791b644c'
response :
{
"data":[2],
"code":200,
"message":""
}

nextIdSimple:
'http://localhost:9999/tinyid/id/nextIdSimple?bizType=test&token=0f673adf80504e2eaa552f5d791b644c'
response:3
dd35e23e-52c5-11ee-a25d-92fbcf53809c.pngdd56e434-52c5-11ee-a25d-92fbcf53809c.png

2、Tinyid-client客户端

如果不想通过http方式,Tinyid-client客户端也是一种不错的选择。

引用 tinyid-server


com.xiaoju.uemc.tinyid
tinyid-client
${tinyid.version}

启动 tinyid-server项目打包后得到 tinyid-server-0.1.0-SNAPSHOT.jar ,设置版本 ${tinyid.version}为0.1.0-SNAPSHOT。

在我们的项目 application.properties 中配置 tinyid-server服务的请求地址 和 用户身份token

tinyid.server=127.0.0.1:9999
tinyid.token=0f673adf80504e2eaa552f5d791b644c```

在Java代码调用TinyId也很简单,只需要一行代码。

//根据业务类型获取单个ID
Longid=TinyId.nextId("test");

//根据业务类型批量获取10个ID
Listids=TinyId.nextId("test",10);

Tinyid整个项目的源码实现也是比较简单,像与数据库交互更直接用jdbcTemplate实现

@Override
publicTinyIdInfoqueryByBizType(StringbizType){
Stringsql="selectid,biz_type,begin_id,max_id,"+
"step,delta,remainder,create_time,update_time,version"+
"fromtiny_id_infowherebiz_type=?";
Listlist=jdbcTemplate.query(sql,newObject[]{bizType},newTinyIdInfoRowMapper());
if(list==null||list.isEmpty()){
returnnull;
}
returnlist.get(0);
}

总结

两种方式推荐使用Tinyid-client,这种方式ID为本地生成,号段长度(step)越长,支持的qps就越大,如果将号段设置足够大,则qps可达1000w+。而且tinyid-clienttinyid-server 访问变的低频,减轻了server端的压力。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据库
    +关注

    关注

    7

    文章

    3591

    浏览量

    63374
  • 生成器
    +关注

    关注

    7

    文章

    302

    浏览量

    20220
  • 客户端
    +关注

    关注

    1

    文章

    282

    浏览量

    16343

原文标题:滴滴的分布式ID生成器,好用的一批!

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    不同雷达目标生成器的架构及目标生成器的设计要求和准则

    雷达目标生成器的性能和能力以及它们测试雷达系统的可用性是关键,这主要取决于几个技术参数。本文介绍不同雷达目标生成器的架构,阐明适合雷达系统性能测试的目标生成器的设计要求和准则,同时给出测量结果举例。
    发表于 03-29 11:40 1542次阅读

    Minitab 交互表格生成器

    生成器
    MinitabUG
    发布于 :2024年04月03日 15:58:54

    应用程序生成器

    我在生成应用程序的时候,右键点击程序生成规范,点击新建,只有源代码发布和web两项,没有应用程序,安装程序以及DLL和ZIP等,是因为我没有应用程序生成器的问题吗?那去哪可以整个免费
    发表于 09-04 09:19

    ID生成器的snowflake

    那些惊艳的算法们(四)——唯ID生成器snowflake
    发表于 06-12 17:22

    如何去使用生成器

    生成器的工作原理是什么?如何去使用生成器呢?
    发表于 10-25 08:44

    python生成器

    python生成器1. 什么是生成器生成器(英文名 Generator ),是个可以像迭代器那样使用for循环来获取元素的函数。生成器
    发表于 02-24 15:56

    pim卡资料生成器

    pim卡资料生成器
    发表于 11-22 23:23 6次下载

    展频时脉生成器

    展频时脉生成器展频时脉的技术是频率调变( FM )的一种应用,相反地,频率调变通常会伴随生成展频的效果。展频时脉最基本的想法,是稍微地调变时脉讯号的频率,造成时脉讯
    发表于 02-26 11:05 23次下载

    自制酸奶生成器

    自制酸奶生成器
    发表于 04-23 11:48 879次阅读
    自制酸奶<b class='flag-5'>生成器</b>

    LED段码生成器

    本文提供的LED段码生成器,希望对你的学习有所帮助!
    发表于 06-03 15:19 96次下载

    代码生成器的应用

    jeesite框架代码生成器,可以很方便的生成代码,挺不错的。
    发表于 01-14 15:19 0次下载

    FPM_0.080_ALLEGRO_封装生成器

    ALLEGRO PCB 封装生成器好用的东东
    发表于 02-19 14:38 0次下载

    python生成器是什么

    python生成器 1. 什么是生成器生成器(英文名 Generator ),是一个可以像迭代器那样使用for循环来获取元素的函数。 生成器的出现(Python 2.2 +),实现
    的头像 发表于 02-24 15:53 3086次阅读

    Arduino赞美生成器

    电子发烧友网站提供《Arduino赞美生成器.zip》资料免费下载
    发表于 11-09 14:22 1次下载
    Arduino赞美<b class='flag-5'>生成器</b>

    通用RFID生成器

    通用RFID生成器资料分享
    发表于 02-10 15:35 1次下载