当前位置: 首页 > news >正文

【分布式】分布式ID生成方案、接口幂等、一致性哈希

【分布式】分布式ID生成方案、接口幂等、一致性哈希

  • 1、分布式ID生成方案都有哪些?
    • 1.1 UUID
    • 1.2 数据库自增
    • 1.3 号段模式
    • 1.4 Redis 实现
    • 1.5 雪花算法
  • 2、如何解决接口幂等的问题?
  • 3、什么是一致性哈希?

1、分布式ID生成方案都有哪些?

在单体应用中,我们可以通过数据库的主键ID来生成唯一的ID,但是如果数据量变大,就需要进行分库分表,在分库分表之后,如何生成一个全局唯一的ID,就是一个关键的问题。

通常情况下,对于分布式ID来说,我们一般希望他具有以下几个特点:

● 全局唯一:必须保证全局唯一性,这个是最基本的要求。
● 高性能&高可用:需要保证ID的生成是稳定且高效的。
● 递增:根据不同的业务情况,有的会要求生成的ID呈递增趋势,也有的要求必须单调递增(后一个ID必须比前一个大),也有的没有严格要求。

1.1 UUID

UUID(Universally Unique Identifier)全局唯一标识符,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。

标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12),共32个字符,通常由以下几部分的组合而成:当前日期和时间,时钟序列,全局唯一的IEEE机器识别号

UUID的优点就是他的性能比较高,不依赖网络,本地就可以生成,使用起来也比较简单。但是他也有两个比较明显的缺点,那就是长度过长和没有任何含义。长度自然不必说,他有32位16进制数字。对于"550e8400-e29b-41d4-a716-446655440000"这个字符串来说,我想任何一个程序员都看不出其表达的含义。一旦使用它作为全局唯一标识,就意味着在日后的问题排查和开发调试过程中会遇到很大的困难。

用UUID当做分布式ID,存在着不适合范围查询、不方便展示以及查询效率低等问题。

1.2 数据库自增

分布式ID也可以使用数据库的自增ID,但是这种实现中就要求一定是一个单库单表才能保证ID自增且不重复,这就带来了一个单点故障的问题。

一旦这个数据库挂了,那整个分布式ID的生成服务就挂了。而且还存在一个性能问题,如果高并发访问数据库的话,就会带来阻塞问题。

1.3 号段模式

号段模式是在数据库的基础上,为了解决性能问题而产生的一种方案。他的意思就是每次去数据库中取ID的时候取出来一批,并放在缓存中,然后下一次生成新ID的时候就从缓存中取。这一批用完了再去数据库中拿新的。

而为了防止多个实例之间发生冲突,需要采用号段的方式,即给每个客户端发放的时候按号段分开,如客户端A取的号段是1-1000,客户端B取的是1001-2000,客户端C取的是2001-3000。当客户端A用完之后,再来取的时候取到的是3001-4000。

号段模式的好处是在同一个客户端中,生成的ID是顺序递增的。并且不需要频繁的访问数据库,也能提升获取ID的性能。缺点是没办法保证全局顺序递增,也存在数据库的单点故障问题。

其实很多分库分表的中间件的主键ID的生成,主要采用的也是号段模式,如TDDL Sequence。

1.4 Redis 实现

基于数据库可以实现,那么基于Redis也是可以的,我们可以依赖Redis的incr命令实现ID的原子性自增。

Redis的好处就是可以借助集群解决单点故障的问题,并且他基于内存性能也比较高。

但是Redis存在数据丢失的情况,无论是那种持久化机制,都无法完全避免。

1.5 雪花算法

雪花算法(Snowflake)是由Twitter研发的一种分布式ID生成算法,它可以生成全局唯一且递增的ID。它的核心思想是将一个64位的ID划分成多个部分,每个部分都有不同的含义,包括时间戳、数据中心标识、机器标识和序列号等。

具体来说,雪花算法生成的ID由以下几个部分组成:

  1. 符号位(1bit):预留的符号位,始终为0,占用1位。
  2. 时间戳(41bit):精确到毫秒级别,41位的时间戳可以容纳的毫秒数是2的41次幂,一年所使用的毫秒数是:365 * 24 * 60 * 60 * 1000,算下来可以使用69年。
  3. 数据中心标识(5bit):可以用来区分不同的数据中心。
  4. 机器标识(5bit):可以用来区分不同的机器。
  5. 序列号(12bit):可以生成4096个不同的序列号。

在这里插入图片描述
基于以上结构,雪花算法在唯一性保证方面就有很多优势:

首先,时间戳位于ID的最高位,保证新生成的ID比旧的ID大,在不同的毫秒内,时间戳肯定不一样

其次,引入数据中心标识和机器标识,这两个标识位都是可以手动配置的,帮助业务来保证不同的数据中心和机器能生成不同的ID。

还有就是,引入序列号,用来解决同一毫秒内多次生成ID的问题,每次生成ID时序列号都会自增,因此不同的ID在序列号上有区别。

所以,基于时间戳+数据中心标识+机器标识+序列号,就保证了在不同进程中主键的不重复,在相同进程中主键的有序性。

雪花算法之所以被广泛使用,主要是因为他有以下优点:

  1. 高性能高可用:生成时不依赖于数据库,完全在内存中生成
  2. 高吞吐:每秒钟能生成数百万的自增 ID
  3. ID 自增:在单个进程中,生成的ID是自增的,可以用作数据库主键做范围查询。但是需要注意的是,在集群中是没办法保证一定顺序递增的。

SnowFlake 算法的缺点或者限制:

1、在Snowflake算法中,每个节点的机器ID和数据中心ID都是硬编码在代码中的,而且这些ID是全局唯一的。当某个节点出现故障或者需要扩容时,就需要更改其对应的机器ID或数据中心ID,但是这个过程比较麻烦,需要重新编译代码,重新部署系统。还有就是,如果某个节点的机器ID或数据中心ID被设置成了已经被分配的ID,那么就会出现重复的ID,这样会导致系统的错误和异常。

2、Snowflake算法中,需要使用zookeeper来协调各个节点的ID生成,但是ZK的部署其实是有挺大的成本的,并且zookeeper本身也可能成为系统的瓶颈。

3、依赖于系统时间的一致性,如果系统时间被回拨,或者不一致,可能会造成 ID 重复。但是时钟回拨并不常见,而且就算发生了,也不代表着生成的ID一定就会重复。所以重复其实是个低概率事件,所以遇到ID重复可以直接抛异常重试。

2、如何解决接口幂等的问题?

高并发场景下,想要解决这个问题,只需要记住一句口令"一锁、二判、三更新",只要严格遵守这个过程,那么就可以解决高并发问题。

一锁:第一步,先加锁。可以加分布式锁、或者悲观锁都可以。但是一定要是一个互斥锁!
二判:第二步,进行幂等性判断。可以基于状态机、流水表、唯一性索引等等进行重复操作的判断。
三更新:第三步,进行数据的更新,将数据进行持久化。

三步需要严格控制顺序,确保加锁成功后进行数据查询和判断,幂等性判断通过后再更新,更新结束后释放锁。

以上操作需要有一个前提,那就是第一步加锁、和第二步判断的时候,需要有一个依据,这个就是幂等号了,通常需要和上游约定一个唯一ID作为幂等号。然后通过对幂等号加锁,再通过幂等号进行幂等判断即可。

一锁这个过程,建议使用Redis实现分布式锁,因为他是非阻塞(tryLock)的高效率的互斥锁。非常适合在幂等控制场景中。

二判这个过程,如果有操作流水,建议基于操作流水做幂等,并将幂等号作为唯一性约束,确保唯一性。如果没有流水,那么基于状态机也是可以的。

但是不管怎么样,数据库的唯一性约束都要加好,这是系统的最后一道防线。万一前面的锁失效了,这里也能控制得住不会产生脏数据。

3、什么是一致性哈希?

哈希算法大家都不陌生,经常被用在负载均衡、分库分表等场景中,比如说我们在做分库分表的时候,最开始我们根据业务预估,把数据库分成了128张表,这时候要插入或者查询一条记录的时候,我们就会先把分表键,如buyer_id进行hash运算,然后再对128取模,得到0-127之间的数字,这样就可以唯一定位到一个分表。

但是随着业务得突飞猛进,128张表,已经不够用了,这时候就需要重新分表,比如增加一张新的表。这时候如果采用hash或者取模的方式,就会导致128+1张表的数据都需要重新分配,成本巨高。

而一致性hash算法, 就能有效的解决这种分布式系统中增加或者删除节点时的失效问题。

一致性哈希(Consistent Hashing)是一种用于分布式系统中数据分片和负载均衡的算法。它的目标是在节点的动态增加或删除时,尽可能地减少数据迁移和重新分布的成本。

实现一致性哈希算法首先需要构造一个哈希环,然后把他划分为固定数量的虚拟节点,如2^32。那么他的节点编号就是 0-2^32-1:

在这里插入图片描述

接下来, 我们把128张表作为节点映射到这些虚拟节点上,每个节点在哈希空间上都有一个对应的虚拟节点:

hash(table_0000)%232、hash(table_0001)%232、hash(table_0002)%2^32 … hash(table_0127)%2^32

在这里插入图片描述

在把这些表做好hash映射之后,我们就需要存储数据了,现在我们要把一些需要分表的数据也根据同样的算法进行hash,并且也将其映射哈希环上。

hash(buyer_id)%2^32:
hash(12321)%232、hash(34432)%232、hash(54543)%2^32 … hash(767676)%2^32

在这里插入图片描述
这样,这个hash环上的虚拟节点就包含两部分数据的映射了,一部分是存储数据的分表的映射,一部分是真实要存储的数据的映射。

那么, 我们最终还是要把这些数据存储到数据库分表中,那么做好哈希之后,这些数据又要保存在哪个数据库表节点中呢?

其实很简单,只需要按照数据的位置,沿着顺时针方向查找,找到的第一个分表节点就是数据应该存放的节点:

在这里插入图片描述
因为要存储的数据,以及存储这些数据的数据库分表,hash后的值都是固定的,所以在数据库数量不变的情况下,下次想要查询数据的时候,只需要按照同样的算法计算一次就能找到对应的分表了。

以上,就是一致性hash算法的原理,那么,再回到我们开头的问题,如果我要增加一个分表怎么办呢?

我们首先要将新增加的表通过一致性hash算法映射到哈希环的虚拟节点中:

在这里插入图片描述
这样,会有一部分数据,因为节点数量发生变化,那么他顺时针遇到的第一个分表可能就变了。

在这里插入图片描述
相比于普通hash算法,在增加服务器之后,影响的范围非常小,只影响到一部分数据,其他的数据是不需要调整的。

所以,一致性哈希算法将整个哈希空间视为一个环状结构,将节点和数据都映射到这个环上。每个节点通过计算一个哈希值,将节点映射到环上的一个位置。而数据也通过计算一个哈希值,将数据映射到环上的一个位置

当有新的数据需要存储时,首先计算数据的哈希值,然后顺时针或逆时针在环上找到最近的节点,将数据存储在这个节点上。当需要查找数据时,同样计算数据的哈希值,然后顺时针或逆时针在环上找到最近的节点,从该节点获取数据

优点:

  1. 数据均衡:在增加或删除节点时,一致性哈希算法只会影响到少量的数据迁移,保持了数据的均衡性。
  2. 高扩展性:当节点数发生变化时,对于已经存在的数据,只有部分数据需要重新分布,不会影响到整体的数据结构。

缺点:

  1. hash倾斜:在节点数较少的情况下,由于哈希空间是有限的,节点的分布可能不够均匀,导致数据倾斜。
  2. 节点的频繁变更:如果频繁添加或删除节点,可能导致大量的数据迁移,造成系统压力。

参考链接:
1、https://www.yuque.com/hollis666/wk6won/hgx0twgg4t7nqg6v

http://www.dtcms.com/a/399121.html

相关文章:

  • 分布式 ID 生成方案实战指南:从选型到落地的全场景避坑手册(一)
  • 对比网站做企业网站需要用到的软件
  • HAProxy 与 Tomcat
  • Tomcat 配置与使用指南
  • 金融数据库--下载全市场可转债日线行情数据
  • 新能源知识库(105)什么是 日本JEMS平台
  • 怎么学做电商然后自己创业seo优化关键词排名
  • 360免费建站怎么进不去网站开发合同模板免费
  • 51单片机实践之数码管电子时钟/时间显示及其设置
  • 条款9:优先选用别名声明,而非typedef
  • Matlab调用GPT-5 API示例
  • 猪八戒网站做软件让你有做黑客感觉的网站
  • 毕业设计指导网站建设揭阳网站推广教程
  • 安全多方计算 联邦学习 同态加密
  • 宁波新亚建设内部网站wordpress开发文档下载
  • GWO-Transformer灰狼算法优化编码器多特征分类预测/故障诊断,Matlab实现,代码解析+运行效果一览
  • 2025 化工 PLM 市场图鉴:软件厂商技术布局与行业应用,助力企业数字化转型
  • 竞品网站分析微信网站需要备案吗
  • 大模型-LLM-large language model
  • 探索生成式纠错在构音障碍语音识别中的应用
  • SCARA 机器人轨迹运动奇异点规避方法
  • 【Linux】自动化构建--make/Makefile
  • 建设银行手机网站wordpress电台
  • 同一种激光模式 高阶模式的产生会降低基模的功率吗 能降低多少?
  • 设计模式-责任链模式详解
  • CentOS7安装mysql最简单教程
  • 垂直网站建设方案南庄建网站服务
  • 【LVS入门宝典】LVS DR模式深度解析:直接路由(DR)的高性能秘诀,MAC地址欺骗与ARP隔离
  • 自做网站视频潍坊免费网站制作
  • 代理通讯篇无外网或不可达SockS全协议规则配置C2正反向上线解决方案