* [一、分布式锁](#一分布式锁) * [数据库的唯一索引](#数据库的唯一索引) * [Redis 的 SETNX 指令](#redis-的-setnx-指令) * [Redis 的 RedLock 算法](#redis-的-redlock-算法) * [Zookeeper 的有序节点](#zookeeper-的有序节点) * [二、分布式事务](#二分布式事务) * [本地消息表](#本地消息表) * [2PC](#2pc) * [三、CAP](#三cap) * [一致性](#一致性) * [可用性](#可用性) * [分区容忍性](#分区容忍性) * [权衡](#权衡) * [四、BASE](#四base) * [基本可用](#基本可用) * [软状态](#软状态) * [最终一致性](#最终一致性) * [五、Paxos](#五paxos) * [执行过程](#执行过程) * [约束条件](#约束条件) * [六、Raft](#六raft) * [单个 Candidate 的竞选](#单个-candidate-的竞选) * [多个 Candidate 竞选](#多个-candidate-竞选) * [数据同步](#数据同步) * [参考](#参考) # 一、分布式锁 在单机场景下,可以使用语言的内置锁来实现进程同步。但是在分布式场景下,需要同步的进程可能位于不同的节点上,那么就需要使用分布式锁。 阻塞锁通常使用互斥量来实现: - 互斥量为 0 表示有其它进程在使用锁,此时处于锁定状态; - 互斥量为 1 表示未锁定状态。 1 和 0 可以用一个整型值表示,也可以用某个数据是否存在表示。 ## 数据库的唯一索引 获得锁时向表中插入一条记录,释放锁时删除这条记录。唯一索引可以保证该记录只被插入一次,那么就可以用这个记录是否存在来判断是否存于锁定状态。 存在以下几个问题: - 锁没有失效时间,解锁失败的话其它进程无法再获得该锁。 - 只能是非阻塞锁,插入失败直接就报错了,无法重试。 - 不可重入,已经获得锁的进程也必须重新获取锁。 ## Redis 的 SETNX 指令 使用 SETNX(set if not exist)指令插入一个键值对,如果 Key 已经存在,那么会返回 False,否则插入成功并返回 True。 SETNX 指令和数据库的唯一索引类似,保证了只存在一个 Key 的键值对,那么可以用一个 Key 的键值对是否存在来判断是否存于锁定状态。 EXPIRE 指令可以为一个键值对设置一个过期时间,从而避免了数据库唯一索引实现方式中释放锁失败的问题。 ## Redis 的 RedLock 算法 使用了多个 Redis 实例来实现分布式锁,这是为了保证在发生单点故障时仍然可用。 - 尝试从 N 个相互独立 Redis 实例获取锁; - 计算获取锁消耗的时间,只有当这个时间小于锁的过期时间,并且从大多数(N / 2 + 1)实例上获取了锁,那么就认为锁获取成功了; - 如果锁获取失败,就到每个实例上释放锁。 ## Zookeeper 的有序节点 ### 1. Zookeeper 抽象模型 Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点的父节点为 /app1。