ID生成服务系列(一)

简介: ID生成服务系列(一)

一、超高并发,超高性能分布式ID服务的要求

在复杂的超高并发,分布式系统中,往往需要对大量的数据和消息进行唯一标识。

在高并发,分布式的金支付,餐饮,酒店,电影等产品的系统中,数据日渐增长,对数据库分库分表后需要有一个唯一的标识一条数据或消息,数据库的自增ID显然不能满足需求;特别一点的如订单,骑手,优惠券也都需要有唯一的ID做标识。

此时一个能够生成全局唯一ID的系统是非常必要的。

概括下来,那业务系统对ID号的要求有哪些呢?

①、全局唯一性:不能出现重复的ID号,既然是唯一标识,这是最基本的要求

②、趋势递增:在MySQL InnoDB引擎中使用的是聚簇索引,由于多数RDBMS使用B-Tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能

③、单调递增:保证下一个ID一定是大于上一个ID,例如事务版本号,IM增量消息,排序等特殊需求。

④、信息安全:如果ID是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL即可;如果是订单号就更加危险了,可以直接知道我们一天的订单量,所以在一些应用场景下,会需要ID无规则。

注意:1234对应不同的场景

特别注意:3和4需求还是互斥的,无法使用同一个方案满足

同时除了对ID号码自身的要求,业务还对ID生成系统的可用性要求极高,并且处于业务的黄金链路上。

想象一下,如果ID生成系统瘫痪,整个系统黄金链路上关键动作都无法执行,这就会带来一场灾难。

二、有序ID能提升写入性能的根本原因:InnoDB存储引擎的数据页分裂

因为在mysql当中,我们写数据是写入到innodb当中的,而且底层的聚簇索引是分页存储的,每个页的大小默认是16kb,页与页之间是用双向链表关联的。

6aab441e62587fdb453d18779b81c550.png

在查找的时候是二分查找,时间复杂度是o(logN)是最高性能的。但是写入的时候是低性能的,而且不能随机的写入,必须保证主键有序,页内ID是有序的。

在聚簇索引当中有两个约束:

①、ID有序存储

②、分页存储

如果在上面的过程当中,ID是无序的话,在一个页当中是满的话,把id插入进入的话,会发生页的分裂:一个页会分成两个页,并且在聚簇索引里面还有非聚簇索引字段,这个时候其他的数据也要进行磁盘的复制移动,会带来高频的磁盘IO。

三、ID生成服务的阶段

①、各自封装

企业内部,早期业务量比较少,各个系统基本都有自己的ID生成模块,类型也是五花八门。有基于TIDB自增的ID,有基于UUID的,也有基于雪花算法的。其中雪花算法也被称为snowflake,使用最为广泛。各自封装模块比较简单,但是实现分散,各自系模块的质量也很难统一保证。

②、集成框架

为了解决上述分散实现的问题,企业内部统一实现了一个综合各类ID生成功能的基础库,供业务方统一调用。

统一基础库解决了分散调用的问题,但是对于snowflake这种带有workerId的算法,需要业务系统关注workerId分配的逻辑

于是,我们把snowflake的逻辑封装到了服务治理框架内,服务启动时,由框架来负责workerId的分配和服务内的唯一性。

③、idGen服务

封装到框架后,同一服务的不同实例之间可以很好的处理workerId的分配的问题。

但是,workerID的逻辑也使得服务内多个实例成为了有状态实例,k8s部署也只能使用statefulset。随着业务量突飞猛进,系统数量增加,业务对系统的稳定性,弹性提出了更多的需求。我们需要ID生成逻辑非常稳定,高效,我们需要服务实例都是无状态实例Deployment,使服务具备快速滚动升级,弹性伸缩的能力。

基于这样的背景,我们决定提供一个单独的ID生成服务,需求如下:

①、支持DB号段模式和snowflake模式两种模式

②、ID生成器自身的可用性,稳定性非常高,具备时钟校准能力

③、吞吐量高,TP99:3毫秒内生成,必须非常低

④、兼容现有逻辑,业务迁移要非常方便

⑤、服务使用Deployment部署

四、ID服务架构设计

DB号段模式

号段模式,简单来说可以理解为对DB自增ID方案的优化

本质上是利用批量获取的方式,定期获取一个号段,缓存在本地供外部使用,减轻DB的压力,提升对外服务性能。

00e395620be4bf64c543dafb5f53ad9d.png

从上往下:

sdk层:

sdk给到第三方的应用sdk的去快速的生成id,单节点的要求:至少100万的qps压测的时候golang sdk600w的qps。java的吞吐量也是大几十万qps

接入层:

由k8s svc:kube-proxy负载云原生的负载均衡。并没有用springcloud gateway

流量通过K8s ingress组件反向代理把流量进来进入到svc:部署Ingress使用

HostNetWork+daemonSet+nodeSelector来保证高可用,选择k8s的两个节点做ingress的部署节点。ingress边缘节点保证高可用选择keepAlivevps来做id漂移的高可用:这里只是同一个idc机房做到高可用:同一个网段做到高可用,实现单个服务的故障的转移,还要做到同城多活/异地多活,idc机房之间的高可用。

号段微服务层:

sdk一定是高并发的。获取id并不是通过号段服务来获取id的,通过号段服务拿到id的区段,之后id的

分配由sdk在内存分配,因为在同内存具备高性能的分配。

号段服务无状态的,功能极致的弱化:伸缩,通过k8s的自动伸缩的资源控制器HPA,进行横向扩展,分配ID号段的。

如果把后端ID服务的压力变小的话,可以把号段弄大一点。

号段弄大的,比如弄成1百万,如果号段很大,每启动一次会浪费1百万个,id是long型的,2的32次方就到了10亿级别。如果无符号到了3-40亿,中间浪费了几百万id的空洞也没问题,而且 业务应用也不会频繁的启动。

号段服务,保证db:存id的区段的高可用:引用db的压力也不会大。

DB层:

DB层也要保证同城多活/异地多活。

snowflake模式

snowflake是Tiwitter于2010年首次对外公开,其值是64位整数,可以做到全局唯一

构造如下:

bbd6c52ce13b2521cc5fda5c9330ee16.png

雪花算法的 ID 由以下几个部分组成:符号位:1 个 bit,始终为 0,用于区分正数和负数。时间戳:41 个 bit,精确到毫秒级别。使用当前时间减去一个固定的开始时间,可以得到一个时间差 值。由于时间戳占用了 41 个 bit,最大可表示的时间为 2^41 / (1000 * 60 * 60 * 24 * 365) = 69 年左右。数据中心 ID:5 个 bit,用于区分不同的数据中心。如果没有多个数据中心,可以将其设置为 0。机器 ID:5 个 bit,用于区分同一数据中心内不同的机器。同样地,如果没有多台机器,可以将其设置为 0。序列号:12 个 bit,用于区分同一毫秒内生成的不同 ID。由于序列号只有 12 个 bit,最大可表示的序列号为 2^12 - 1 = 4095。如果在同一毫秒内生成的序列号超过了 4095,需要等到下一毫秒再生成新的ID。综上所述,一个雪花算法生成的 ID 长度为 64 bit,可以保证在分布式系统中生成唯一的 ID。

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
7月前
|
存储 C# 数据库
C# 生成唯一ID,有哪些方法?
【2月更文挑战第12天】
963 0
|
前端开发
class id
class id
79 0
|
2月前
|
Oracle Java 关系型数据库
@Id、@GeneratedValue的作用,以及@GeneratedValue的使用
@Id、@GeneratedValue的作用,以及@GeneratedValue的使用
|
3月前
|
算法 NoSQL 数据库
ID生成服务系列(二)
ID生成服务系列(二)
|
自然语言处理 安全 Unix
了解一下新工具ULID?
了解一下新工具ULID?
264 0
|
自然语言处理 算法 安全
还在用uuid ,ulid来了
还在用uuid ,ulid来了
1750 0
|
安全
IDOR绝不止他人的 ID
目标中有这样一个功能,免费用户最多可以同时创建 3 个列表。如果您发送创建三个以上列表的请求,该站点将授予普通用户选择 3 个列表并锁定第 4 个和其他列表的权利。此外,无法管理、添加或删除、共享或重命名锁定列表。
123 0
IDOR绝不止他人的 ID
高性能高并发的生成唯一的Id
高性能高并发的生成唯一的Id
111 0