开源|为什么要使用ns4_gear_idgen ID生成器?

38次阅读

共计 3949 个字符,预计需要花费 10 分钟才能阅读完成。

导语:宜信于 2019 年 3 月 29 日正式开源 nextsystem4(以下简称“NS4”)系列模块。此次开源的 NS4 系列模块是围绕当前支付系统笨重、代码耦合度高、维护成本高而产生的分布式业务系统解决方案。NS4 系列框架允许创建复杂的流程 / 业务流,对于业务服务节点的实现可串联,可分布式。其精简、轻量,实现了“脱容器”(不依赖 tomcat、jetty 等容器)独立运行。NS4 系列框架的设计理念是将业务和逻辑进行分离,开发人员只需通过简单的配置和业务实现就可以实现逻辑复杂、性能高效、功能稳定的业务系统。点击查看框架整体介绍
NS4 系列包括 4 个开源模块,分别是:ns4_frame 分布式服务框架(详情点击查看:开源 |ns4_frame 分布式服务框架开发指南)、ns4_gear_idgen ID 生成器组件(NS4 框架 Demo 示例)、ns4_gear_watchdog 监控系统组件(服务守护、应用性能监控、数据采集、自动化报警系统)和 ns4_chatbot 通讯组件。
其中,ns4_gear_idgen(ID 生成器)是基于 ns4_frame 框架实现的,它支持分布式部署,生成全局唯一的 ID,其中长度、前缀、后缀、步长、进制也可根据自己的业务自由配置,还可以通过 ns4_gear_idgen 对 NS4.0 框架进行测试。本文重点介绍 ns4_gear_idgen(ID 生成器)方案具备哪些优点。
项目开源地址:https://github.com/newsettle/…
一、引子
在复杂的系统中,往往需要使用一个有意义且有序的序列号来作为全局唯一的 ID,来对大量的数据(如订单账户)进行标识。
二、业内方案简介
2.1 时间戳方案
取当前毫秒数 / 微秒作为 ID,如 System.currentTimeMillis()
优点

本地生成 ID,不需要进行远程调用,时延低。
生成的 ID 趋势递增。
生成的 ID 是整数,建立索引后查询效率高。

缺点

并发量过高,会生成重复的 ID。
不能高可用,存在单点故障问题。
不够灵活,不能实现对不同业务的 ID 隔离。

2.2 UUID 方案
UUID(Universally Unique Identifier)的标准型式包含 32 个 16 进制数字,以连字号分为五段,形式为 8 -4-4-4-12 的 36 个字符。示例:550e8400-e29b-41d4-a716- 446655440000,到目前为止业界一共有 5 种方式生成 UUID,详情见 IETF 发布的 UUID 规范 A Universally Unique IDentifier (UUID) URN Namespace。
优点
性能非常高:本地生成,没有网络消耗。
缺点

不易于存储:UUID 太长,16 字节 128 位,通常以 36 长度的字符串表示,很多场景不适用。
信息不安全:基于 MAC 地址生成 UUID 的算法可能会造成 MAC 地址泄露,这个漏洞曾被用于寻找梅丽莎病毒的制作者位置。
无序 ID:于 ID 之前毫无顺序可言。

ID 作为主键时在特定的环境会存在一些问题,比如做 DB 主键的场景下,UUID 就非常不适用:
A:MySQL 官方有明确的建议主键要尽量越短越好[4],36 个字符长度的 UUID 不符合要求。
All indexes other than the clustered index are known as secondary indexes. In InnoDB, each record in a secondary index contains the primary key columns for the row, as well as the columns specified for the secondary index. InnoDB uses this primary key value to search for the row in the clustered index. If the primary key is long, the secondary indexes use more space, so it is advantageous to have a short primary key.
B:对 MySQL 索引不利。如果作为数据库主键,在 InnoDB 引擎下,UUID 的无序性可能会引起数据位置频繁变动,严重影响性能。

2.3 Snowflake 方案
这种方案大致来说是一种以划分命名空间 (UUID 也算,由于比较常见,所以单独分析) 来生成 ID 的一种算法,这种方案把 64-bit 分别划分成多段,分开来标示机器、时间等,比如在 snowflake 中的 64-bit 分别表示。如下图 (图片来自网络) 所示:

41-bit 的时间可以表示(1L<<41)/(1000L*3600*24*365)=69 年的时间,10-bit 机器可以分别表示 1024 台机器。如果我们对 IDC 划分有需求,还可以将 10-bit 分 5 -bit 给 IDC,分 5 -bit 给工作机器。这样就可以表示 32 个 IDC,每个 IDC 下可以有 32 台机器,可以根据自身需求定义。
12 个自增序列号可以表示 2^12 个 ID,理论上 snowflake 方案的 QPS 约为 409.6w/s,这种分配方式可以保证在任何一个 IDC 的任何一台机器在任意毫秒内生成的 ID 都是不同的。
优点

毫秒数在高位,自增序列在低位,整个 ID 都是趋势递增的。
不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成 ID 的性能也是非常高的。
可以根据自身业务特性分配 bit 位,非常灵活。

缺点

强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。
不够灵活,不能实现对不同业务的 ID 隔离。

2.4 数据库 auto_increment 方案
以 MySQL 举例,利用给字段设置 auto_increment_increment 和 auto_increment_offset 来保证 ID 自增,每次业务使用下列 SQL 读写 MySQL 得到 ID 号。
begin;
REPLACE INTO Tickets64 (stub) VALUES (‘a’);
SELECT LAST_INSERT_ID();
commit;

优点

非常简单,利用现有数据库系统的功能实现,成本小,有 DBA 专业维护。
ID 号单调自增,可以实现一些对 ID 有特殊要求的业务。

缺点

强依赖 DB,当 DB 异常时整个系统不可用,属于致命问题。配置主从复制可以尽可能的。
增加可用性,但是数据一致性在特殊情况下难以保证。主从切换时的不一致可能会导致重复发号。
ID 发号性能瓶颈限制在单台 MySQL 的读写性能。

2.5 redis 生成 ID
Redis 的所有命令操作都是单线程的,本身提供像 incr 和 increby 这样的自增原子命令,所以能保证生成的 ID 肯定是唯一有序的。
考虑到单节点的性能瓶颈,可以使用 Redis 集群来获取更高的吞吐量。假如一个集群中有 5 台 Redis。可以初始化每台 Redis 的值分别是 1, 2, 3, 4, 5,然后步长都是 5。各个 Redis 生成的 ID 为:
A:1, 6, 11, 16, 21
B:2, 7, 12, 17, 22
C:3, 8, 13, 18, 23
D:4, 9, 14, 19, 24
E:5, 10, 15, 20, 25
优点

不依赖于数据库,灵活方便,且性能优于数据库。
数字 ID 天然排序,对分页或者需要排序的结果很有帮助。
使用集群可以防止单点故障问题。

缺点

如果系统中没有 Redis,还需要引入新的组件,增加系统复杂度。
需要编码和配置的工作量比较大。
步长、初始值需提前确定好且不易于扩展。

2.6 ns4_gear_idgen 方案
先看下数据库表设计:

字段说明:

id:数据库主键,无实际含义。
key_name:用来区分业务,不同的业务使用不同的。
key_name,每个 key_name 的 ID 相互隔离,互不影响。如果以后有性能需求需要对数据库扩容,不需要上述描述的复杂的扩容操作,只需要对 biz_tag 分库分表就行。
key_value:表示该 key_name 目前所被分配的 ID 号段的最大值。
key_length:生成 ID 的长度。
key_cache:表示每次分配的号段长度。原来获取 ID 每次都需要写数据库,现在只需要把 key_cache 设置得足够大,比如 1000。那么只有当 1000 个号被消耗完了之后才会去重新读写一次数据库。读写数据库的频率从 1 减小到了 1 /step。

key_prefix:生成 ID 的前缀,可配置自定义前缀 + 日期部分 如: ${date14}/TEST${date14}
ID 前缀日期部分支持以下几种日期格式:

key_suffix=:生成 ID 的后缀,可配置亦可不配置
key_digit:ID 进制数,支持 10 进制 36 进制 62 进制。
Version:每条记录对应的版本号,用户更新记录。

优点

很方便的线性扩展,能够支撑大多数业务场景。
生成 ID 规则多样,可配置且支持 10 进制、36 进制、62 进制。
业务之间 ID 相互隔离,互不影响。
获取 ID 不用频繁操作数据库,快消耗完号段内 ID 时才会操作数据库,减轻了数据库的压力。
提前初始化号段内的 ID,保证在每个号段内 ID 使用完之前初始化完成,避免业务使用完 ID 后才初始化带来的影响。
可以自定义 key_value 的大小,非常方便业务从原有的 ID 方式上迁移过来。
容灾性高:服务内部有号段缓存,即使 DB 宕机,短时间内服务仍能正常对外提供服务。

三、功能介绍
该 ID 生成器是基于 NS4 框架实现的,支持分布式部署,同时生成的 ID 长度、前缀、后缀、步长,进制也可根据自己的业务自由的配置。
其功能可分为以下几个部分:

获取单个 Long 类型的 ID 如 66310
获取批量 String 类型的 ID:19011123221266312, 19011123221266313, 19011123221266314, 19011123221266315

四、请求方式

五、SQL 脚本
见 ns4_gear_idgen 源码下 gear_key.sql
内容来源:宜信技术学院

正文完
 0