分布式层 #
一、分布式层概述 #
1.1 分布式架构 #
text
分布式层架构
┌─────────────────────────────────────────────────────────────┐
│ │
│ 事务层 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Range 分片管理 │ │
│ │ 数据分片 / Range 分裂合并 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Raft 共识协议 │ │
│ │ 副本一致性 / Leader选举 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 副本管理 │ │
│ │ 副本分布 / 故障恢复 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Gossip 协议 │ │
│ │ 节点发现 / 信息传播 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
1.2 核心概念 #
| 概念 | 说明 |
|---|---|
| Range | 数据分片单元 |
| Replica | Range的副本 |
| Raft | 共识协议 |
| Gossip | 节点通信协议 |
二、Range分片 #
2.1 Range概念 #
text
Range 数据分片
┌─────────────────────────────────────────────────────────────┐
│ │
│ 数据按 Key 范围分片: │
│ │
│ 整个数据空间 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Range 1 │ Range 2 │ Range 3 │ Range 4 │ Range 5 │ │
│ │ [a-f) │ [f-m) │ [m-r) │ [r-w) │ [w-z] │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Range 特点: │
│ ├── 默认大小: 512MB │
│ ├── 自动分裂: 超过阈值自动分裂 │
│ ├── 自动合并: 数据减少自动合并 │
│ └── 自动均衡: 自动迁移到不同节点 │
│ │
│ 每个 Range 是一个 Raft Group: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Range 1 │ │
│ │ │ │
│ │ ┌──────────┐ │ │
│ │ │ Leader │ Replica 1 │ │
│ │ └──────────┘ │ │
│ │ │ │ │
│ │ ├─────────────────┐ │ │
│ │ ▼ ▼ │ │
│ │ ┌──────────┐ ┌──────────┐ │ │
│ │ │Follower │ │Follower │ │ │
│ │ │ Replica 2│ │ Replica 3│ │ │
│ │ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
2.2 Range分裂 #
text
Range 分裂
┌─────────────────────────────────────────────────────────────┐
│ │
│ 触发条件: │
│ ├── Range 大小超过阈值 (默认 512MB) │
│ └── 手动触发 │
│ │
│ 分裂过程: │
│ │
│ 分裂前: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Range 1 │ │
│ │ [a-z] │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 分裂后: │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ Range 1 │ │ Range 2 │ │
│ │ [a-m) │ │ [m-z] │ │
│ └─────────────────────┘ └─────────────────────┘ │
│ │
│ 分裂步骤: │
│ 1. 选择分裂点 │
│ 2. 创建新 Range │
│ 3. 分配副本 │
│ 4. 更新元数据 │
│ │
└─────────────────────────────────────────────────────────────┘
2.3 Range合并 #
text
Range 合并
┌─────────────────────────────────────────────────────────────┐
│ │
│ 触发条件: │
│ ├── Range 大小过小 │
│ └── 相邻 Range 都很小 │
│ │
│ 合并过程: │
│ │
│ 合并前: │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ Range 1 │ │ Range 2 │ │
│ │ [a-m) │ │ [m-z] │ │
│ │ (很小) │ │ (很小) │ │
│ └─────────────────────┘ └─────────────────────┘ │
│ │ │
│ ▼ │
│ 合并后: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Range 1 │ │
│ │ [a-z] │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
2.4 查看Range #
sql
-- 查看 Range 分布
SELECT
range_id,
start_key,
end_key,
replicas,
lease_holder
FROM crdb_internal.ranges;
-- 查看特定表的 Range
SHOW RANGES FROM TABLE users;
-- 查看 Range 统计
SELECT
range_id,
round(statistics->>'key_bytes'::numeric / 1024 / 1024, 2) AS size_mb
FROM crdb_internal.ranges
ORDER BY range_id;
三、Raft共识协议 #
3.1 Raft原理 #
text
Raft 共识协议
┌─────────────────────────────────────────────────────────────┐
│ │
│ 角色: │
│ ├── Leader: 处理所有请求 │
│ ├── Follower: 接收 Leader 日志 │
│ └── Candidate: 选举时的候选者 │
│ │
│ 每个 Range 是一个 Raft Group: │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Raft Group │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │ Leader (Node 1) │ │ │
│ │ │ │ │ │
│ │ │ - 处理所有客户端请求 │ │ │
│ │ │ - 复制日志到 Followers │ │ │
│ │ │ - 提交日志 │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌──────────┴──────────┐ │ │
│ │ ▼ ▼ │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ │ │
│ │ │ Follower (Node 2)│ │ Follower (Node 3)│ │ │
│ │ │ │ │ │ │ │
│ │ │ - 接收日志 │ │ - 接收日志 │ │ │
│ │ │ - 应用日志 │ │ - 应用日志 │ │ │
│ │ └──────────────────┘ └──────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
3.2 写入流程 #
text
Raft 写入流程
┌─────────────────────────────────────────────────────────────┐
│ │
│ 客户端写入请求 │
│ │ │
│ ▼ │
│ 1. 客户端发送写入到任意节点 │
│ │ │
│ ▼ │
│ 2. 转发到 Range Leader │
│ │ │
│ ▼ │
│ 3. Leader 追加日志到本地 │
│ │ │
│ ▼ │
│ 4. Leader 复制日志到 Followers │
│ │ │
│ │ ┌──────────┐ │
│ │ │ Leader │ │
│ │ └────┬─────┘ │
│ │ │ │
│ │ ┌────┴────┐ │
│ │ ▼ ▼ │
│ │ ┌──────┐ ┌──────┐ │
│ │ │Foll 1│ │Foll 2│ │
│ │ └──────┘ └──────┘ │
│ │ │
│ ▼ │
│ 5. 多数节点确认 (Quorum) │
│ │ │
│ ▼ │
│ 6. Leader 提交日志 │
│ │ │
│ ▼ │
│ 7. 返回客户端成功 │
│ │
└─────────────────────────────────────────────────────────────┘
3.3 Leader选举 #
text
Leader 选举
┌─────────────────────────────────────────────────────────────┐
│ │
│ 触发条件: │
│ ├── Leader 故障 │
│ └── Follower 超时未收到心跳 │
│ │
│ 选举过程: │
│ │
│ 1. Follower 超时,转为 Candidate │
│ │ │
│ ▼ │
│ 2. Candidate 增加任期号 │
│ │ │
│ ▼ │
│ 3. 向其他节点发送投票请求 │
│ │ │
│ ▼ │
│ 4. 收集投票 │
│ │ │
│ ├── 多数投票 → 成为 Leader │
│ │ │
│ ├── 收到更高任期 → 降为 Follower │
│ │ │
│ └── 未获多数 → 重新选举 │
│ │
│ 选举安全: │
│ ├── 一个任期最多一个 Leader │
│ └── 只有最新日志的节点才能当选 │
│ │
└─────────────────────────────────────────────────────────────┘
四、副本管理 #
4.1 副本分布 #
text
副本分布策略
┌─────────────────────────────────────────────────────────────┐
│ │
│ 默认副本数: 3 │
│ │
│ 分布原则: │
│ ├── 不同节点 │
│ ├── 不同机架 (如配置) │
│ └── 不同区域 (如配置) │
│ │
│ 示例: 5节点集群 │
│ │
│ Node 1 Node 2 Node 3 Node 4 Node 5 │
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │R1 L │ │R1 F │ │R1 F │ │ │ │ │ │
│ │R2 F │ │R2 L │ │R2 F │ │ │ │ │ │
│ │R3 F │ │R3 F │ │R3 L │ │ │ │ │ │
│ │R4 F │ │R4 F │ │ │ │R4 L │ │ │ │
│ │R5 F │ │R5 F │ │ │ │R5 F │ │R5 L │ │
│ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │
│ │
│ L = Leader, F = Follower │
│ │
└─────────────────────────────────────────────────────────────┘
4.2 副本配置 #
sql
-- 配置默认副本数
ALTER DATABASE defaultdb CONFIGURE ZONE USING
num_replicas = 5;
-- 配置特定表的副本数
ALTER TABLE users CONFIGURE ZONE USING
num_replicas = 5;
-- 配置副本放置约束
ALTER DATABASE mydb CONFIGURE ZONE USING
constraints = '[+region=us-east]';
-- 查看Zone配置
SHOW ZONE CONFIGURATION FOR DATABASE defaultdb;
4.3 副本迁移 #
text
副本迁移
┌─────────────────────────────────────────────────────────────┐
│ │
│ 触发条件: │
│ ├── 节点添加 │
│ ├── 节点移除 │
│ ├── 负载不均衡 │
│ └── 存储空间不足 │
│ │
│ 迁移过程: │
│ │
│ 1. 选择目标节点 │
│ │ │
│ ▼ │
│ 2. 添加新副本 (Learner) │
│ │ │
│ ▼ │
│ 3. 同步数据 │
│ │ │
│ ▼ │
│ 4. 新副本追上进度 │
│ │ │
│ ▼ │
│ 5. 新副本成为 Follower │
│ │ │
│ ▼ │
│ 6. 移除旧副本 │
│ │
└─────────────────────────────────────────────────────────────┘
五、Gossip协议 #
5.1 Gossip原理 #
text
Gossip 协议
┌─────────────────────────────────────────────────────────────┐
│ │
│ 用途: │
│ ├── 节点发现 │
│ ├── 信息传播 │
│ └── 集群状态维护 │
│ │
│ 工作原理: │
│ │
│ 每个节点定期随机选择其他节点交换信息: │
│ │
│ Node 1 Node 2 Node 3 │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ │◄──────►│ │◄──────►│ │ │
│ │ │ │ │ │ │ │
│ └─────┘ └─────┘ └─────┘ │
│ ▲ │ │ │
│ │ │ │ │
│ └──────────────┴──────────────┘ │
│ │
│ 传播的信息: │
│ ├── 节点状态 │
│ ├── Range 元数据 │
│ ├── 存储容量 │
│ └── 网络延迟 │
│ │
└─────────────────────────────────────────────────────────────┘
5.2 节点发现 #
bash
# 启动时指定 join 节点
cockroach start --join=node1:26257,node2:26257,node3:26257
# 新节点自动通过 Gossip 发现其他节点
六、负载均衡 #
6.1 自动均衡 #
text
自动负载均衡
┌─────────────────────────────────────────────────────────────┐
│ │
│ 均衡目标: │
│ ├── Range 数量均衡 │
│ ├── Leader 数量均衡 │
│ ├── 存储空间均衡 │
│ └── 负载均衡 │
│ │
│ 均衡过程: │
│ │
│ 1. 监控集群状态 │
│ │ │
│ ▼ │
│ 2. 计算均衡得分 │
│ │ │
│ ▼ │
│ 3. 选择需要迁移的 Range │
│ │ │
│ ▼ │
│ 4. 执行副本迁移 │
│ │ │
│ ▼ │
│ 5. 更新元数据 │
│ │
│ 示例: │
│ Node 1 (100 Ranges) → Node 2 (50 Ranges) │
│ 迁移 25 Ranges 到 Node 2 │
│ │
└─────────────────────────────────────────────────────────────┘
6.2 查看均衡状态 #
sql
-- 查看节点状态
SELECT
node_id,
is_live,
ranges,
replicas,
lease_holder
FROM crdb_internal.kv_node_status;
-- 查看 Range 分布
SELECT
node_id,
COUNT(*) AS range_count
FROM crdb_internal.ranges
CROSS JOIN UNNEST(replicas) AS node_id
GROUP BY node_id
ORDER BY node_id;
七、故障恢复 #
7.1 节点故障 #
text
节点故障处理
┌─────────────────────────────────────────────────────────────┐
│ │
│ 故障检测: │
│ ├── 心跳超时 │
│ └── Gossip 信息更新 │
│ │
│ 恢复过程: │
│ │
│ 1. 检测到节点故障 │
│ │ │
│ ▼ │
│ 2. 受影响的 Range 触发 Leader 选举 │
│ │ │
│ ▼ │
│ 3. 新 Leader 选出 │
│ │ │
│ ▼ │
│ 4. 添加新副本到其他节点 │
│ │ │
│ ▼ │
│ 5. 集群恢复正常 │
│ │
│ 示例: Node 1 故障 │
│ │
│ 故障前: │
│ Node 1 (Leader) Node 2 (Follower) Node 3 (Follower) │
│ │
│ 故障后: │
│ Node 1 (Down) Node 2 (Leader) Node 3 (Follower) │
│ │
│ 恢复后: │
│ Node 4 (Follower) Node 2 (Leader) Node 3 (Follower) │
│ │
└─────────────────────────────────────────────────────────────┘
7.2 数据恢复 #
sql
-- 查看集群健康状态
SELECT * FROM crdb_internal.kv_node_status;
-- 查看副本不足的 Range
SELECT
range_id,
replicas,
voting_replicas
FROM crdb_internal.ranges
WHERE array_length(replicas, 1) < 3;
-- 手动触发副本补充
-- CockroachDB 自动处理
八、总结 #
分布式层要点:
| 组件 | 功能 |
|---|---|
| Range | 数据分片 |
| Raft | 共识协议 |
| Replica | 副本管理 |
| Gossip | 节点通信 |
下一步,让我们学习事务层!
最后更新:2026-03-27