在单机场景下,可以使用语言的内置锁来实现进程同步。但是在分布式场景下,需要同步的进程可能位于不同的节点上,那么就需要使用分布式锁。
阻塞锁通常使用互斥量来实现:
互斥量为 0 表示有其它进程在使用锁,此时处于锁定状态;互斥量为 1 表示未锁定状态。1 和 0 可以用一个整型值表示,也可以用某个数据是否存在表示。
• Redis 的 SETNX 指令
• Redis 的 RedLock 算法
• Zookeeper 的有序节点
指事务的操作位于不同的节点上,需要保证事务的 AICD 特性。例如在下单场景下,库存和订单如果本地消息表与业务数据表处于同一个数据库中,这样就能利用本地事务来保证在对这两个表的操作满足事务特性,并且使用了消息队列来保证最终一致性。
本地消息表与业务数据表处于同一个数据库中,这样就能利用本地事务来保证在对这两个表的操作满足事务特性,并且使用了消息队列来保证最终一致性。
• 在分布式事务操作的一方完成写业务数据的操作之后向本地消息表发送一个消息,本地事务能保证这个消息一定会被写入本地消息表中。
• 之后将本地消息表中的消息转发到 Kafka 等消息队列中,如果转发成功则将消息从本地消息表中删除,否则继续重新转发。
• 在分布式事务操作的另一方从消息队列中读取一个消息,并执行消息中的操作。不在同一个节点上,就涉及分布式事务。


分布式系统不可能同时满足一致性(C:Consistency)、可用性(A:Availability)和分区容忍性(P:Partition Tolerance),最多只能同时满足其中两项。

在分布式系统中,分区容忍性必不可少,因为需要总是假设网络是不可靠的。因此,CAP 理论实际上是要在可用性和一致性之间做权衡:可用性和一致性往往是冲突的,很难使它们同时满足。在多个节点之间进行数据同步时,
• 为了保证一致性(CP),就需要让所有节点下线成为不可用的状态,等待同步完成;
• 为了保证可用性(AP),在同步过程中允许读取所有节点的数据,但是数据可能不一致。

BASE 是基本可用(Basically Available)、软状态(Soft State)和最终一致性(Eventually Consistent)三个短语的缩写。
BASE 理论是对 CAP 中一致性和可用性权衡的结果,它的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。

基本可用:指分布式系统在出现故障的时候,保证核心可用,允许损失部分可用性。例如,电商在做促销时,为了保证购物系统的稳定性,部分消费者可能会被引导到一个降级的页面。
软状态:指允许系统中的数据存在中间状态,并认为该中间状态不会影响系统整体可用性,即允许系统不同节点的数据副本之间进行同步的过程存在时延。
最终一致性:最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能达到一致的状态。ACID 要求强一致性,通常运用在传统的数据库系统上。而 BASE 要求最终一致性,通过牺牲强一致性来达到可用性,通常运用在大型分布式系统中。在实际的分布式场景中,不同业务单元和组件对一致性的要求是不同的,因此 ACID 和 BASE 往往会结合在一起使用。
Paxos 算法通过投票来对写操作进行全局编号,同一时刻,只有一个写操作被批准,同时并发的写操作要去争取选票,只有获得过半数选票的写操作才会被批准(所以永远只会有一个写操作得到批准),其他的写操作竞争失败只好再发起一轮投票,就这样,所有写操作都被严格编号排序。编号严格递增,当一个节点接受了一个编号为 100 的写操作,之后又接受到编号为 99 的写操作(因为网络延迟等很多不可预见原因),它马上能意识到自己数据不一致了,自动停止对外服务并重启同步过程。任何一个节点挂掉都不会影响整个集群的数据一致性(总 2n+1 台,除非挂掉大于 n 台)




zookeeper 作为当今最流行的分布式系统应用协调框架,采用 zab 协议的目标就是建立一个高可用可扩展的分布式数据主备系统。即在任何时刻只要 leader 发生宕机,都能保证分布式系统数据的可靠性和最终一致性。
• 客户端发起一个写操作请求;
• Leader 服务器将客户端的 request 请求转化为事物 proposql 提案,同时为每个 proposal 分配一个全局唯一的 ID,即 ZXID。
• leader 服务器与每个 follower 之间都有一个队列,leader 将消息发送到该队列
• follower 机器从队列中取出消息处理完(写入本地事物日志中)毕后,向leader 服务器发送 ACK 确认。
• leader 服务器收到半数以上的 follower 的 ACK 后,即认为可以发送commit 。
• leader 向所有的 follower 服务器发送 commit 消息。
1、选主:定时器机制
2、数据同步:
ZooKeeper 是一种分布式协调服务,用于管理大型主机。在分布式环境中协调和管理服务是一个复杂的过程。ZooKeeper 通过其简单的架构和 API 解决了这个问题。
ZooKeeper 允许开发人员专注于核心应用程序逻辑,而不必担心应用程序的分布式特性。
ZooKeeper 框架最初是在“Yahoo!”上构建的,用于以简单而稳健的方式访问他们的应用程序。后来,Apache ZooKeeper 成为 Hadoop,HBase 和其他分布式框架使用的有组织服务的标准。例如,Apache HBase 使用 ZooKeeper 跟踪分布式数据的状态。ZK是 Google Chubby 的开源实现。
• JDK 安装
• 下载并解压 ZK 3.4.x
• 配置文件$(ZK)/conf/zoo.cfg

• 启动 ZooKeeper 服务
$ bin/zkServer.sh start


• 启动客户端
$ bin/zkCli.sh
1、创建 znode(create /FirstZnode “Myfirstzookeeper-app")
2、获取数据(get /FirstZnode)
3、监视 znode 的变化(get /FirstZnode 1)
4、设置数据(set /SecondZnode Data-updated)
5、创建 znode 的子节点(create /FirstZnode/Child1 firstchildren)
6、列出 znode 的子节点(ls /MyFirstZnode)
7、检查状态(stat /FirstZnode)
8、移除/删除 znode(rmr /FirstZnode)
• 停止服务
$ bin/zkServer.sh stop


Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点的父节点为 /app1

• 永久节点:不会因为会话结束或者超时而消失;
• 临时节点:如果会话结束或者超时就会消失;
• 有序节点:会在节点名的后面加一个数字后缀,并且是有序的,例如生成的有序节点为 /lock/node-0000000000,它的下一个有序节点则为/lock/node-0000000001,以此类推。
为一个节点注册监听器,在节点状态发生改变时,会给客户端发送消息。
• 创建一个锁目录 /lock;
• 当一个客户端需要获取锁时,在 /lock 下创建临时的且有序的子节点;
• 客户端获取 /lock 下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁;否则监听自己的前一个子节点,获得子节点的变更通知后重复此步骤直至获得锁;
• 执行业务代码,完成后,删除对应的子节点。
如果一个已经获得锁的会话超时了,因为创建的是临时节点,所以该会话对应的临时节点会被删除,其它会话就可以获得锁了。可以看到,Zookeeper 分布式锁不会出现数据库的唯一索引实现的分布式锁释放锁失败问题。
一个节点未获得锁,只需要监听自己的前一个子节点,这是因为如果监听所有的子节点,那么任意一个子节点状态改变,其它所有子节点都会收到通知(羊群效应),而我们只希望它的后一个子节点收到通知。
在 zookeeper 的文件系统里创建一个目录,即有唯一的 path。在我们使用 tborg无法确定上游程序的部署机器时即可与下游程序约定好 path,通过 path 即能互相探索发现。
程序总是需要配置的,如果程序分散部署在多台机器上,要逐个改变配置就变得困难。好吧,现在把这些配置全部放到 zookeeper 上去,保存在 Zookeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中就好。


所有客户端在它下面创建临时顺序编号目录节点,和选 master 一样,编号最小的获得锁,用完删除,依次方便。
两种类型的队列:
1、 同步队列,当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达。
2、队列按照 FIFO 方式进行入队和出队操作。
第一类,在约定目录下创建临时目录节点,监听节点数目是否是我们要求的数目。
第二类,和分布式锁服务中的控制时序场景基本原理一致,入列有编号,出列按编号。
本文分享就到这里了,了解更多分布式算法与Zookeeper可关注微信公众号“老周扯IT”