> 整理了一些Java方面的架构、面试资料(微服务、集群、分布式、中间件等),有需要的小伙伴可以关注公众号【程序员内点事】,无套路自行领取
- [一口气说出 9种 分布式ID生成方式,面试官有点懵了](https://mp.weixin.qq.com/s?__biz=MzAxNTM4NzAyNg==&mid=2247483785&idx=1&sn=8b828a8ae1701b810fe3969be536cb14&chksm=9b859174acf21862f0b95e0502a1a441c496a5488f5466b2e147d7bb9de072bde37c4db25d7a&token=745402269&lang=zh_CN#rd)
- [面试总被问分库分表怎么办?你可以这样怼他](https://mp.weixin.qq.com/s?__biz=MzAxNTM4NzAyNg==&mid=2247483882&idx=1&sn=9e0901dd19e3e06a135fe96780c43477&chksm=9b859117acf218017d9273d4328258c01f6a105180ae8534dc0aaa362cc0c3020dd55de3a95e&token=361009039&lang=zh_CN#rd)
- [3万字总结,Mysql优化之精髓](https://juejin.im/post/5e3eb616f265da570d734dcb)
- [技术部突然宣布:JAVA开发人员全部要会接口自动化测试框架](https://mp.weixin.qq.com/s?__biz=MzAxNTM4NzAyNg==&mid=2247483790&idx=1&sn=f2cb1cc3a4159f6565799e7cec45c9d7&chksm=9b859173acf218654de6344b576b02cca619dd09b6df4750828315b3d591df82a03055032e0c&token=745402269&lang=zh_CN#rd)
- [9种分布式ID生成之美团(Leaf)实战](https://mp.weixin.qq.com/s?__biz=MzAxNTM4NzAyNg==&mid=2247483889&idx=1&sn=35b0eb32de3e63c013585ef1db5d3076&chksm=9b85910cacf2181a23bdc1344a1984e3fa7613cc8dd9e28f3b10196fc73b9022d735f4031e88&token=662935248&lang=zh_CN#rd)
#### 引言
接着《一口气说出 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。
原理图大致如下图:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200314110819168.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hpbnpoaWZ1MQ==,size_16,color_FFFFFF,t_70)
#### `Tinyid`实现
`Tinyid`的GitHub地址 : https://github.comhttps://img.qb5200.com/download-x/didi/tinyid.git
`Tinyid`提供了两种调用方式,一种基于`Tinyid-server`提供的http方式,另一种`Tinyid-client`客户端方式。
不管使用哪种方式调用,搭建`Tinyid`都必须提前建表`tiny_id_info`、`tiny_id_token`。
```handlebars
CREATE TABLE `tiny_id_info` (
`id` bigint(20) unsigned 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');
```
`tiny_id_info`表是具体业务方号段信息数据表
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200314114423657.png)
`max_id` :号段的最大值
`step`:步长,即为号段的长度
`biz_type`:业务类型
号段获取对`max_id`字段做一次`update`操作,`update max_id= max_id + step`,更新成功则说明新号段获取成功,新的号段范围是`(max_id ,max_id +step]`。
`tiny_id_token`是一个权限表,表示当前token可以操作哪些业务的号段信息。
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020031411444742.png)
修改`tinyid-server`中 `\offline\application.properties` 文件配置数据库,由于`tinyid`支持数据库多`master`模式,可以配置多个数据库信息。启动 `TinyIdServerApplication` 测试一下。
```sql
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:3306https://img.qb5200.com/download-x/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和号段。
```java
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