Redis分布式
一、 Redis分布式概述
1. 什么是Redis分布式
Redis分布式是指将Redis数据分布到多个Redis节点上,通过集群方式提供更高性能、更大容量和更好可用性的解决方案。
1.2 为什么需要Redis分布式
数据量增长:单机内存容量有限
高并发需求:单机性能瓶颈
高可用性:避免单点故障
可扩展性:支持业务动态扩容
1.3 Redis分布式方案对比
| 方案 | 优点 | 缺点 | 使用场景 |
|---|---|---|---|
| 主从复制 | 部署简单,读写分离 | 写操作单点,故障需手动切换 | 读多写少,数据备份 |
| Redis Sentinel | 自动故障转移,高可用 | 配置复杂,扩容不便 | 生产环境高可用 |
| Redis Cluster | 数据分片,自动故障转移 | 客户端兼容性要求,迁移复杂 | 大数据量,高并发 |
二、 Redis分布式方案搭建
2.1 主从复制搭建
配置文件示例
主节点 (master.conf):
port 6379
daemonize yes
pidfile /var/run/redis_6379.pid
logfile "/var/log/redis_6379.log"
从节点 (slave.conf):
port 6380
daemonize yes
pidfile /var/run/redis_6380.pid
logfile "/var/log/redis_6380.log"
slaveof 127.0.0.1 6379
启动命令
# 启动主节点
redis-server master.conf
# 启动从节点
redis-server slave.conf
2.2 Redis Sentinel搭建
Sentinel配置文件
sentinel.conf:
port 26379
daemonize yes
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 15000
启动Sentinel
redis-sentinel sentinel.conf
2.3 Redis Cluster搭建 ( 企业级推荐方案首选)
集群节点配置
创建6个节点配置文件(3主3从),端口7000 7001 7002 7003 7004 7005:
node-7000.conf如:
port 7000
cluster-enabled yes
cluster-config-file nodes-7000.conf
cluster-node-timeout 5000
appendonly yes
启动集群节点
for port in 7000 7001 7002 7003 7004 7005; do
redis-server node-${port}.conf &
done
创建集群
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
三、 Redis分布式应用场景
3.1 会话存储(Session Storage)
// 分布式Session存储
$sessionHandler = new RedisSessionHandler($redisCluster);
session_set_save_handler($sessionHandler, true);
session_start();
3.2 缓存集群
热点数据分布式缓存
缓存击穿防护
多级缓存架构
3.3 分布式锁
class RedisDistributedLock {
private $redis;
public function __construct($redis) {
$this->redis = $redis;
}
public function lock($key, $timeout = 10) {
$identifier = uniqid();
$end = time() + $timeout;
while (time() < $end) {
if ($this->redis->set($key, $identifier, ['NX', 'EX' => $timeout])) {
return $identifier;
}
usleep(100000); // 100ms
}
return false;
}
public function unlock($key, $identifier) {
$script = "
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
";
return $this->redis->eval($script, [$key, $identifier], 1);
}
}
3.4 消息队列
// 分布式消息队列
class RedisMessageQueue {
private $redis;
private $queueName;
public function __construct($redis, $queueName) {
$this->redis = $redis;
$this->queueName = $queueName;
}
public function push($message) {
return $this->redis->lpush($this->queueName, json_encode($message));
}
public function pop($timeout = 0) {
$result = $this->redis->brpop($this->queueName, $timeout);
return $result ? json_decode($result[1], true) : null;
}
}
四、PHP中的Redis分布式应用
4.1 安装Redis扩展
# 安装PHP Redis扩展
pecl install redis
4.2连接Redis Cluster
<?php
class RedisClusterManager {
private $cluster;
public function __construct($nodes) {
$this->cluster = new RedisCluster(null, $nodes);
}
public function set($key, $value, $ttl = null) {
return $ttl ?
$this->cluster->setex($key, $ttl, $value) :
$this->cluster->set($key, $value);
}
public function get($key) {
return $this->cluster->get($key);
}
public function mget($keys) {
return $this->cluster->mget($keys);
}
}
// 使用示例
$nodes = [
'127.0.0.1:7000',
'127.0.0.1:7001',
'127.0.0.1:7002'
];
$redisCluster = new RedisClusterManager($nodes);
$redisCluster->set('user:1001', json_encode(['name' => 'John', 'age' => 25]));
4.3 分布式ID生成器
class DistributedIdGenerator {
private $redis;
private $key;
public function __construct($redis, $key = 'distributed:id') {
$this->redis = $redis;
$this->key = $key;
}
public function generate($step = 1) {
return $this->redis->incrby($this->key, $step);
}
public function generateWithTimestamp($prefix = '') {
$timestamp = (int)(microtime(true) * 1000);
$sequence = $this->redis->incr($this->key . ':seq');
return $prefix . $timestamp . str_pad($sequence % 1000, 3, '0', STR_PAD_LEFT);
}
}
4.4 分布式计数器
class DistributedCounter {
private $redis;
public function __construct($redis) {
$this->redis = $redis;
}
public function increment($key, $value = 1, $ttl = 3600) {
$script = "
local current = redis.call('incrby', KEYS[1], ARGV[1])
if current == tonumber(ARGV[1]) then
redis.call('expire', KEYS[1], ARGV[2])
end
return current
";
return $this->redis->eval($script, [$key, $value, $ttl], 1);
}
public function get($key) {
return $this->redis->get($key) ?: 0;
}
}
4.5 分布式限流器
class DistributedRateLimiter {
private $redis;
public function __construct($redis) {
$this->redis = $redis;
}
public function isAllowed($key, $limit, $window) {
$script = "
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = redis.call('GET', key) or 0
if tonumber(current) >= limit then
return 0
else
redis.call('INCR', key)
if tonumber(current) == 0 then
redis.call('EXPIRE', key, window)
end
return 1
end
";
return (bool)$this->redis->eval($script, [$key, $limit, $window], 1);
}
}