溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

什么是分布式ID生成器Tinyid

發(fā)布時(shí)間:2021-10-22 15:31:53 來(lái)源:億速云 閱讀:102 作者:iii 欄目:數(shù)據(jù)庫(kù)

本篇內(nèi)容介紹了“什么是分布式ID生成器Tinyid”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

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ù)、或者推算出訂單量等信息

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。

原理圖大致如下圖:

什么是分布式ID生成器Tinyid

Tinyid原理圖

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。

CREATE TABLE `tiny_id_info` (    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主鍵',    `biz_type` varchar(63) NOT NULL DEFAULT '' COMMENT '業(yè)務(wù)類型,唯一',    `begin_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '開始id,僅記錄初始值,無(wú)其他含義。初始化時(shí)begin_id和max_id應(yīng)相同',    `max_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '當(dāng)前最大id',    `step` int(11) DEFAULT '0' COMMENT '步長(zhǎng)',    `delta` int(11) NOT NULL DEFAULT '1' COMMENT '每次id增量',    `remainder` int(11) NOT NULL DEFAULT '0' COMMENT '余數(shù)',    `create_time` timestamp NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '創(chuàng)建時(shí)間',    `update_time` timestamp NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '更新時(shí)間',    `version` bigint(20) NOT NULL DEFAULT '0' COMMENT '版本號(hào)',    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可訪問(wèn)的業(yè)務(wù)類型標(biāo)識(shí)',    `remark` varchar(255) NOT NULL DEFAULT '' COMMENT '備注',    `create_time` timestamp NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '創(chuàng)建時(shí)間',    `update_time` timestamp NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '更新時(shí)間',    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');

tiny_id_info表是具體業(yè)務(wù)方號(hào)段信息數(shù)據(jù)表

什么是分布式ID生成器Tinyid

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,更新成功則說(shuō)明新號(hào)段獲取成功,新的號(hào)段范圍是(max_id ,max_id +step]。

tiny_id_token是一個(gè)權(quán)限表,表示當(dāng)前token可以操作哪些業(yè)務(wù)的號(hào)段信息。

什么是分布式ID生成器Tinyid

修改tinyid-server中  \offline\application.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:mysql://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: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

1、Http方式

tinyid內(nèi)部一共提供了四個(gè)http接口來(lái)獲取ID和號(hào)段。

package com.xiaoju.uemc.tinyid.server.controller;  /**   * @author du_imba   */  @RestController  @RequestMapping("/id/")  public class IdContronller {       private static final Logger logger = LoggerFactory.getLogger(IdContronller.class);      @Autowired      private IdGeneratorFactoryServer idGeneratorFactoryServer;      @Autowired      private SegmentIdService segmentIdService;      @Autowired      private TinyIdTokenService tinyIdTokenService;      @Value("${batch.size.max}")      private Integer batchSizeMax;      @RequestMapping("nextId")     public Response<List<Long>> nextId(String bizType, Integer batchSize, String token) {          Response<List<Long>> response = new Response<>();          try {              IdGenerator idGenerator = idGeneratorFactoryServer.getIdGenerator(bizType);              List<Long> ids = idGenerator.nextId(newBatchSize);              response.setData(ids);          } catch (Exception e) {              response.setCode(ErrorCode.SYS_ERR.getCode());              response.setMessage(e.getMessage());              logger.error("nextId error", e);          }          return response;     }      @RequestMapping("nextIdSimple")      public String nextIdSimple(String bizType, Integer batchSize, String token) {          String response = "";          try {              IdGenerator idGenerator = idGeneratorFactoryServer.getIdGenerator(bizType);              if (newBatchSize == 1) {                  Long id = idGenerator.nextId();                  response = id + "";              } else {                  List<Long> idList = idGenerator.nextId(newBatchSize);                  StringBuilder sb = new StringBuilder();                  for (Long id : idList) {                     sb.append(id).append(",");                  }                  response = sb.deleteCharAt(sb.length() - 1).toString();              }          } catch (Exception e) {              logger.error("nextIdSimple error", e);         }          return response;      }      @RequestMapping("nextSegmentId")      public Response<SegmentId> nextSegmentId(String bizType, String token) {          try {              SegmentId segmentId = segmentIdService.getNextSegmentId(bizType);              response.setData(segmentId);          } catch (Exception e) {              response.setCode(ErrorCode.SYS_ERR.getCode());              response.setMessage(e.getMessage());              logger.error("nextSegmentId error", e);          }          return response;      }      @RequestMapping("nextSegmentIdSimple")      public String nextSegmentIdSimple(String bizType, String token) {          String response = "";          try {              SegmentId segmentId = segmentIdService.getNextSegmentId(bizType);              response = segmentId.getCurrentId() + "," + segmentId.getLoadingId() + "," + segmentId.getMaxId()                      + "," + segmentId.getDelta() + "," + segmentId.getRemainder();          } catch (Exception e) {              logger.error("nextSegmentIdSimple error", e);          }          return response;      }  }

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": ""  }  nextId Simple:  'http://localhost:9999/tinyid/id/nextIdSimple?bizType=test&token=0f673adf80504e2eaa552f5d791b644c'  response: 3

什么是分布式ID生成器Tinyid

什么是分布式ID生成器Tinyid

2、Tinyid-client客戶端

如果不想通過(guò)http方式,Tinyid-client客戶端也是一種不錯(cuò)的選擇。

引用 tinyid-server包

<dependency>      <groupId>com.xiaoju.uemc.tinyid</groupId>      <artifactId>tinyid-client</artifactId>      <version>${tinyid.version}</version>  </dependency>

啟動(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  Long id = TinyId.nextId("test");  // 根據(jù)業(yè)務(wù)類型 批量獲取10個(gè)ID  List<Long> ids = TinyId.nextId("test", 10);

Tinyid整個(gè)項(xiàng)目的源碼實(shí)現(xiàn)也是比較簡(jiǎn)單,像與數(shù)據(jù)庫(kù)交互更直接用jdbcTemplate實(shí)現(xiàn)

@Override  public TinyIdInfo queryByBizType(String bizType) {      String sql = "select id, biz_type, begin_id, max_id," +              " step, delta, remainder, create_time, update_time, version" +              " from tiny_id_info where biz_type = ?";      List<TinyIdInfo> list = jdbcTemplate.query(sql, new Object[]{bizType}, new TinyIdInfoRowMapper());      if(list == null || list.isEmpty()) {          return null;      }      return list.get(0);  }

“什么是分布式ID生成器Tinyid”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI