什么是一致性哈希?

典型回答 哈希算法大家都不陌生,经常被用在负载均衡、分库分表等场景中,比如说我们在做分库分表的时候,最开始我们根据业务预估,把数据库分成了128张表,这时候要插入或者查询一条记录的时候,我们就会先把分表键,如buyer_id进行hash运算,然后再对128取模,得到0-127之间的数字,这样就可以唯一定位到一个分表。 但是随着业务得突飞猛进,128张表,已经不够用了,这时候就需要重新分表,比如增加一张新的表。这时候如果采用hash或者取模的方式,就会导致128+1张表的数据都需要重新分配,成本巨高。 而一致性hash算法, 就能有效的解决这种分布式系统中增加或者删除节点时的失效问题。 一致性哈希(Consistent Hashing)是一种用于分布式系统中数据分片和负载均衡的算法。它的目标是在节点的动态增加或删除时,尽可能地减少数据迁移和重新分布的成本。 实现一致性哈希算法首先需要构造一个哈希环,然后把他划分为固定数量的虚拟节点,如2^32。那么他的节点编号就是 0-2^32-1: 接下来, 我们把128张表作为节点映射到这些虚拟节点上,每个节点在哈希空间上都有一个对应的虚拟节点: hash(table_0000)%2^32、hash(table_0001)%2^32、hash(table_0002)%2^32 …. hash(table_0127)%2^32 在把这些表做好hash映射之后,我们就需要存储数据了,现在我们要把一些需要分表的数据也根据同样的算法进行hash,并且也将其映射哈希环上。 hash(buyer_id)%2^32: hash(12321)%2^32、hash(34432)%2^32、hash(54543)%2^32 …. hash(767676)%2^32 这样,这个hash环上的虚拟节点就包含两部分数据的映射了,一部分是存储数据的分表的映射,一部分是真实要存储的数据的映射。 那么, 我们最终还是要把这些数据存储到数据库分表中,那么做好哈希之后,这些数据又要保存在哪个数据库表节点中呢? 其实很简单,只需要按照数据的位置,沿着顺时针方向查找,找到的第一个分表节点就是数据应该存放的节点: 因为要存储的数据,以及存储这些数据的数据库分表,hash后的值都是固定的,所以在数据库数量不变的情况下,下次想要查询数据的时候,只需要按照同样的算法计算一次就能找到对应的分表了。 以上,就是一致性hash算法的原理,那么,再回到我们开头的问题,如果我要增加一个分表怎么办呢? 我们首先要将新增加的表通过一致性hash算法映射到哈希环的虚拟节点中: 这样,会有一部分数据,因为节点数量发生变化,那么他顺时针遇到的第一个分表可能就变了。 相比于普通hash算法,在增加服务器之后,影响的范围非常小,只影响到一部分数据,其他的数据是不需要调整的。 所以,再总结一下。一致性哈希算法将整个哈希空间视为一个环状结构,将节点和数据都映射到这个环上。每个节点通过计算一个哈希值,将节点映射到环上的一个位置。而数据也通过计算一个哈希值,将数据映射到环上的一个位置。 当有新的数据需要存储时,首先计算数据的哈希值,然后顺时针或逆时针在环上找到最近的节点,将数据存储在这个节点上。当需要查找数据时,同样计算数据的哈希值,然后顺时针或逆时针在环上找到最近的节点,从该节点获取数据。 优点: 数据均衡:在增加或删除节点时,一致性哈希算法只会影响到少量的数据迁移,保持了数据的均衡性。 高扩展性:当节点数发生变化时,对于已经存在的数据,只有部分数据需要重新分布,不会影响到整体的数据结构。 但是,他也不是没有缺点的: hash倾斜:在节点数较少的情况下,由于哈希空间是有限的,节点的分布可能不够均匀,导致数据倾斜。 节点的频繁变更:如果频繁添加或删除节点,可能导致大量的数据迁移,造成系统压力。 扩展知识 hash倾斜 其实,hash倾斜带来的主要问题就是如果数据过于集中的话,就会使得节点数量发生变化时,数据的迁移成本过高。 那么想要解决这个问题,比较好的办法就是增加服务器节点,这样节点就会尽可能的分散了。 但是如果没有那么多服务器,我们也可以引入一些虚拟节点,把一个服务器节点,拆分成多个虚拟节点,然后数据在映射的时候先映射到虚拟节点,然后虚拟节点在找到对应的物理节点进行存储和读取就行了。 这时候,因为有虚拟节点的引入,数据就会比较分散,在增加或者减少服务器数量的时候,影响的数据就不会有那么多了。

March 22, 2026 · 1 min · santu

什么是事务消息,为什么需要事务消息?

典型回答 如果不用事务消息,就用本地消息的话,那么一次操作一般是这样的流程: 1、执行本地事务 2、发送MQ消息 3、消费MQ消息 如果一切顺利,那么没啥说的,双方都能处理成功,最终是一致的,但是实际情况是,因为网络延迟、网络抖动、服务器本身的稳定性、MQ自身的稳定性等原因,这个过程会出现各种各样的问题。 一旦在第一个参与者本地事务操作之后,如果出现了MQ发送失败、或者发送成功了,但是MQ自己存储失败了等原因,可能就会导致不一致了。 有人会问了,那这里如果MQ发失败了,本地事务回滚不就行了么? 有问题,因为会出现一种极端情况,那就是当出现网络抖动的时候,发送MQ因为网络超时返回了失败,本地事务回滚之后,但是网络超时不一定是MQ没有接收到,有可能处理成功了,但是返回的时候超时了。这时候就会出现:本地事务回滚了、但是MQ发送成功了的问题。这时候下游正常消费MQ之后,就又出现不一致了。 ✅为啥不要在事务中做外部调用? 而且,MQ自身也不一定可靠,不管是哪种MQ,在极端情况下,都是有可能丢消息的,也就说,可能会出现本地事务成功之后,发送MQ成功了,但是因为MQ自身原因,导致消息丢了,还是会出现不一致。 所以,总之就是引入MQ之后,会因为各种原因导致不一致,那怎么解决这个问题呢? 解决方案就是能有一个机制保证MQ一定可以发送成功,或者是如果失败了,也有机制能够重试让他成功。 那么这个方案就是事务消息(RocketMQ中的那种,非Kafka中的那种),即把一个发送消息的过程拆成2步,先发一个半消息,确保成功之后,在执行本地事务,本地事务成功后,再发第二个半消息。 如果第一个半消息发失败了,本地事务不会执行。 如果第一个半消息发成功了,本地事务执行失败了,MQ也不会消费。 如果第二个半消息发送失败了,MQ会反查来决定commit还是rollback。 具体参考: ✅RocketMQ的事务消息是如何实现的? ✅RocketMQ的事务消息和Kafka的事务消息有什么区别?

March 22, 2026 · 1 min · santu

什么是分布式事务?

典型回答 分布式事务是指在分布式系统中涉及到多个数据库或多个应用程序之间的事务处理,这些数据库或应用程序可能分布在不同的物理节点上,甚至可能位于不同的地理位置。在分布式事务中,需要确保所有参与者的事务操作都能够保持一致性,即所有参与者的事务要么全部提交成功,要么全部回滚。 举个例子,假设一个电商系统,用户下单后需要扣减库存、扣减账户余额、生成订单等操作。在单机环境下,可以将这些操作放在同一个事务中,保证原子性、一致性和持久性。但在分布式环境下,可能存在多个服务(如库存服务、账户服务、订单服务)分布在不同的物理节点上,此时需要确保所有服务操作的事务都能够同步进行,避免出现数据不一致的情况。 为了解决分布式事务的问题,出现了一些分布式事务解决方案,如XA协议、TCC事务、最大努力通知等。这些解决方案的实现方式各不相同,但都需要考虑如何确保所有参与者的事务操作能够保持一致性,以及如何处理可能出现的异常情况。 ✅常见的分布式事务有哪些?

March 22, 2026 · 1 min · santu

什么是分布式事务中的三阶段提交(3PC)

典型回答 ✅有了2阶段提交为什么还需要3阶段提交? 上面的2PC中介绍过了两阶段提交的原理和他主要存在的问题,在 2PC 中,如果协调者(Coordinator)在关键节点挂掉,参与者(Participant)可能会一直阻塞,甚至出现 数据不一致的情况。于是 3PC 在 2PC 的基础上加入了一个额外的阶段(预提交阶段),并引入了 超时机制,让参与者在没有收到协调者指令时也能自己做决定,从而减少阻塞问题。 3PC把2PC的准备阶段再次一分为二,这样三阶段提交就有CanCommit、PreCommit、DoCommit三个阶段。 CanCommit 阶段(询问阶段) 协调者向所有参与者发送 CanCommit 请求,询问是否可以执行事务。 参与者: 如果可以执行(比如本地检查通过),返回 Yes; 如果不可以执行,返回 No。 这一阶段相当于 2PC 的第一阶段,但只是“问一下”。 PreCommit 阶段(预提交阶段) 如果所有参与者都返回 Yes: 协调者向所有参与者发送 PreCommit 请求,进入预提交阶段。 参与者执行事务操作(写日志、加锁),并进入 等待提交状态。 此时参与者可以安全地提交事务,但还没有最终提交。 如果有任何参与者返回 No: 协调者发送 Abort 请求,所有参与者回滚事务。 这一阶段是 3PC 与 2PC 的关键区别:参与者已经进入一个“可以提交”的安全状态。 DoCommit 阶段(提交阶段) 如果 PreCommit 阶段所有参与者都确认成功: 协调者向所有参与者发送 DoCommit 请求。 参与者正式提交事务并释放资源。 如果有失败或超时: 协调者发送 Abort 请求,参与者回滚事务。 如果协调者在这个阶段挂掉,参与者可以依赖超时机制自行提交事务,从而避免阻塞。 这里再举一个生活中类似三阶段提交的例子: 班长要组织全班同学聚餐,由于大家毕业多年,所以要逐个打电话敲定时间,时间初定10.1日。然后开始逐个打电话。 班长:小A,我们想定在10.1号聚会,你有时间嘛?有时间你就说YES,没有你就说NO,然后我还会再去问其他人,具体时间地点我会再通知你,这段时间你可先去干你自己的事儿,不用一直等着我。(协调者询问事务是否可以执行,这一步不会锁定资源) 小A:好的,我有时间。(参与者反馈) 班长:小B,我们想定在10.1号聚会……不用一直等我。 班长收集完大家的时间情况了,一看大家都有时间,那么就再次通知大家。(协调者接收到所有YES指令) 班长:小A,我们确定了10.1号聚餐,你要把这一天的时间空出来,这一天你不能再安排其他的事儿了。然后我会逐个通知其他同学,通知完之后我会再来和你确认一下,还有啊,如果我没有特意给你打电话,你就10.1号那天来聚餐就行了。对了,你确定能来是吧?(协调者发送事务执行指令,这一步锁住资源。如果由于网络原因参与者在后面没有收到协调者的命令,他也会执行commit) ...

March 22, 2026 · 1 min · santu

什么是分布式事务中的两阶段提交(2PC)

典型回答 在分布式系统中,一个事务可能会跨多个数据库或服务节点。我们需要保证事务的 ACID 特性,即使分布在多个节点上,也要么所有节点都成功提交,要么全部回滚,不能出现部分成功、部分失败的情况。 2PC(Two-Phase Commit,两阶段提交协议) 就是一个经典的分布式事务一致性协议,通过协调者(Coordinator) 和 参与者(Participant) 的配合来实现。参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是终止操作。 协调者(Coordinator): 通常就是发起事务的那个应用程序或一个专门的事务管理器。 负责驱动整个协议流程,询问所有参与者,并根据反馈做出最终决定。 参与者(Participants): 分布式事务中涉及的各个独立的资源管理器。 例如:不同的数据库、不同的微服务。 它们各自管理自己的本地事务,并执行协调者的指令。 所谓的两个阶段是指:**第一阶段:准备阶段(投票阶段)和第二阶段:提交阶段(执行阶段)。**在日常生活中其实是有很多事都是这种二阶段提交的,比如西方婚礼中就经常出现这种场景: 牧师:”你愿意娶这个女人吗?爱她、忠诚于她,无论她贫困、患病或者残疾,直至死亡。Doyou(你愿意吗)?” 新郎:”Ido(我愿意)!” 牧师:”你愿意嫁给这个男人吗?爱他、忠诚于他,无论他贫困、患病或者残疾,直至死亡。Doyou(你愿意吗)?” 新娘:”Ido(我愿意)!” 牧师:现在请你们面向对方,握住对方的双手,作为妻子和丈夫向对方宣告誓言。 新郎:我——某某某,全心全意娶你做我的妻子,无论是顺境或逆境,富裕或贫穷,健康或疾病,快乐或忧愁,我都将毫无保留地爱你,我将努力去理解你,完完全全信任你。我们将成为一个整体,互为彼此的一部分,我们将一起面对人生的一切,去分享我们的梦想,作为平等的忠实伴侣,度过今后的一生。 新娘:我全心全意嫁给你作为你的妻子,无论是顺境或逆境,富裕或贫穷,健康或疾病,快乐或忧愁,我都将毫无保留的爱你,我将努力去理解你,完完全全信任你,我们将成为一个整体,互为彼此的一部分,我们将一起面对人生的一切,去分享我们的梦想,作为平等的忠实伴侣,度过今后的一生。 上面这个比较经典的桥段就是一个典型的二阶段提交过程。 首先协调者(牧师)会询问两个参与者(二位新人)是否能执行事务提交操作(愿意结婚)。如果两个参与者能够执行事务的提交,先执行事务操作,然后返回YES,如果没有成功执行事务操作,就返回NO。 当协调者接收到所有的参与者的反馈之后,开始进入事务提交阶段。如果所有参与者都返回YES,那就发送COMMIT请求,如果有一个人返回NO,那就发送rollback请求。 值得注意的是,二阶段提交协议的第一阶段准备阶段不仅仅是回答YES or NO,还是要执行事务操作的,只是执行完事务操作,并没有进行commit还是rollback。和上面的结婚例子不太一样。如果非要举例的话可以理解为男女双方交换定情信物的过程。信物一旦交给对方了,这个信物就不能挪作他用了。也就是说,一旦事务执行之后,在没有执行commit或者rollback之前,资源是被锁定的。这会造成阻塞。 所以2PC总结一下就是以下两个PC: 第一阶段:准备阶段(Prepare Phase) 协调者 向所有参与者发送 Prepare 请求,询问能否提交事务。 参与者: 执行事务,但不提交(只是写入日志或加锁,进入“预提交”状态)。 如果成功,返回 “Yes”;如果失败或遇到冲突,返回 “No”。 此时事务还没有真正提交,所有参与者都处于 等待协调者指令的状态。 第二阶段:提交阶段(Commit Phase) 如果 所有参与者都返回 Yes: 协调者向所有参与者发送 Commit 请求。 参与者正式提交事务,并释放锁资源。 如果 任意一个参与者返回 No(或超时未响应): 协调者向所有参与者发送 Rollback 请求。 参与者回滚事务,释放锁资源。 这样,所有节点的结果保持一致。 ...

March 22, 2026 · 1 min · santu

什么是分布式系统?和集群的区别?

典型回答 分布式是针对集中式来说的,先说集中式,集中式系统就是把一整个系统的所有功能,包括数据库等等全部都部署在一起,通过一个整套系统对外提供服务。但是集中式系统存在系统大而复杂、难于维护、容易发生单点故障、扩展性差等问题。而这些问题在分布式系统中可以很好的解决。 分布式就是把一个集中式系统拆分成多个系统,每一个系统单独对外提供部分功能,整个分布式系统整体对外提供一整套服务。对于访问分布式系统的用户来说,感知上就像访问一台计算机一样。 分布式意味着可以采用更多的普通计算机(相对于昂贵的大型机)组成分布式集群对外提供服务。计算机越多,CPU、内存、存储资源等也就越多,能够处理的并发访问量也就越大。但是分布式系统中也存在着网络通信延迟、数据一致性等问题。 拿电商网站来说,我们一般把一个电商网站横向拆分成商品模块、订单模块、购物车模块、消息模块、支付模块等。然后我们把不同的模块部署到不同的机器上,各个模块之间通过远程服务调用(RPC)等方式进行通信。以一个分布式的系统对外提供服务。 分布式(distributed)是指在多台不同的服务器中部署不同的服务模块,通过远程调用协同工作,对外提供服务。 集群(cluster)是指在多台不同的服务器中部署相同应用或服务模块,构成一个集群,通过负载均衡设备对外提供服务。 扩展知识 分布式系统的特征 分布式系统需要各个主机之间通信和协调主要通过网络进行,所以,分布式系统中的计算机在空间上几乎没有任何限制,这些计算机可能被放在不同的机柜上,也可能被部署在不同的机房中,还可能在不同的城市中,对于大型的网站甚至可能分布在不同的国家和地区。 但是,无论空间上如何分布,一个标准的分布式系统应该具有以下几个主要特征: 分布性 分布式系统中的多台计算机之间在空间位置上可以随意分布,系统中的多台计算机之间没有主、从之分,即没有控制整个系统的主机,也没有受控的从机。 透明性 系统资源被所有计算机共享。每台计算机的用户不仅可以使用本机的资源,还可以使用本分布式系统中其他计算机的资源(包括CPU、文件、打印机等)。 同一性 系统中的若干台计算机可以互相协作来完成一个共同的任务,或者说一个程序可以分布在几台计算机上并行地运行。 通信性 系统中任意两台计算机都可以通过通信来交换信息。 和集中式系统相比,分布式系统的性价比更高、处理能力更强、可靠性更高、也有很好的扩展性。但是,分布式在解决了网站的高并发问题的同时也带来了一些其他问题。首先,分布式的必要条件就是网络,这可能对性能甚至服务能力造成一定的影响。其次,一个集群中的服务器数量越多,服务器宕机的概率也就越大。另外,由于服务在集群中分布式部署,用户的请求只会落到其中一台机器上,所以,一旦处理不好就很容易产生数据一致性问题。

March 22, 2026 · 1 min · santu

什么是柔性事务?

典型回答 柔性事务,是业内解决分布式事务的主要方案。所谓柔性事务,相比较与数据库事务中的ACID这种刚性事务来说,柔性事务保证的是“基本可用,最终一致。”这其实就是基于BASE理论,保证数据的最终一致性。 虽然柔性事务并不像刚性事务那样完全遵循ACID,但是,也是部分遵循ACID的,简单看一下关于ACID四个属性,柔性事务的支撑程度: 原子性:严格遵循 一致性:事务完成后的一致性严格遵循;事务中的一致性可适当放宽 隔离性:并行事务间不可影响;事务中间结果可见性允许安全放宽 持久性:严格遵循 在业内,关于柔性事务**,最主要的有以下三种类型:异步确保型、补偿型、最大努力通知型。** 扩展知识 想要实现柔性事务,有几个基础条件需要具备,以下介绍几个柔性事务实现的基础。 可查询操作 可查询操作,几乎是所有的分布式解决方案都需要的。 举一个常见的分布式场景的例子,如订单处理这一功能: 1 2 3 4 5 6 7 8 /** 支付订单处理 **/ public void completeOrder() { orderDao.update(); // 订单服务本地更新订单状态 accountService.update(); // 调用资金账户服务给资金帐户加款 pointService.update(); // 调用积分服务给积分帐户增加积分 accountingService.insert(); // 调用会计服务向会计系统写入会计原始凭证 merchantNotifyService.notify(); // 调用商户通知服务向商户发送支付结果通知 } 以上这个支付订单处理的例子中,除了订单服务本地更新订单状态以外的所有操作,都需要调用RPC接口来执行,这种情况单纯的本地事务就无法保证数据的一致性了。就需要引入分布式事务。 在分布式事务执行过程中,如果某一个步骤执行出错,就需要明确的知道其他几个操作的处理情况,这就需要其他的服务都能够提供查询接口,保证可以通过查询来判断操作的处理情况。 为了保证操作的可查询,需要对于每一个服务的每一次调用都有一个全局唯一的标识,可以是业务单据号(如订单号)、也可以是系统分配的操作流水号(如支付记录流水号)。除此之外,操作的时间信息也要有完整的记录。 幂等操作 幂等性,其实是一个数学概念。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数,如: f(f(x)) = f(x) 在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。也就是说,同一个方法,使用同样的参数,调用多次产生的业务结果与调用一次产生的业务结果相同。 这一个要求其实也比较好理解,因为要保证数据的最终一致性,很多解决防范都会有很多重试的操作,如果一个方法不保证幂等,那么将无法被重试。 幂等操作的实现方式有多种,如在系统中缓存所有的请求与处理结果、检测到重复操作后,直接返回上一次的处理结果等。 可补偿操作 提到事务,为了保证原子性,就可能发生commit和rollback,那么在分布式事务中,要想进行rollback,就需要提供可补偿操作。 比如上面的订单处理的例子中,在调用积分服务给积分帐户增加积分操作执行之后,经过分布式事务协调,最终决定回滚整个事务,那么就需要提供一个调用积分服务给积分帐户扣减积分的操作。 并且,补偿操作同时也需要满足幂等性。 ...

March 22, 2026 · 1 min · santu

什么是负载均衡,有哪些常见算法?

典型回答 为了提升web应用的各方面能力,我们一般会把多台机器组成一个集群对外提供服务。然而,我们的网站对外提供的访问入口都是一个的,比如www.hollischuang.com。那么当用户在浏览器输入www.hollischuang.com的时候如何将用户的请求分发到集群中不同的机器上呢,这就是负载均衡在做的事情。 负载均衡(Load Balance),意思是将负载(工作任务,访问请求)进行平衡、分摊到多个操作单元(服务器,组件)上进行执行。是解决高性能,单点故障(高可用),扩展性(水平伸缩)的终极解决方案。 负载均衡服务器在决定将请求转发到具体哪台真实服务器的时候,是通过负载均衡算法来实现的。负载均衡算法,是一个负载均衡服务器的核心。 负载均衡算法可以分为两类:静态负载均衡算法和动态负载均衡算法。 静态负载均衡算法包括:轮询,比率,优先权 动态负载均衡算法包括: 最少连接数,最快响应速度,观察方法,预测法,动态性能分配,动态服务器补充,服务质量,服务类型,规则模式。 扩展知识 负载均衡分类 想要实现负载均衡,其实有很多种做法,在深入介绍负载均衡之前,要先介绍一个概念,那就是OSI七层模型。 OSI是一个开放性的通信系统互连参考模型,他是一个定义得非常好的协议规范。 OSI模型有7层结构,每层都可以有几个子层。 OSI的7层从上到下分别是 7、应用层; 6、表示层; 5、会话层; 4、传输层; 3、网络层; 2、数据链路层; 1、物理层; 其中高层(即7、6、5、4层)定义了应用程序的功能,下面3层(即3、2、1层)主要面向通过网络的端到端的数据流。 在这七层模型种,高层次都是依赖于低层次的。层次越高,使用起来越方便。 我们经常听到的一些和计算机网络有关的概念中: telnet、HTTP、FTP、NFS、SMTP、DNS等属于第七层应用层的概念。 TCP、UDP、SPX等属于第四层传输层的概念。 IP、IPX等属于第三层网络层的概念。 ATM、FDDI等属于第二层数据链路层的概念。 了解了网络协议的七层模型以后,再来看看负载均衡。我们可以很明确的一点是,负载均衡是要在网络传输中做文章的。而要在网络传输过程搞事情,那么这七层模型就势必躲不开。 所以,根据负载均衡技术实现在OSI七层模型的不同层次,是可以给负载均衡分类的。 常见的实现方式中,主要可以在应用层、传输层、网络层和数据传输层做文章。所以,工作在应用层的负载均衡,我们通常称之为七层负载均衡、工作在传输层的我们称之为四层负载均衡。 大致可以分为以下几种,其中最常用的是四层和七层负载均衡: 二层负载均衡 负载均衡服务器对外依然提供一个VIP(虚IP),集群中不同的机器采用相同IP地址,但是机器的MAC地址不一样。当负载均衡服务器接受到请求之后,通过改写报文的目标MAC地址的方式将请求转发到目标机器实现负载均衡。 三层负载均衡 和二层负载均衡类似,负载均衡服务器对外依然提供一个VIP(虚IP),但是集群中不同的机器采用不同的IP地址。当负载均衡服务器接受到请求之后,根据不同的负载均衡算法,通过IP将请求转发至不同的真实服务器。 四层负载均衡 四层负载均衡工作在OSI模型的传输层,由于在传输层,只有TCP/UDP协议,这两种协议中除了包含源IP、目标IP以外,还包含源端口号及目的端口号。四层负载均衡服务器在接受到客户端请求后,以后通过修改数据包的地址信息(IP+端口号)将流量转发到应用服务器。 七层负载均衡 七层负载均衡工作在OSI模型的应用层,应用层协议较多,常用http、radius、dns等。七层负载就可以基于这些协议来负载。这些应用层协议中会包含很多有意义的内容。比如同一个Web服务器的负载均衡,除了根据IP加端口进行负载外,还可根据七层的URL、浏览器类别、语言来决定是否要进行负载均衡。 负载均衡工具 市面上有很多开源的负载均衡的工具或软件,基本都是基于前面提到的方案实现的,大多数是工作在第七层和第四层的。Nginx/LVS/HAProxy是目前使用最广泛的三种负载均衡软件。 LVS LVS(Linux Virtual Server),也就是Linux虚拟服务器, 是一个由章文嵩博士发起的自由软件项目。使用LVS技术要达到的目标是:通过LVS提供的负载均衡技术和Linux操作系统实现一个高性能、高可用的服务器群集,它具有良好可靠性、可扩展性和可操作性。从而以低廉的成本实现最优的服务性能。 LVS主要用来做四层负载均衡。 Nginx Nginx(发音同engine x)是一个网页服务器,它能反向代理HTTP, HTTPS, SMTP, POP3, IMAP的协议链接,以及一个负载均衡器和一个HTTP缓存。 Nginx主要用来做七层负载均衡。 HAProxy HAProxy是一个使用C语言编写的自由及开放源代码软件,其提供高可用性、负载均衡,以及基于TCP和HTTP的应用程序代理。 HAProxy主要用来做七层负载均衡。 常见负载均衡算法 负载均衡服务器在决定将请求转发到具体哪台真实服务器的时候,是通过负载均衡算法来实现的。负载均衡算法,是一个负载均衡服务器的核心。 负载均衡算法可以分为两类:静态负载均衡算法和动态负载均衡算法。 静态负载均衡算法包括:轮询,比率,优先权 动态负载均衡算法包括: 最少连接数,最快响应速度,观察方法,预测法,动态性能分配,动态服务器补充,服务质量,服务类型,规则模式。 轮询(Round Robin):顺序循环将请求一次顺序循环地连接每个服务器。当其中某个服务器发生第二到第7 层的故障,BIG-IP 就把其从顺序循环队列中拿出,不参加下一次的轮询,直到其恢复正常。 比率(Ratio):给每个服务器分配一个加权值为比例,根椐这个比例,把用户的请求分配到每个服务器。当其中某个服务器发生第二到第7 层的故障,BIG-IP 就把其从服务器队列中拿出,不参加下一次的用户请求的分配, 直到其恢复正常。 优先权(Priority):给所有服务器分组,给每个组定义优先权,BIG-IP 用户的请求,分配给优先级最高的服务器组(在同一组内,采用轮询或比率算法,分配用户的请求);当最高优先级中所有服务器出现故障,BIG-IP 才将请求送给次优先级的服务器组。这种方式,实际为用户提供一种热备份的方式。 最少的连接方式(Least Connection):传递新的连接给那些进行最少连接处理的服务器。当其中某个服务器发生第二到第7 层的故障,BIG-IP 就把其从服务器队列中拿出,不参加下一次的用户请求的分配, 直到其恢复正常。 最快模式(Fastest):传递连接给那些响应最快的服务器。当其中某个服务器发生第二到第7 层的故障,BIG-IP 就把其从服务器队列中拿出,不参加下一次的用户请求的分配,直到其恢复正常。 观察模式(Observed):连接数目和响应时间以这两项的最佳平衡为依据为新的请求选择服务器。当其中某个服务器发生第二到第7 层的故障,BIG-IP就把其从服务器队列中拿出,不参加下一次的用户请求的分配,直到其恢复正常。 预测模式(Predictive):BIG-IP利用收集到的服务器当前的性能指标,进行预测分析,选择一台服务器在下一个时间片内,其性能将达到最佳的服务器相应用户的请求。(被BIG-IP 进行检测) 动态性能分配(Dynamic Ratio-APM):BIG-IP 收集到的应用程序和应用服务器的各项性能参数,动态调整流量分配。 动态服务器补充(Dynamic Server Act.):当主服务器群中因故障导致数量减少时,动态地将备份服务器补充至主服务器群。 服务质量(QoS):按不同的优先级对数据流进行分配。 服务类型(ToS): 按不同的服务类型(在Type of Field中标识)负载均衡对数据流进行分配。 规则模式:针对不同的数据流设置导向规则,用户可自行。 以上,就是目前实现负载均衡的主流算法。不同的负载均衡服务器会选择不同的算法。就像电影院和火车站可能会选用不同的引导策略一样。火车站可能会把行李少的旅客分配到一个专门的入口,可能给即将发车的旅客分派到特快入口,手持可扫描车票的用户单独分配到特殊入口等。 ...

March 22, 2026 · 1 min · santu

什么是雪花算法,怎么保证不重复的?

典型回答 雪花算法(Snowflake)是由Twitter研发的一种分布式ID生成算法,它可以生成全局唯一且递增的ID。它的核心思想是将一个64位的ID划分成多个部分,每个部分都有不同的含义,包括时间戳、数据中心标识、机器标识和序列号等。 具体来说,雪花算法生成的ID由以下几个部分组成: 符号位(1bit):预留的符号位,始终为0,占用1位。 时间戳(41bit):精确到毫秒级别,41位的时间戳可以容纳的毫秒数是2的41次幂,一年所使用的毫秒数是:365 * 24 * 60 * 60 * 1000,算下来可以使用69年。 数据中心标识(5bit):可以用来区分不同的数据中心。 机器标识(5bit):可以用来区分不同的机器。 序列号(12bit):可以生成4096个不同的序列号。 基于以上结构,雪花算法在唯一性保证方面就有很多优势: 首先,时间戳位于ID的最高位,保证新生成的ID比旧的ID大,在不同的毫秒内,时间戳肯定不一样。 其次,引入数据中心标识和机器标识,这两个标识位都是可以手动配置的,帮助业务来保证不同的数据中心和机器能生成不同的ID。 还有就是,引入序列号,用来解决同一毫秒内多次生成ID的问题,每次生成ID时序列号都会自增,因此不同的ID在序列号上有区别。 所以,基于时间戳+数据中心标识+机器标识+序列号,就保证了在不同进程中主键的不重复,在相同进程中主键的有序性。 雪花算法之所以被广泛使用,主要是因为他有以下优点: 高性能高可用:生成时不依赖于数据库,完全在内存中生成 高吞吐:每秒钟能生成数百万的自增 ID ID 自增:在单个进程中,生成的ID是自增的,可以用作数据库主键做范围查询。但是需要注意的是,在集群中是没办法保证一定顺序递增的。 SnowFlake 算法的缺点或者限制: 1、在Snowflake算法中,每个节点的机器ID和数据中心ID都是硬编码在代码中的,而且这些ID是全局唯一的。当某个节点出现故障或者需要扩容时,就需要更改其对应的机器ID或数据中心ID,但是这个过程比较麻烦,需要重新编译代码,重新部署系统。还有就是,如果某个节点的机器ID或数据中心ID被设置成了已经被分配的ID,那么就会出现重复的ID,这样会导致系统的错误和异常。 2、Snowflake算法中,需要使用zookeeper来协调各个节点的ID生成,但是ZK的部署其实是有挺大的成本的,并且zookeeper本身也可能成为系统的瓶颈。 3、依赖于系统时间的一致性,如果系统时间被回拨,或者不一致,可能会造成 ID 重复。 扩展知识 时钟回拨问题 ✅什么是雪花算法的时钟回拨问题,如何解决?

March 22, 2026 · 1 min · santu

什么是雪花算法的时钟回拨问题,如何解决?

典型回答 ✅什么是雪花算法,怎么保证不重复的? 上面介绍过雪花算法,他可以旨在生成一个64位的、全局唯一、趋势递增的Long类型ID。雪花算法生成的ID由以下几个部分组成: 符号位(1bit):预留的符号位,始终为0,占用1位。 时间戳(41bit):精确到毫秒级别,41位的时间戳可以容纳的毫秒数是2的41次幂,一年所使用的毫秒数是:365 * 24 * 60 * 60 * 1000,算下来可以使用69年。 数据中心标识(5bit):可以用来区分不同的数据中心。 机器标识(5bit):可以用来区分不同的机器。 序列号(12bit):可以生成4096个不同的序列号。 所谓时钟回拨,是指生成分布式ID的服务器上的系统时间,由于某种原因,突然相比之前的时间发生倒退了。产生始终回拨的主要原因通常包括: 网络时间协议(NTP)同步:这是最常见的原因。操作系统会自动与NTP服务器同步时间,如果本地时间快了,NTP会强制将时间回调。 人工手动修改:运维人员误操作,手动将系统时间改错了。 闰秒调整:极少数情况下,操作系统处理闰秒时可能采用“倒退一秒”的策略。 而当前时间的时间戳作为雪花算法中重要的一部分,一旦时间戳倒退了,那么就意味着单台机器上生成的ID可能会出现不顺序递增的情况,当然, 更加严重的就是发生重复。 (因为对于同一台机器来说,数据中心标识、机器标识可能都是一样的,而如果时间戳又出现了相同的情况,那么一旦序列化也一致了,那么就会出现重复ID,所以时钟回拨会导致重复的概率大大升高。) 解决方案 解决时钟问题,有几种典型的方案: 1、等待法 2、动态切换机器标识或者数据中心标识 3、直接抛异常 4、提前生成一批ID备用 等待法 如果回拨时间非常短(比如几百毫秒左右),我们可以选择等待一小段时间,让系统时间追上来。实现方式就是让当前线程sleep一下,然后再执行。 1 2 3 4 if (currentTimestamp < lastTimestamp) { long offset = lastTimestamp - currentTimestamp; Thread.sleep(offset); // 等待offset毫秒 } 切换法 如果发生时钟回拨,这时候我们可以通过动态切换一下机器标识或者数据中心标识等方式,来避免生成的ID重复。 因为机器ID有5个bit,那么我们就可以提前预留一个bit,当出现回拨时,就把这个一个bit运用上,这样机器ID变了,即使其他的都相同,那么整个生成的结果也就不一样了。 抛异常 这种看似不像方案的方案其实用的也挺多的,比如美团的leaf,百度的UidGenerator也都采用过。 因为时钟回拨并不常见,而且就算发生了,也不代表着生成的ID一定就会重复。所以重复其实是个低概率事件,而且,就算时钟不回拨,雪花算法也有极端情况下会重复(并发极高,在同一台机器,时间戳相同,序列号也刚好相同的情况),所以,遇到重复的情况,直接阻断流程也是一种方案,这样至少能避免数据重复。 美团的 <font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">Leaf</font>方案使用ZooKeeper持久化存储工作节点ID和上次的时间戳。启动时检查时钟回拨,如果回拨时间过长(超过阈值),则启动失败,等待人工处理。 ...

March 22, 2026 · 1 min · santu

留言给博主