在大数据与高并发场景下,单节点 Redis 的容量与可用性已无法满足需求。Redis 通过集群与分布式技术,实现了数据的分片存储与高可用部署,成为分布式系统的核心组件。本文将深入解析 Redis 集群的底层原理、架构模式与实战经验,结合代码示例与最佳实践,帮助开发者构建高性能、高可用的分布式缓存系统。
Redis 集群采用 哈希槽(Hash Slot) 实现数据分片,共有 16384 个槽:
CRC16(key) % 16384
计算哈希槽位置# 查看集群节点与槽分布
redis-cli -c -h 127.0.0.1 -p 7000 CLUSTER NODES
客户端直接与目标节点通信,无需通过代理:
MOVED
或ASK
重定向核心架构:
配置示例:
# 主节点配置(master.conf)
port 6379
bind 0.0.0.0
# 从节点配置(slave.conf)
port 6380
slaveof 127.0.0.1 6379
优缺点:
核心组件:
工作流程:
配置示例:
# sentinel.conf
port 26379
sentinel monitor mymaster 127.0.0.1 6379 2 # 2个哨兵同意才触发故障转移
sentinel down-after-milliseconds mymaster 5000 # 主节点5秒无响应视为下线
sentinel failover-timeout mymaster 10000 # 故障转移超时时间
优缺点:
核心特性:
部署步骤:
# 节点配置(如7000.conf)
port 7000
cluster-enabled yes # 开启集群模式
cluster-config-file nodes.conf # 集群配置文件
cluster-node-timeout 15000 # 节点超时时间
appendonly yes
redis-server 7000.conf
redis-server 7001.conf
...
redis-cli --cluster create \
127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 \
127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
--cluster-replicas 1 # 每个主节点1个从节点
# 查看集群信息
redis-cli -c -h 127.0.0.1 -p 7000 CLUSTER INFO
# 查看节点列表
redis-cli -c -h 127.0.0.1 -p 7000 CLUSTER NODES
# 向集群写入数据(自动路由到对应节点)
redis-cli -c -h 127.0.0.1 -p 7000 SET user:1001 "Alice"
# 读取数据
redis-cli -c -h 127.0.0.1 -p 7000 GET user:1001
# 1. 启动新节点(7006)
redis-server 7006.conf
# 2. 将新节点加入集群
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
# 3. 分配哈希槽(从其他节点迁移部分槽到新节点)
redis-cli --cluster reshard 127.0.0.1:7000
# 模拟主节点(如7000)下线
redis-cli -h 127.0.0.1 -p 7000 SHUTDOWN
# 查看集群状态(7003会自动晋升为主节点)
redis-cli -c -h 127.0.0.1 -p 7001 CLUSTER NODES
# 恢复原主节点(作为从节点加入集群)
redis-server 7000.conf
redis-cli -c -h 127.0.0.1 -p 7000 CLUSTER REPLICATE <新主节点ID>
# Python实现RedLock算法
import redis
import time
import uuid
def acquire_lock(redis_nodes, resource, expiration=10):
lock_value = str(uuid.uuid4()) # 唯一标识锁
start_time = time.time()
# 尝试在多数节点获取锁
acquired_nodes = []
for node in redis_nodes:
if node.set(resource, lock_value, nx=True, ex=expiration):
acquired_nodes.append(node)
# 判断是否在多数节点获取成功
elapsed_time = time.time() - start_time
if len(acquired_nodes) >= (len(redis_nodes) // 2 + 1) and \
elapsed_time < expiration:
return lock_value
else:
# 获取失败,释放已获取的锁
for node in acquired_nodes:
node.delete(resource)
return False
def release_lock(redis_nodes, resource, lock_value):
for node in redis_nodes:
# 使用Lua脚本原子性验证并删除锁
script = """
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end
"""
node.eval(script, 1, resource, lock_value)
// Java实现RedLock算法
import redis.clients.jedis.Jedis;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class RedLock {
private List<Jedis> jedisInstances;
private int quorum; // 法定数量
public RedLock(List<Jedis> jedisInstances) {
this.jedisInstances = jedisInstances;
this.quorum = (jedisInstances.size() / 2) + 1;
}
public String lock(String resource, long expirationTime) {
String lockValue = UUID.randomUUID().toString();
int acquiredCount = 0;
long startTime = System.currentTimeMillis();
// 尝试在所有节点获取锁
for (Jedis jedis : jedisInstances) {
try {
// 使用SET命令的NX和EX选项原子性获取锁
String result = jedis.set(resource, lockValue, "NX", "EX", expirationTime);
if ("OK".equals(result)) {
acquiredCount++;
}
} catch (Exception e) {
// 记录异常但继续尝试其他节点
e.printStackTrace();
}
// 如果已超过过期时间,放弃获取锁
if (System.currentTimeMillis() - startTime > expirationTime) {
break;
}
}
// 判断是否获取多数节点的锁
if (acquiredCount >= quorum) {
return lockValue;
} else {
// 获取失败,释放已获取的锁
unlock(resource, lockValue);
return null;
}
}
public void unlock(String resource, String lockValue) {
String script =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +<