数据复制 #

一、复制概述 #

1.1 复制原理 #

text
数据复制架构
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   每个 Range 有多个副本:                                    │
│                                                             │
│   ┌─────────────────────────────────────────────────────┐   │
│   │                  Range 1                             │   │
│   │                                                      │   │
│   │   ┌──────────┐                                      │   │
│   │   │ Leader   │  Replica 1 (Node 1)                  │   │
│   │   │          │  处理读写请求                        │   │
│   │   └──────────┘                                      │   │
│   │        │                                             │   │
│   │        ├─────────────────┐                          │   │
│   │        ▼                 ▼                          │   │
│   │   ┌──────────┐    ┌──────────┐                      │   │
│   │   │Follower  │    │Follower  │                      │   │
│   │   │ Replica 2│    │ Replica 3│                      │   │
│   │   │ (Node 2) │    │ (Node 3) │                      │   │
│   │   └──────────┘    └──────────┘                      │   │
│   │                                                      │   │
│   └─────────────────────────────────────────────────────┘   │
│                                                             │
│   默认副本数: 3                                             │
│   通过 Raft 协议保持一致性                                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

1.2 复制特点 #

特点 说明
强一致性 Raft协议保证
自动故障恢复 自动选举新Leader
可配置副本数 根据需求调整
跨区域复制 支持多区域部署

二、Raft协议 #

2.1 Raft角色 #

text
Raft 角色
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   Leader:                                                   │
│   ├── 处理所有客户端请求                                   │
│   ├── 复制日志到 Followers                                 │
│   └── 一个 Raft Group 只有一个 Leader                      │
│                                                             │
│   Follower:                                                 │
│   ├── 接收 Leader 的日志                                   │
│   ├── 应用日志到状态机                                     │
│   └── 响应 Leader 的心跳                                   │
│                                                             │
│   Candidate:                                                │
│   ├── 选举时的临时角色                                     │
│   ├── 发起投票请求                                         │
│   └── 获得多数票后成为 Leader                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2.2 日志复制 #

text
日志复制流程
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   客户端写入                                                │
│       │                                                     │
│       ▼                                                     │
│   1. Leader 接收请求                                        │
│       │                                                     │
│       ▼                                                     │
│   2. Leader 追加日志到本地                                  │
│       │                                                     │
│       ▼                                                     │
│   3. Leader 复制日志到 Followers                            │
│       │                                                     │
│       │    ┌──────────┐                                     │
│       │    │ Leader   │                                     │
│       │    └────┬─────┘                                     │
│       │         │                                           │
│       │    ┌────┴────┐                                      │
│       │    ▼         ▼                                      │
│       │ ┌──────┐ ┌──────┐                                   │
│       │ │Foll 1│ │Foll 2│                                   │
│       │ └──────┘ └──────┘                                   │
│       │                                                     │
│       ▼                                                     │
│   4. 多数节点确认 (Quorum)                                  │
│       │                                                     │
│       ▼                                                     │
│   5. Leader 提交日志                                        │
│       │                                                     │
│       ▼                                                     │
│   6. 返回客户端成功                                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2.3 Leader选举 #

text
Leader 选举流程
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   触发条件:                                                 │
│   ├── Leader 故障                                          │
│   └── Follower 超时未收到心跳                              │
│                                                             │
│   选举过程:                                                 │
│                                                             │
│   1. Follower 超时,转为 Candidate                         │
│      │                                                      │
│      ▼                                                      │
│   2. Candidate 增加任期号                                   │
│      │                                                      │
│      ▼                                                      │
│   3. 向其他节点发送投票请求                                 │
│      │                                                      │
│      ▼                                                      │
│   4. 收集投票                                               │
│      │                                                      │
│      ├── 多数投票 → 成为 Leader                            │
│      │                                                      │
│      ├── 收到更高任期 → 降为 Follower                      │
│      │                                                      │
│      └── 未获多数 → 重新选举                                │
│                                                             │
│   选举安全:                                                 │
│   ├── 一个任期最多一个 Leader                              │
│   └── 只有最新日志的节点才能当选                           │
│                                                             │
└─────────────────────────────────────────────────────────────┘

三、副本配置 #

3.1 配置副本数 #

sql
-- 查看当前Zone配置
SHOW ZONE CONFIGURATION FOR RANGE default;

-- 配置默认副本数
ALTER RANGE default CONFIGURE ZONE USING
    num_replicas = 5;

-- 配置数据库副本数
ALTER DATABASE mydb CONFIGURE ZONE USING
    num_replicas = 5;

-- 配置表副本数
ALTER TABLE users CONFIGURE ZONE USING
    num_replicas = 3;

-- 配置索引副本数
ALTER INDEX users@idx_email CONFIGURE ZONE USING
    num_replicas = 3;

3.2 副本放置约束 #

sql
-- 配置区域约束
ALTER DATABASE mydb CONFIGURE ZONE USING
    constraints = '[+region=us-east]';

-- 配置多区域约束
ALTER DATABASE mydb CONFIGURE ZONE USING
    constraints = '[+region=us-east]',
    replica_constraints = '[[+region=us-west], [+region=eu-west]]';

-- 查看Zone配置
SHOW ZONE CONFIGURATION FOR DATABASE mydb;

3.3 副本分布查看 #

sql
-- 查看Range分布
SELECT 
    range_id,
    start_key,
    end_key,
    replicas,
    lease_holder
FROM crdb_internal.ranges
LIMIT 10;

-- 查看特定表的Range
SHOW RANGES FROM TABLE users;

-- 查看节点上的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;

四、高可用设计 #

4.1 容错能力 #

text
容错能力
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   副本数与容错能力:                                         │
│                                                             │
│   ┌─────────────────────────────────────────────────────┐   │
│   │ 副本数 │ 可容忍故障节点 │ 最小节点数               │   │
│   ├─────────────────────────────────────────────────────┤   │
│   │   3    │       1         │       3                 │   │
│   │   5    │       2         │       5                 │   │
│   │   7    │       3         │       7                 │   │
│   └─────────────────────────────────────────────────────┘   │
│                                                             │
│   Quorum 计算:                                              │
│   Quorum = (副本数 / 2) + 1                                 │
│                                                             │
│   示例:                                                     │
│   3副本: Quorum = 2, 可容忍 1 个故障                       │
│   5副本: Quorum = 3, 可容忍 2 个故障                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

4.2 故障恢复 #

text
故障恢复流程
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   节点故障检测:                                             │
│   ├── 心跳超时                                             │
│   └── Gossip 信息更新                                      │
│                                                             │
│   恢复过程:                                                 │
│                                                             │
│   1. 检测到节点故障                                         │
│      │                                                      │
│      ▼                                                      │
│   2. 受影响的 Range 触发 Leader 选举                        │
│      │                                                      │
│      ▼                                                      │
│   3. 新 Leader 选出                                         │
│      │                                                      │
│      ▼                                                      │
│   4. 添加新副本到其他节点                                   │
│      │                                                      │
│      ▼                                                      │
│   5. 数据同步                                               │
│      │                                                      │
│      ▼                                                      │
│   6. 集群恢复正常                                           │
│                                                             │
└─────────────────────────────────────────────────────────────┘

4.3 多区域部署 #

sql
-- 多区域部署配置
-- 假设有3个区域: us-east, us-west, eu-west

-- 配置数据库放置策略
ALTER DATABASE mydb CONFIGURE ZONE USING
    num_replicas = 5,
    constraints = '[+region=us-east]',
    replica_constraints = '[[+region=us-west], [+region=eu-west]]';

-- 配置特定表的放置策略
ALTER TABLE users CONFIGURE ZONE USING
    num_replicas = 3,
    constraints = '[+region=us-east]',
    replica_constraints = '[[+region=us-west], [+region=eu-west]]';

-- 查看区域分布
SELECT 
    r.range_id,
    r.replicas,
    n.locality
FROM crdb_internal.ranges r
CROSS JOIN UNNEST(r.replicas) AS replica
JOIN crdb_internal.kv_node_status n ON n.node_id = replica
WHERE r.start_key >= '/Table/53'
  AND r.end_key < '/Table/54';

五、副本管理 #

5.1 副本迁移 #

sql
-- 手动触发副本迁移
-- 将Range迁移到特定节点
ALTER RANGE 123 RELOCATE TO [1, 2, 3];

-- 查看迁移状态
SELECT * FROM crdb_internal.ranges
WHERE range_id = 123;

5.2 副本状态查看 #

sql
-- 查看副本状态
SELECT 
    range_id,
    replicas,
    voting_replicas,
    lease_holder,
    lease_holder_locality
FROM crdb_internal.ranges
LIMIT 20;

-- 查看副本不足的Range
SELECT 
    range_id,
    replicas,
    array_length(replicas, 1) AS replica_count
FROM crdb_internal.ranges
WHERE array_length(replicas, 1) < 3;

5.3 租约持有者 #

sql
-- 查看租约持有者分布
SELECT 
    lease_holder,
    COUNT(*) AS range_count
FROM crdb_internal.ranges
GROUP BY lease_holder
ORDER BY range_count DESC;

-- 手动转移租约
ALTER RANGE 123 TRANSFER LEASE TO 2;

六、读写一致性 #

6.1 一致性级别 #

text
读写一致性级别
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   强一致性读:                                               │
│   ├── 从 Leader 读取                                       │
│   ├── 保证最新数据                                         │
│   └── 延迟较高                                             │
│                                                             │
│   过期读 (Stale Read):                                      │
│   ├── 从 Follower 读取                                     │
│   ├── 可能不是最新数据                                     │
│   └── 延迟较低                                             │
│                                                             │
│   时间旅行查询:                                             │
│   ├── AS OF SYSTEM TIME                                    │
│   ├── 读取历史数据                                         │
│   └── 不阻塞写入                                           │
│                                                             │
└─────────────────────────────────────────────────────────────┘

6.2 过期读配置 #

sql
-- 使用 AS OF SYSTEM TIME
SELECT * FROM users
AS OF SYSTEM TIME '-10s';

-- 使用时间戳
SELECT * FROM users
AS OF SYSTEM TIME 1711555200000000000;

-- 在事务中使用
BEGIN AS OF SYSTEM TIME '-1m';
SELECT * FROM users;
COMMIT;

-- 配置最大过期时间
SET SESSION CHARACTERISTICS AS TRANSACTION
    AS OF SYSTEM TIME '-30s';

七、监控与运维 #

7.1 监控指标 #

sql
-- 查看集群状态
SELECT * FROM crdb_internal.kv_node_status;

-- 查看副本健康状态
SELECT 
    range_id,
    replicas,
    lease_holder,
    CASE 
        WHEN array_length(replicas, 1) >= 3 THEN 'healthy'
        WHEN array_length(replicas, 1) >= 2 THEN 'degraded'
        ELSE 'critical'
    END AS health_status
FROM crdb_internal.ranges;

-- 查看Raft状态
SELECT 
    range_id,
    raft_state
FROM crdb_internal.ranges
LIMIT 10;

7.2 运维操作 #

sql
-- 添加节点后重新均衡
-- CockroachDB 自动均衡,无需手动操作

-- 移除节点
-- 先安全下线
cockroach node drain --host=<node-host>:26257

-- 然后移除
cockroach node decommission <node-id> --host=<any-node>:26257

-- 查看下线状态
SELECT * FROM crdb_internal.kv_node_status
WHERE draining = true;

八、总结 #

数据复制要点:

特性 说明
Raft协议 保证强一致性
副本管理 自动分布和均衡
高可用 自动故障恢复
多区域 支持跨区域部署
一致性 强一致和过期读

下一步,让我们学习数据分区!

最后更新:2026-03-27