当前位置:网站首页 > 历史 > 特殊符号生成器(特殊符号生成器在线转换)

特殊符号生成器(特殊符号生成器在线转换)

特殊符号生成器(特殊符号生成器在线转换)更新内容(v1.4.8)PreciseShardingValueRangeShardingValue特殊符号生成器(特殊符号生成器在线转换)简介CosId旨在提供通用、灵活、高性能的分布式ID生成器。目前提供了俩类ID生成器:快速开始背景(为什么需要分

更新内容(v1.4.8)

PreciseShardingValue

RangeShardingValue

特殊符号生成器(特殊符号生成器在线转换)

特殊符号生成器(特殊符号生成器在线转换)

简介

CosId旨在提供通用、灵活、高性能的分布式ID生成器。目前提供了俩类ID生成器:

快速开始

背景(为什么需要分布式ID)

在软件系统演进过程中,随着业务规模的增长,我们需要进行集群化部署来分摊计算、存储压力,应用服务我们可以很轻松做到无状态、弹性伸缩。但是仅仅增加服务副本数就够了吗?显然不够,因为性能瓶颈往往是在数据库层面,那么这个时候我们就需要考虑如何进行数据库的扩容、伸缩、集群化,通常使用分库、分表的方式来处理。那么我如何分片(水平分片,当然还有垂直分片不过不是本文需要讨论的内容)呢,分片得前提是我们得先有一个ID,然后才能根据分片算法来分片。(比如比较简单常用的ID取模分片算法,这个跟Hash算法的概念类似,我们得先有key才能进行Hash取得插入槽位。)

当然还有很多分布式场景需要分布式ID,这里不再一一列举。

分布式ID方案的核心指标

不同分布式ID方案核心指标对比

分布式ID

全局唯一性

有序性

吞吐量

稳定性(1s=1000,000us)

自治性

可用性

适应性

存储空间

UUID/GUID

完全无序

3078638(ops/s)

P9999=0.325(us/op)

完全自治

100%

128-bit

SnowflakeId

本地单调递增,全局趋势递增(受全局时钟影响)

4096000(ops/s)

P9999=0.244(us/op)

依赖时钟

时钟回拨会导致短暂不可用

64-bit

SegmentId

本地单调递增,全局趋势递增(受Step影响)

29506073(ops/s)

P9999=46.624(us/op)

依赖第三方号段分发器

受号段分发器可用性影响

64-bit

SegmentChainId

本地单调递增,全局趋势递增(受Step、安全距离影响)

127439148(ops/s)

P9999=0.208(us/op)

依赖第三方号段分发器

受号段分发器可用性影响,但因安全距离存在,预留ID段,所以高于SegmentId

64-bit

有序性(要想分而治之·二分查找法,必须要维护我)

刚刚我们已经讨论了ID有序性的重要性,所以我们设计ID算法时应该尽可能地让ID是单调递增的,比如像表的自增主键那样。但是很遗憾,因全局时钟、性能等分布式系统问题,我们通常只能选择局部单调递增、全局趋势递增的组合(就像我们在分布式系统中不得不的选择最终一致性那样)以获得多方面的权衡。下面我们来看一下什么是单调递增与趋势递增。

有序性之单调递增

单调递增:T表示全局绝对时点,假设有Tn+1>Tn(绝对时间总是往前进的,这里不考虑相对论、时间机器等),那么必然有F(Tn+1)>F(Tn),数据库自增主键就属于这一类。另外需要特别说明的是单调递增跟连续性递增是不同的概念。连续性递增:F(n+1)=(F(n)+step)即下一次获取的ID一定等于当前ID+Step,当Step=1时类似于这样一个序列:1->2->3->4->5。

扩展小知识:数据库的自增主键也不是连续性递增的,相信你一定遇到过这种情况,请思考一下数据库为什么这样设计?

有序性之趋势递增

趋势递增:Tn>Tn-s,那么大概率有F(Tn)>F(Tn-s)。虽然在一段时间间隔内有乱序,但是整体趋势是递增。从上图上看,是有上升趋势的(趋势线)。

分布式ID分配方案

UUID/GUID

UUID最大的缺陷是随机的、无序的,当用于主键时会导致数据库的主键索引效率低下(为了维护索引树,频繁的索引中间位置插入数据,而不是追加写)。这也是UUID不适用于数据库主键的最为重要的原因。

SnowflakeId

SnowflakeId使用Long(64-bit)位分区来生成ID的一种分布式ID算法。通用的位分配方案为:timestamp(41-bit)+machineId(10-bit)+sequence(12-bit)=63-bit。

从SnowflakeId设计上可以看出:

SnowflakeId之机器号分配问题

在SnowflakeId中根据业务设计的位分配方案确定了基本上就不再有变更了,也很少需要维护。但是machineId总是需要配置的,而且集群中是不能重复的,否则分区原则就会被破坏而导致ID唯一性原则破坏,当集群规模较大时machineId的维护工作是非常繁琐,低效的。

有一点需要特别说明的,SnowflakeId的MachineId是逻辑上的概念,而不是物理概念。想象一下假设MachineId是物理上的,那么意味着一台机器拥有只能拥有一个MachineId,那会产生什么问题呢?

目前CosId提供了以下三种MachineId分配器。

SnowflakeId之时钟回拨问题

时钟回拨的致命问题是会导致ID重复、冲突(这一点不难理解),ID重复显然是不能被容忍的。在SnowflakeId算法中,按照MachineId分区ID,我们不难理解的是不同MachineId是不可能产生相同ID的。所以我们解决的时钟回拨问题是指当前MachineId的时钟回拨问题,而不是所有集群节点的时钟回拨问题。

MachineId时钟回拨问题大体可以分为俩种情况:

SnowflakeId之JavaScript数值溢出问题

JavaScript的Number.MAX_SAFE_INTEGER只有53-bit,如果直接将63位的SnowflakeId返回给前端,那么会产生值溢出的情况(所以这里我们应该知道后端传给前端的long值溢出问题,迟早会出现,只不过SnowflakeId出现得更快而已)。很显然溢出是不能被接受的,一般可以使用以下俩种处理方案:

号段模式(SegmentId)

从上面的设计图中,不难看出号段模式基本设计思路是通过每次获取一定长度(Step)的可用ID(Id段/号段),来降低网络IO请求次数,提升性能。

号段链模式(SegmentChainId)

分布式ID(CosId)之号段链模式性能(1.2亿/s)解析

SegmentChainId是SegmentId增强版,相比于SegmentId有以下优势:

集成

CosIdPlugin(MyBatis插件)

KotlinDSL

ShardingSphere插件

KotlinDSL

CosIdKeyGenerateAlgorithm(分布式主键)

基于间隔的时间范围分片算法

PreciseShardingValue

RangeShardingValue

取模分片算法

PreciseShardingValue

RangeShardingValue

性能测试报告

SegmentChainId-吞吐量(ops/s)

RedisChainIdBenchmark-Throughput

MySqlChainIdBenchmark-Throughput

SegmentChainId-每次操作耗时的百分位数(us/op)

RedisChainIdBenchmark-Percentile

MySqlChainIdBenchmark-Percentile

基准测试报告运行环境说明

上一篇: 清朝名将杨芳简介(清代名将杨芳)
下一篇: 兰陵王高长恭历史介绍(兰陵王高长恭容貌复原图)

为您推荐

发表评论