导语:宜信于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/ns4_gear_idgen
一、引子
在复杂的系统中,往往需要使用一个有意义且有序的序列号来作为全局唯一的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
内容来源:宜信技术学院
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/191752.html