Tinyid 深度解密滴滴的高性能ID生成器( 二 )


6、Tinyid的技术原理详解6.1 ID生成系统的技术要点在简单系统中,我们常常使用db的id自增方式来标识和保存数据,随着系统的复杂,数据的增多,分库分表成为了常见的方案,db自增已无法满足要求 。
这时候全局唯一的id生成系统就派上了用场,当然这只是id生成其中的一种应用场景 。
那么,一个成熟的id生成系统应该具备哪些能力呢?
1)唯一性:无论怎样都不能重复,id全局唯一是最基本的要求;
2)高性能:基础服务尽可能耗时少,如果能够本地生成最好;
3)高可用:虽说很难实现100%的可用性,但是也要无限接近于100%的可用性;
4)易用性:能够拿来即用,接入方便,同时在系统设计和实现上要尽可能的简单 。
6.2 Tinyid的实现原理我们先来看一下最常见的id生成方式,db的auto_increment,相信大家都非常熟悉 。
我也见过一些同学在实战中使用这种方案来获取一个id,这个方案的优点是简单,缺点是每次只能向db获取一个id,性能比较差,对db访问比较频繁,db的压力会比较大 。
那么,是不是可以对这种方案优化一下呢?可否一次向db获取一批id呢?答案当然是可以的 。
一批id,我们可以看成是一个id范围,例如(1000,2000],这个1000到2000也可以称为一个“号段”,我们一次向db申请一个号段,加载到内存中,然后采用自增的方式来生成id,这个号段用完后,再次向db申请一个新的号段,这样对db的压力就减轻了很多,同时内存中直接生成id,性能则提高了很多 。
PS:简单解释一下什么是号段模式:

号段模式就是从数据库批量的获取自增ID,每次从数据库取出一个号段范围,例如 (1,1000] 代表1000个ID,业务服务将号段在本地生成1~1000的自增ID并加载到内存 。
那么保存db号段的表该怎设计呢?我们继续往下看 。
6.3 DB号段算法描述
Tinyid 深度解密滴滴的高性能ID生成器

文章插图
 
如上表,我们很容易想到的是db直接存储一个范围(start_id,end_id],当这批id使用完毕后,我们做一次update操作,update start_id=2000(end_id), end_id=3000(end_id+1000),update成功了,则说明获取到了下一个id范围 。仔细想想,实际上start_id并没有起什么作用,新的号段总是(end_id,end_id+1000] 。
所以这里我们更改一下,db设计应该是这样的:
Tinyid 深度解密滴滴的高性能ID生成器

文章插图
 
如上表所示:
1)我们增加了biz_type,这个代表业务类型,不同的业务的id隔离;
2)max_id则是上面的end_id了,代表当前最大的可用id;
3)step代表号段的长度,可以根据每个业务的qps来设置一个合理的长度;
4)version是一个乐观锁,每次更新都加上version,能够保证并发更新的正确性。
那么我们可以通过如下几个步骤来获取一个可用的号段:
A、查询当前的max_id信息:select id, biz_type, max_id, step, version from tiny_id_info where biz_type='test';
B、计算新的max_id: new_max_id = max_id + step;
C、更新DB中的max_id:update tiny_id_info set max_id=#{new_max_id} , verison=version+1 where id=#{id} and max_id=#{max_id} and version=#{version};
D、如果更新成功,则可用号段获取成功,新的可用号段为(max_id, new_max_id];
E、如果更新失败,则号段可能被其他线程获取,回到步骤A,进行重试 。
6.4 号段生成方案的简单架构如上述内容,我们已经完成了号段生成逻辑 。
那么我们的id生成服务架构可能是这样的:
Tinyid 深度解密滴滴的高性能ID生成器

文章插图
 
如上图,id生成系统向外提供http服务,请求经过我们的负载均衡router,到达其中一台tinyid-server,从事先加载好的号段中获取一个id 。
如果号段还没有加载,或者已经用完,则向db再申请一个新的可用号段,多台server之间因为号段生成算法的原子性,而保证每台server上的可用号段不重,从而使id生成不重 。
可以看到:
1)如果tinyid-server如果重启了,那么号段就作废了,会浪费一部分id;
2)同时id也不会连续;
3)每次请求可能会打到不同的机器上,id也不是单调递增的,而是趋势递增的(不过这对于大部分业务都是可接受的) 。
6.5 简单架构的问题到此一个简单的id生成系统就完成了,那么是否还存在问题呢?
回想一下我们最开始的id生成系统要求:高性能、高可用、简单易用 。


推荐阅读