diff --git a/notes/分布式问题分析.md b/notes/分布式问题分析.md index c9852e33..4f019525 100644 --- a/notes/分布式问题分析.md +++ b/notes/分布式问题分析.md @@ -1,29 +1,104 @@ -* [一、分布式事务](#一分布式事务) - * [本地消息](#本地消息) +* [一、分布式锁](#一分布式锁) + * [数据库的唯一索引](#数据库的唯一索引) + * [Redis 的 SETNX 指令](#redis-的-setnx-指令) + * [Redis 的 RedLock 算法](#redis-的-redlock-算法) + * [Zookeeper 的有序节点](#zookeeper-的有序节点) +* [二、分布式事务](#二分布式事务) + * [本地消息表](#本地消息表) * [两阶段提交协议](#两阶段提交协议) -* [二、分布式锁](#二分布式锁) - * [原理](#原理) - * [实现](#实现) * [三、分布式 Session](#三分布式-session) + * [Sticky Sessions](#sticky-sessions) + * [Session Replication](#session-replication) + * [Session Server](#session-server) * [四、负载均衡](#四负载均衡) * [算法](#算法) * [实现](#实现) -* [参考资料](#参考资料) -# 一、分布式事务 +# 一、分布式锁 -指事务的操作位于不同的节点上,需要保证事务的 AICD 特性。例如在下单场景下,库存和订单如果不在同一个节点上,就需要涉及分布式事务。 +在单机场景下,可以使用 Java 提供的内置锁来实现进程同步。但是在分布式场景下,需要同步的进程可能位于不同的节点上,那么就需要使用分布式锁。 -## 本地消息 +阻塞锁通常使用互斥量来实现,互斥量为 1 表示有其它进程在使用锁,此时处于锁定状态,互斥量为 0 表示未锁定状态。1 和 0 可以用一个整型值来存储,也可以用某个数据存在或者不存在来存储,某个数据存在表示互斥量为 1。 + +## 数据库的唯一索引 + +当想要获得锁时,就向表中插入一条记录,释放锁时就删除这条记录。唯一索引可以保证该记录只被插入一次,那么就可以用这个记录是否存在来判断是否存于锁定状态。 + +存在以下几个问题: + +- 锁没有失效时间,解锁失败的话其他线程无法再获得锁。 +- 只能是非阻塞锁,插入失败直接就报错了,无法重试。 +- 不可重入,已经获得锁的进程也必须重新获取锁。 + +## 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。 + +