Tinyid
是滴滴開發(fā)的一款分布式ID系統(tǒng),Tinyid
是在美團(tuán)(Leaf)
的leaf-segment
算法基礎(chǔ)上升級(jí)而來,不僅支持了數(shù)據(jù)庫(kù)多主節(jié)點(diǎn)模式,還提供了tinyid-client
客戶端的接入方式,使用起來更加方便。但和美團(tuán)(Leaf)不同的是,Tinyid只支持號(hào)段一種模式不支持雪花模式。
Tinyid的特性
- 全局唯一的long型ID
- 趨勢(shì)遞增的id
- 提供 http 和 java-client 方式接入
- 支持批量獲取ID
- 支持生成1,3,5,7,9...序列的ID
- 支持多個(gè)db的配置
適用場(chǎng)景 :只關(guān)心ID是數(shù)字,趨勢(shì)遞增的系統(tǒng),可以容忍ID不連續(xù),可以容忍ID的浪費(fèi)
不適用場(chǎng)景 :像類似于訂單ID的業(yè)務(wù),因生成的ID大部分是連續(xù)的,容易被掃庫(kù)、或者推算出訂單量等信息
基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
- 項(xiàng)目地址:https://github.com/YunaiV/ruoyi-vue-pro
- 視頻教程:https://doc.iocoder.cn/video/
Tinyid原理
Tinyid
是基于號(hào)段模式實(shí)現(xiàn),再簡(jiǎn)單啰嗦一下號(hào)段模式的原理:就是從數(shù)據(jù)庫(kù)批量的獲取自增ID,每次從數(shù)據(jù)庫(kù)取出一個(gè)號(hào)段范圍,例如 (1,1000]
代表1000個(gè)ID,業(yè)務(wù)服務(wù)將號(hào)段在本地生成1~1000
的自增ID并加載到內(nèi)存.。
Tinyid
會(huì)將可用號(hào)段加載到內(nèi)存中,并在內(nèi)存中生成ID,可用號(hào)段在首次獲取ID時(shí)加載,如當(dāng)前號(hào)段使用達(dá)到一定比例時(shí),系統(tǒng)會(huì)異步的去加載下一個(gè)可用號(hào)段,以此保證內(nèi)存中始終有可用號(hào)段,以便在發(fā)號(hào)服務(wù)宕機(jī)后一段時(shí)間內(nèi)還有可用ID。
原理圖大致如下圖:
![dcb852c4-52c5-11ee-a25d-92fbcf53809c.png](https://file1.elecfans.com/web2/M00/A2/F9/wKgZomUCui-APGzrAAIwdXCRoh0870.png)
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
Tinyid實(shí)現(xiàn)
Tinyid
的GitHub地址 :https://github.com/didi/tinyid.git
Tinyid
提供了兩種調(diào)用方式,一種基于Tinyid-server
提供的http方式,另一種Tinyid-client
客戶端方式。不管使用哪種方式調(diào)用,搭建Tinyid
都必須提前建表tiny_id_info
、tiny_id_token
。
CREATETABLE`tiny_id_info`(
`id`bigint(20)unsignedNOTNULLAUTO_INCREMENTCOMMENT'自增主鍵',
`biz_type`varchar(63)NOTNULLDEFAULT''COMMENT'業(yè)務(wù)類型,唯一',
`begin_id`bigint(20)NOTNULLDEFAULT'0'COMMENT'開始id,僅記錄初始值,無其他含義。初始化時(shí)begin_id和max_id應(yīng)相同',
`max_id`bigint(20)NOTNULLDEFAULT'0'COMMENT'當(dāng)前最大id',
`step`int(11)DEFAULT'0'COMMENT'步長(zhǎng)',
`delta`int(11)NOTNULLDEFAULT'1'COMMENT'每次id增量',
`remainder`int(11)NOTNULLDEFAULT'0'COMMENT'余數(shù)',
`create_time`timestampNOTNULLDEFAULT'2010-01-010000'COMMENT'創(chuàng)建時(shí)間',
`update_time`timestampNOTNULLDEFAULT'2010-01-010000'COMMENT'更新時(shí)間',
`version`bigint(20)NOTNULLDEFAULT'0'COMMENT'版本號(hào)',
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可訪問的業(yè)務(wù)類型標(biāo)識(shí)',
`remark`varchar(255)NOTNULLDEFAULT''COMMENT'備注',
`create_time`timestampNOTNULLDEFAULT'2010-01-010000'COMMENT'創(chuàng)建時(shí)間',
`update_time`timestampNOTNULLDEFAULT'2010-01-010000'COMMENT'更新時(shí)間',
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
表是具體業(yè)務(wù)方號(hào)段信息數(shù)據(jù)表
![dcff08cc-52c5-11ee-a25d-92fbcf53809c.png](https://file1.elecfans.com/web2/M00/A2/F9/wKgZomUCui-ANtseAABdarlKukA978.png)
max_id
:號(hào)段的最大值
step
:步長(zhǎng),即為號(hào)段的長(zhǎng)度
biz_type
:業(yè)務(wù)類型
號(hào)段獲取對(duì)max_id
字段做一次update
操作,update max_id= max_id + step
,更新成功則說明新號(hào)段獲取成功,新的號(hào)段范圍是(max_id ,max_id +step]
。
tiny_id_token
是一個(gè)權(quán)限表,表示當(dāng)前token可以操作哪些業(yè)務(wù)的號(hào)段信息。
![dd1d31ee-52c5-11ee-a25d-92fbcf53809c.png](https://file1.elecfans.com/web2/M00/A2/F9/wKgZomUCujCANCNsAABfKNXcg5k947.png)
修改tinyid-server
中 offlineapplication.properties
文件配置數(shù)據(jù)庫(kù),由于tinyid
支持?jǐn)?shù)據(jù)庫(kù)多master
模式,可以配置多個(gè)數(shù)據(jù)庫(kù)信息。啟動(dòng) TinyIdServerApplication
測(cè)試一下。
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
內(nèi)部一共提供了四個(gè)http
接口來獲取ID和號(hào)段。
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;
}
}
nextId
、nextIdSimple
都是獲取下一個(gè)ID,nextSegmentIdSimple
、getNextSegmentId
是獲取下一個(gè)可用號(hào)段。區(qū)別在于接口是否有返回狀態(tài)。
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.png](https://file1.elecfans.com/web2/M00/A2/F9/wKgZomUCujCAc5_mAABviCLGmbQ532.png)
![dd56e434-52c5-11ee-a25d-92fbcf53809c.png](https://file1.elecfans.com/web2/M00/A2/F9/wKgZomUCujCAEleEAACG0ChI17g787.png)
2、Tinyid-client客戶端
如果不想通過http方式,Tinyid-client
客戶端也是一種不錯(cuò)的選擇。
引用 tinyid-server
包
com.xiaoju.uemc.tinyid
tinyid-client
${tinyid.version}
啟動(dòng) tinyid-server
項(xiàng)目打包后得到 tinyid-server-0.1.0-SNAPSHOT.jar
,設(shè)置版本 ${tinyid.version}
為0.1.0-SNAPSHOT。
在我們的項(xiàng)目 application.properties
中配置 tinyid-server
服務(wù)的請(qǐng)求地址 和 用戶身份token
tinyid.server=127.0.0.1:9999
tinyid.token=0f673adf80504e2eaa552f5d791b644c```
在Java代碼調(diào)用TinyId
也很簡(jiǎn)單,只需要一行代碼。
//根據(jù)業(yè)務(wù)類型獲取單個(gè)ID
Longid=TinyId.nextId("test");
//根據(jù)業(yè)務(wù)類型批量獲取10個(gè)ID
Listids=TinyId.nextId("test",10);
Tinyid
整個(gè)項(xiàng)目的源碼實(shí)現(xiàn)也是比較簡(jiǎn)單,像與數(shù)據(jù)庫(kù)交互更直接用jdbcTemplate實(shí)現(xiàn)
@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);
}
總結(jié)
兩種方式推薦使用Tinyid-client
,這種方式ID為本地生成,號(hào)段長(zhǎng)度(step
)越長(zhǎng),支持的qps
就越大,如果將號(hào)段設(shè)置足夠大,則qps可達(dá)1000w+。而且tinyid-client
對(duì) tinyid-server
訪問變的低頻,減輕了server端的壓力。
-
數(shù)據(jù)庫(kù)
+關(guān)注
關(guān)注
7文章
3848瀏覽量
64687 -
生成器
+關(guān)注
關(guān)注
7文章
319瀏覽量
21129 -
客戶端
+關(guān)注
關(guān)注
1文章
290瀏覽量
16766
原文標(biāo)題:滴滴的分布式ID生成器,好用的一批!
文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
![](https://file1.elecfans.com/web2/M00/C6/E0/wKgZomYNCkmAGJf2AAKIIXI8xYA361.png)
python生成器
STM32庫(kù)函數(shù)代碼自動(dòng)生成器正式版
python生成器是什么
為什么需要分布式ID?求一種分布式ID生成方案
TSMaster報(bào)文發(fā)送的信號(hào)生成器操作說明
![TSMaster報(bào)文發(fā)送的信號(hào)<b class='flag-5'>生成器</b>操作說明](https://file.elecfans.com/web2/M00/40/07/pYYBAGJrUk2AaMaTAAAQONQtdzo461.jpg)
評(píng)論