整理了一些Java方面的架构、面试资料(微服务、集群、分布式、中间件等),有需要的小伙伴可以关注公众号【程序员内点事】,无套路自行领取

引言

接着《一口气说出 9种 分布式ID生成方式,面试官有点懵了》来继续详细的介绍分布式ID生成器,大家比较感兴趣的美团(Leaf)滴滴(Tinyid)百度(uid-generator)三个开源项目,美团(Leaf)已经讲完,详见《9种分布式ID生成之美团(Leaf)实战》,今天结合实战搞一下滴滴开源的(Tinyid)。


Tinyid介绍

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大部分是连续的,容易被扫库、或者推算出订单量等信息


Tinyid原理

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

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

原理图大致如下图:

Tinyid实现

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

Tinyid提供了两种调用方式,一种基于Tinyid-server提供的http方式,另一种Tinyid-client客户端方式。

不管使用哪种方式调用,搭建Tinyid都必须提前建表tiny_id_infotiny_id_token

  1. CREATE TABLE `tiny_id_info` (
  2. `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  3. `biz_type` varchar(63) NOT NULL DEFAULT '' COMMENT '业务类型,唯一',
  4. `begin_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '开始id,仅记录初始值,无其他含义。初始化时begin_id和max_id应相同',
  5. `max_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '当前最大id',
  6. `step` int(11) DEFAULT '0' COMMENT '步长',
  7. `delta` int(11) NOT NULL DEFAULT '1' COMMENT '每次id增量',
  8. `remainder` int(11) NOT NULL DEFAULT '0' COMMENT '余数',
  9. `create_time` timestamp NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '创建时间',
  10. `update_time` timestamp NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '更新时间',
  11. `version` bigint(20) NOT NULL DEFAULT '0' COMMENT '版本号',
  12. PRIMARY KEY (`id`),
  13. UNIQUE KEY `uniq_biz_type` (`biz_type`)
  14. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT 'id信息表';
  15. CREATE TABLE `tiny_id_token` (
  16. `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',
  17. `token` varchar(255) NOT NULL DEFAULT '' COMMENT 'token',
  18. `biz_type` varchar(63) NOT NULL DEFAULT '' COMMENT '此token可访问的业务类型标识',
  19. `remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
  20. `create_time` timestamp NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '创建时间',
  21. `update_time` timestamp NOT NULL DEFAULT '2010-01-01 00:00:00' COMMENT '更新时间',
  22. PRIMARY KEY (`id`)
  23. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT 'token信息表';
  24. INSERT INTO `tiny_id_info` (`id`, `biz_type`, `begin_id`, `max_id`, `step`, `delta`, `remainder`, `create_time`, `update_time`, `version`)
  25. VALUES
  26. (1, 'test', 1, 1, 100000, 1, 0, '2018-07-21 23:52:58', '2018-07-22 23:19:27', 1);
  27. INSERT INTO `tiny_id_info` (`id`, `biz_type`, `begin_id`, `max_id`, `step`, `delta`, `remainder`, `create_time`, `update_time`, `version`)
  28. VALUES
  29. (2, 'test_odd', 1, 1, 100000, 2, 1, '2018-07-21 23:52:58', '2018-07-23 00:39:24', 3);
  30. INSERT INTO `tiny_id_token` (`id`, `token`, `biz_type`, `remark`, `create_time`, `update_time`)
  31. VALUES
  32. (1, '0f673adf80504e2eaa552f5d791b644c', 'test', '1', '2017-12-14 16:36:46', '2017-12-14 16:36:48');
  33. INSERT INTO `tiny_id_token` (`id`, `token`, `biz_type`, `remark`, `create_time`, `update_time`)
  34. VALUES
  35. (2, '0f673adf80504e2eaa552f5d791b644c', 'test_odd', '1', '2017-12-14 16:36:46', '2017-12-14 16:36:48');

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



max_id :号段的最大值

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

biz_type:业务类型

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

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

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

  1. datasource.tinyid.primary.driver-class-name=com.mysql.jdbc.Driver
  2. datasource.tinyid.primary.url=jdbc:mysql://127.0.0.1:3306/xin-master?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8
  3. datasource.tinyid.primary.username=junkang
  4. datasource.tinyid.primary.password=junkang
  5. datasource.tinyid.primary.testOnBorrow=false
  6. datasource.tinyid.primary.maxActive=10
  7. datasource.tinyid.secondary.driver-class-name=com.mysql.jdbc.Driver
  8. datasource.tinyid.secondary.url=jdbc:mysql://localhost:3306/db2?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8
  9. datasource.tinyid.secondary.username=root
  10. datasource.tinyid.secondary.password=123456
  11. datasource.tinyid.secondary.testOnBorrow=false
  12. datasource.tinyid.secondary.maxActive=10
1、Http方式

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

  1. package com.xiaoju.uemc.tinyid.server.controller;
  2. /**
  3. * @author du_imba
  4. */
  5. @RestController
  6. @RequestMapping("/id/")
  7. public class IdContronller {
  8. private static final Logger logger = LoggerFactory.getLogger(IdContronller.class);
  9. @Autowired
  10. private IdGeneratorFactoryServer idGeneratorFactoryServer;
  11. @Autowired
  12. private SegmentIdService segmentIdService;
  13. @Autowired
  14. private TinyIdTokenService tinyIdTokenService;
  15. @Value("${batch.size.max}")
  16. private Integer batchSizeMax;
  17. @RequestMapping("nextId")
  18. public Response<List<Long>> nextId(String bizType, Integer batchSize, String token) {
  19. Response<List<Long>> response = new Response<>();
  20. try {
  21. IdGenerator idGenerator = idGeneratorFactoryServer.getIdGenerator(bizType);
  22. List<Long> ids = idGenerator.nextId(newBatchSize);
  23. response.setData(ids);
  24. } catch (Exception e) {
  25. response.setCode(ErrorCode.SYS_ERR.getCode());
  26. response.setMessage(e.getMessage());
  27. logger.error("nextId error", e);
  28. }
  29. return response;
  30. }
  31. @RequestMapping("nextIdSimple")
  32. public String nextIdSimple(String bizType, Integer batchSize, String token) {
  33. String response = "";
  34. try {
  35. IdGenerator idGenerator = idGeneratorFactoryServer.getIdGenerator(bizType);
  36. if (newBatchSize == 1) {
  37. Long id = idGenerator.nextId();
  38. response = id + "";
  39. } else {
  40. List<Long> idList = idGenerator.nextId(newBatchSize);
  41. StringBuilder sb = new StringBuilder();
  42. for (Long id : idList) {
  43. sb.append(id).append(",");
  44. }
  45. response = sb.deleteCharAt(sb.length() - 1).toString();
  46. }
  47. } catch (Exception e) {
  48. logger.error("nextIdSimple error", e);
  49. }
  50. return response;
  51. }
  52. @RequestMapping("nextSegmentId")
  53. public Response<SegmentId> nextSegmentId(String bizType, String token) {
  54. try {
  55. SegmentId segmentId = segmentIdService.getNextSegmentId(bizType);
  56. response.setData(segmentId);
  57. } catch (Exception e) {
  58. response.setCode(ErrorCode.SYS_ERR.getCode());
  59. response.setMessage(e.getMessage());
  60. logger.error("nextSegmentId error", e);
  61. }
  62. return response;
  63. }
  64. @RequestMapping("nextSegmentIdSimple")
  65. public String nextSegmentIdSimple(String bizType, String token) {
  66. String response = "";
  67. try {
  68. SegmentId segmentId = segmentIdService.getNextSegmentId(bizType);
  69. response = segmentId.getCurrentId() + "," + segmentId.getLoadingId() + "," + segmentId.getMaxId()
  70. + "," + segmentId.getDelta() + "," + segmentId.getRemainder();
  71. } catch (Exception e) {
  72. logger.error("nextSegmentIdSimple error", e);
  73. }
  74. return response;
  75. }
  76. }

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

  1. nextId:
  2. 'http://localhost:9999/tinyid/id/nextId?bizType=test&token=0f673adf80504e2eaa552f5d791b644c'
  3. response
  4. {
  5. "data": [2],
  6. "code": 200,
  7. "message": ""
  8. }
  9. nextId Simple:
  10. 'http://localhost:9999/tinyid/id/nextIdSimple?bizType=test&token=0f673adf80504e2eaa552f5d791b644c'
  11. response: 3



2、Tinyid-client客户端

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

引用 tinyid-server

  1. <dependency>
  2. <groupId>com.xiaoju.uemc.tinyid</groupId>
  3. <artifactId>tinyid-client</artifactId>
  4. <version>${tinyid.version}</version>
  5. </dependency>

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

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

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

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

  1. // 根据业务类型 获取单个ID
  2. Long id = TinyId.nextId("test");
  3. // 根据业务类型 批量获取10个ID
  4. List<Long> ids = TinyId.nextId("test", 10);

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

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

总结

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

整理了一些Java方面的架构、面试资料(微服务、集群、分布式、中间件等),有需要的小伙伴可以关注公众号【程序员内点事】,无套路自行领取

面试总被问分布式ID怎么办? 滴滴(Tinyid)甩给他的更多相关文章

  1. 面试刷题31:分布式ID设计方案

    面试中关于分布式的问题很多.(分布式事务,基本理论CAP,BASE,分布式锁)先来一个简单的. 简单说一下分布式ID的设计方案? 首先要明确在分布式环境下,分布式id的基本要求. 1, 全局唯一,在分 ...

  2. 面试总被问到HTTP缓存机制及原理?看完你就彻底明白了

    前言 Http 缓存机制作为 web 性能优化的重要手段,对于从事 Web 开发的同学们来说,应该是知识体系库中的一个基础环节,同时对于有志成为前端架构师的同学来说是必备的知识技能. 但是对于很多前端 ...

  3. 面试被问分布式事务(2PC、3PC、TCC),这样解释没毛病!

    整理了一些Java方面的架构.面试资料(微服务.集群.分布式.中间件等),有需要的小伙伴可以关注公众号[程序员内点事],无套路自行领取 更多优选 一口气说出 9种 分布式ID生成方式,面试官有点懵了 ...

  4. 9种分布式ID生成之 美团(Leaf)实战

    整理了一些Java方面的架构.面试资料(微服务.集群.分布式.中间件等),有需要的小伙伴可以关注公众号[程序员内点事],无套路自行领取 更多优选 一口气说出 9种 分布式ID生成方式,面试官有点懵了 ...

  5. 一口气说出 9种 分布式ID生成方式,面试官有点懵了

    整理了一些Java方面的架构.面试资料(微服务.集群.分布式.中间件等),有需要的小伙伴可以关注公众号[程序员内点事],无套路自行领取 本文作者:程序员内点事 原文链接:https://mp.weix ...

  6. 面试总问的jvm调优到底是要干什么?

    1. 压力测试的理解,xxx的性能10w/s,对你有意义么? 没有那家卖瓜的会说自己家的不甜,同样,没有哪个开源项目愿意告诉你在对它条件最苛刻的时候压力情况是多少,一般官网号称给你看的性能指标都是在最 ...

  7. 一口气说出9种分布式ID生成方式,面试官有点懵

    一.为什么要用分布式ID? 在说分布式ID的具体实现之前,我们来简单分析一下为什么用分布式ID?分布式ID应该满足哪些特征? 1.1.什么是分布式ID? 拿MySQL数据库举个栗子:在我们业务数据量不 ...

  8. 面试连环炮系列(十一):说说你们的分布式ID设计方案

    说说你们的分布式ID设计方案 我们采用Snowflake算法,生成一个64bit的数字,64bit被划分成多个段,分别表示时间戳.机器编码.序号. 41位的时间序列(精确到毫秒,41位的长度可以使用6 ...

  9. 美团分布式ID生成框架Leaf源码分析及优化改进

    本文主要是对美团的分布式ID框架Leaf的原理进行介绍,针对Leaf原项目中的一些issue,对Leaf项目进行功能增强,问题修复及优化改进,改进后的项目地址在这里: Leaf项目改进计划 https ...

随机推荐

  1. SQL热备原理

  2. CAS 5.3.x 相关信息

    CAS 5.3.x 相关信息 单点登录系统 学习网站: https://www.apereo.org/projects/cas 官方网站 https://github.com/apereo/cas-o ...

  3. [LC] 116. Populating Next Right Pointers in Each Node

    You are given a perfect binary tree where all leaves are on the same level, and every parent has two ...

  4. ionic2踩坑之文本域自适应高度(自定义指令,适用angular2)

    话不多说,看代码: import { Directive, ElementRef, HostListener,Input, Renderer, Component } from '@angular/c ...

  5. ionic2踩坑之ionic build android报错

    自己项目一直跑的好好好好的,build还是run都没问题,今天忽然一个小伙伴build一直报错.\ 错误如下: Error occurred during initialization of VMCo ...

  6. Pandas提取单元格的值

    如提取第1行,第2列的值: df.iloc[[0],[1]] 则会返回一个df,即有字段名和行号. 如果用values属性取值: df.iloc[[0],[1]].values 返回的值会是列表,而且 ...

  7. 奇葩念头:微信能取代WP应用吗

         墙倒众人推,原本是再平常不过的事,但每次发生都显得特别凄凉--就和楼市买房买涨不买跌一样,在互联网行业,用户数量越多的产品或服务,就会越受到行业和大众青睐.而越是小众的产品或服务,虽然是要走 ...

  8. Google在百慕大避税几十亿美金,为什么巨头和富豪都会选百慕大避税?

    为什么"越有钱越有钱"?为什么富豪只要不自己"作",就能让自己的财富疯狂增加?除了经营意识之外,关键他们还可以利用自己的资源.实力等去做很多看似让人不齿,但其实 ...

  9. 如何进行Web服务的性能测试

         涉及到web服务的功能在不断的增加,对于我们测试来说,我们不仅要保证服务端功能的正确性,也要验证服务端程序的性能是否符合要求.那么性能测试都要做些什么呢?我们该怎样进行性能测试呢? 性能测试 ...

  10. spring cloud实战 1-高可用注册中心

    创建父maven项目 提交代码至GitHub 创建eureka-server-1 项目搭建两种方式: 父pom中继承spring-boot-starter-parent,子pom中直接结成父pom.该 ...