LNMP + Redis Sentinel 高可用架构部署手册(续)
新增层级 :Redis 缓存/会话层(一主二从 + 三哨兵自动故障转移)
新增节点:
角色 主机名 IP地址 Redis 主节点 redis-master 192.168.1.40Redis 从节点1 redis-slave1 192.168.1.41Redis 从节点2 redis-slave2 192.168.1.42
十一、Redis 高可用架构设计
11.1 架构原理(Sentinel 哨兵模式)
┌─────────────────┐
│ PHP/Web 应用 │
└────────┬────────┘
│
┌──────────────┼──────────────┐
↓ ↓ ↓
Sentinel:26379 Sentinel:26379 Sentinel:26379
192.168.1.40 192.168.1.41 192.168.1.42
└──────────────┬──────────────┘
↓
"当前 Master 是 192.168.1.40:6379"
│
┌──────────────┼──────────────┐
↓ ↓ ↓
Redis-Master Redis-Slave1 Redis-Slave2
192.168.1.40 192.168.1.41 192.168.1.42
:6379 :6379 :6379
写 + 读 只读 只读
故障自动转移流程(无需人工干预):
- 主观下线 :某个 Sentinel 发现 Master 5 秒内无响应,标记为
SDOWN - 客观下线 :超过半数的 Sentinel(这里 ≥2 个)都发现 Master 挂了,标记为
ODOWN - 选举领头:Sentinel 们投票选出一个"老大"来执行故障转移
- 选新 Master:从两个 Slave 中选出数据最新的一个(依据:优先级 > 复制偏移量 > 运行ID)
- 切换角色 :执行
REPLICAOF NO ONE将选中的 Slave 提升为 Master - 重新指向:通知其他 Slave 改挂到新 Master 下
- 通知客户端:Sentinel 返回新的 Master 地址,PHP 应用自动重连
高可用收益:
- 无单点故障:Master 挂了 10 秒内自动切换,业务无感知
- 读写分离:读请求分散到 3 个节点,写入压力由单节点承担(Sentinel 架构特点)
- 数据安全:AOF + RDB 双持久化,重启不丢数据
十二、Redis 二进制安装(所有 Redis 节点执行)
12.1 基础准备
bash
# 创建用户和目录
useradd -s /sbin/nologin redis
mkdir -p /data/redis/{data,logs,sentinel}
chown -R redis:redis /data/redis
# 安装编译依赖
yum install -y gcc make tcl systemd-devel jemalloc jemalloc-devel
12.2 编译安装 Redis 7.2
bash
cd /usr/local/src
wget https://download.redis.io/releases/redis-7.2.4.tar.gz
tar -zxvf redis-7.2.4.tar.gz
cd redis-7.2.4
# 编译(开启systemd支持,使用jemalloc内存分配器)
make BUILD_TLS=yes USE_SYSTEMD=yes MALLOC=jemalloc -j$(nproc)
make install PREFIX=/usr/local/redis
# 创建软链接
ln -s /usr/local/redis/bin/redis-* /usr/local/bin/
ln -s /usr/local/redis/bin/redis-server /usr/local/bin/redis-server
编译参数解释:
| 参数 | 作用 |
|---|---|
USE_SYSTEMD=yes |
支持 systemd 通知机制,服务启动更可靠 |
MALLOC=jemalloc |
使用 jemalloc 内存分配器,减少 Redis 内存碎片,长期运行更稳定 |
BUILD_TLS=yes |
编译支持 TLS 加密传输(可选,为未来扩展预留) |
12.3 环境变量
bash
echo 'export PATH=/usr/local/redis/bin:$PATH' >> /etc/profile
source /etc/profile
十三、Redis 核心配置详解
13.1 主节点配置(redis-master: 192.168.1.40)
bash
cat > /usr/local/redis/redis.conf << 'EOF'
# ========== 网络绑定 ==========
bind 0.0.0.0
port 6379
protected-mode no # 已设置密码,关闭保护模式
tcp-backlog 511
timeout 0
tcp-keepalive 300
# ========== 进程管理 ==========
daemonize no # systemd管理必须设为no
supervised systemd # 由systemd监管
pidfile /data/redis/redis.pid
loglevel notice
logfile /data/redis/logs/redis.log
dir /data/redis/data
# ========== 安全认证 ==========
requirepass RedisStrongPass123!
masterauth RedisStrongPass123! # 主节点也配,故障切换后它可能变成从节点
# ========== 持久化:RDB 快照 ==========
# 900秒内1次修改、300秒内10次修改、60秒内10000次修改则触发快照
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
# ========== 持久化:AOF 日志(核心!必开)==========
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec # 每秒刷盘一次,性能与安全的最佳平衡
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes # 混合持久化(RDB全量+AOF增量),Redis 4.0+必开
# ========== 内存管理 ==========
maxmemory 2gb # 根据服务器内存调整,建议不超过物理内存50%
maxmemory-policy volatile-lru # 对设置了过期时间的key使用LRU淘汰
maxmemory-samples 5
# ========== 复制相关(主节点也配,为故障转移后做准备)==========
repl-diskless-sync yes # 无磁盘复制,网络好时用
repl-diskless-sync-delay 5
repl-ping-replica-period 10
repl-timeout 60
repl-disable-tcp-nodelay no
# ========== 慢查询日志 ==========
slowlog-log-slower-than 10000 # 超过10毫秒的记录
slowlog-max-len 128
# ========== 客户端连接 ==========
maxclients 10000
EOF
13.2 从节点配置(redis-slave1 和 redis-slave2)
在 192.168.1.41 和 192.168.1.42 上执行,仅增加/修改以下差异项:
bash
cat > /usr/local/redis/redis.conf << 'EOF'
bind 0.0.0.0
port 6379
protected-mode no
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize no
supervised systemd
pidfile /data/redis/redis.pid
loglevel notice
logfile /data/redis/logs/redis.log
dir /data/redis/data
requirepass RedisStrongPass123!
masterauth RedisStrongPass123!
# ========== 从节点专属:指向主节点 ==========
replicaof 192.168.1.40 6379
replica-read-only yes # 从节点只读,防止误写入导致主从不一致
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
maxmemory 2gb
maxmemory-policy volatile-lru
maxmemory-samples 5
repl-diskless-sync yes
repl-diskless-sync-delay 5
repl-ping-replica-period 10
repl-timeout 60
slowlog-log-slower-than 10000
slowlog-max-len 128
maxclients 10000
EOF
核心参数解释:
| 参数 | 所在文件 | 作用 |
|---|---|---|
masterauth |
redis.conf |
从节点连接主节点时的认证密码。踩坑点:主节点也必须配! 故障转移后原主会变成从,没配这个就挂不上 |
replicaof 192.168.1.40 6379 |
redis.conf |
从节点启动后自动向主节点请求同步数据 |
appendonly yes + aof-use-rdb-preamble yes |
redis.conf |
开启混合持久化。重启时先加载RDB全量,再回放AOF增量,恢复速度提升10倍+ |
appendfsync everysec |
redis.conf |
每秒把AOF缓冲刷到磁盘。always最安全但性能暴跌,no最快但可能丢1秒数据 |
maxmemory-policy volatile-lru |
redis.conf |
内存满时,优先淘汰设置了过期时间 且最近最少使用的key。缓存场景的最佳策略 |
protected-mode no |
redis.conf |
关闭保护模式,允许外网连接。但必须配合 requirepass 使用,否则裸奔 |
13.3 持久化策略深度解析
| 模式 | 配置 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| RDB | save 900 1 |
文件紧凑,恢复极快 | 可能丢几分钟数据 | 灾难恢复、备份 |
| AOF | appendonly yes |
最多丢1秒数据 | 文件大,恢复慢 | 数据安全优先 |
| 混合 | aof-use-rdb-preamble yes |
兼得两者优势 | 无 | 生产环境必用 |
十四、Sentinel 哨兵配置(所有 Redis 节点执行)
14.1 哨兵配置文件
bash
cat > /usr/local/redis/sentinel.conf << 'EOF'
# ========== 基础配置 ==========
port 26379
daemonize no
supervised systemd
pidfile /data/redis/sentinel.pid
logfile /data/redis/logs/sentinel.log
dir /data/redis/sentinel
# ========== 监控主节点 ==========
# sentinel monitor <主节点名> <IP> <端口> <判定客观下线所需票数>
# 3个哨兵时,配2表示至少2个哨兵认为主节点挂了才触发切换
sentinel monitor mymaster 192.168.1.40 6379 2
# 主节点认证密码
sentinel auth-pass mymaster RedisStrongPass123!
# 主观下线时间:5秒无响应则认为可能挂了
sentinel down-after-milliseconds mymaster 5000
# 故障转移时,同时向新主节点同步的从节点数量(避免带宽打满)
sentinel parallel-syncs mymaster 1
# 故障转移总超时时间:60秒
sentinel failover-timeout mymaster 60000
# 从节点优先级(数值小的优先被提升为主节点,设为相同则按偏移量选)
sentinel replica-priority mymaster 100
EOF
chown -R redis:redis /usr/local/redis/sentinel.conf
chmod 640 /usr/local/redis/sentinel.conf
⚠️ 踩坑点 :Sentinel 启动后会自动重写 这个配置文件(追加 known-replica、known-sentinel 等信息),所以 /data/redis/sentinel 目录必须对 redis 用户可写。如果权限不对,Sentinel 启动后会异常退出。
14.2 systemd 服务文件
Redis Server 服务(所有节点):
bash
cat > /usr/lib/systemd/system/redis.service << 'EOF'
[Unit]
Description=Redis In-Memory Data Store
After=network.target
[Service]
Type=notify
User=redis
Group=redis
ExecStart=/usr/local/redis/bin/redis-server /usr/local/redis/redis.conf
ExecStop=/usr/local/redis/bin/redis-cli -a RedisStrongPass123! shutdown
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
Redis Sentinel 服务(所有节点):
bash
cat > /usr/lib/systemd/system/redis-sentinel.service << 'EOF'
[Unit]
Description=Redis Sentinel
After=network.target redis.service
Wants=redis.service
[Service]
Type=notify
User=redis
Group=redis
ExecStart=/usr/local/redis/bin/redis-sentinel /usr/local/redis/sentinel.conf
ExecStop=/bin/kill -s TERM $MAINPID
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
14.3 启动服务(所有节点)
bash
# 先启动 Redis Server
systemctl start redis
systemctl enable redis
# 等待3秒,确认主从复制正常
sleep 3
# 再启动 Sentinel
systemctl start redis-sentinel
systemctl enable redis-sentinel
14.4 验证主从 + 哨兵状态
bash
# 1. 查看主从复制状态(在主节点执行)
redis-cli -a RedisStrongPass123! info replication
必须看到:
role:master
connected_slaves:2
slave0:ip=192.168.1.41,port=6379,state=online
slave1:ip=192.168.1.42,port=6379,state=online
bash
# 2. 查看哨兵状态(任意节点执行)
redis-cli -p 26379 sentinel master mymaster
确认输出中有 num-slaves:2、num-other-sentinels:2。
bash
# 3. 手动测试故障转移(可选,生产慎用)
# 在主节点执行:
redis-cli -a RedisStrongPass123! DEBUG SEGFAULT
# 或者 systemctl stop redis
# 在从节点1执行,观察是否被提升为主:
redis-cli -a RedisStrongPass123! info replication
# 等待约 10 秒,应看到 role:master
# 恢复旧主节点:
systemctl start redis
# 它会自动变成从节点挂到新主下
十五、PHP 连接 Redis 高可用
15.1 安装 phpredis 扩展(web-01 和 web-02 执行)
phpredis 是 PHP 连接 Redis 的 C 语言扩展,性能最好,且支持 PHP Session 直接保存到 Redis。
bash
cd /usr/local/src
wget https://pecl.php.net/get/redis-6.0.2.tgz
tar -zxvf redis-6.0.2.tgz
cd redis-6.0.2
/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make -j$(nproc) && make install
15.2 启用扩展
bash
cat >> /usr/local/php/etc/php.ini << 'EOF'
; ========== Redis 扩展 ==========
extension=redis.so
; ========== Session 共享配置 ==========
session.save_handler = redis
; 多实例配置:PHP会自动在多个Redis间做故障检测和简单切换
session.save_path = "tcp://192.168.1.40:6379?auth=RedisStrongPass123!&weight=1&timeout=2&read_timeout=2, tcp://192.168.1.41:6379?auth=RedisStrongPass123!&weight=1&timeout=2&read_timeout=2, tcp://192.168.1.42:6379?auth=RedisStrongPass123!&weight=1&timeout=2&read_timeout=2"
session.gc_maxlifetime = 1440
EOF
# 重启 PHP-FPM
systemctl restart php-fpm
Session 共享原理 :Web-01 和 Web-02 的 PHP Session 全部写入 Redis,用户请求被负载均衡到任意一台 Web 服务器时,都能读取到相同的 Session 数据,实现无状态化。
⚠️ 踩坑点 :session.save_path 中的 auth= 参数在较老版本的 phpredis 中不支持。如果报错,升级 phpredis 到 5.3.0+,或改用下面 predis 方案管理 Session。
15.3 业务代码连接 Redis Sentinel(推荐方案)
PHP 自带的 redis 扩展对 Sentinel 的读写分离支持不够完善。对于业务缓存(非 Session),推荐使用 Predis 库(纯 PHP,Composer 安装),它原生完美支持 Sentinel 自动故障转移。
bash
# 在项目目录下安装 predis
cd /data/www
/usr/local/php/bin/php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
/usr/local/php/bin/php composer-setup.php --install-dir=/usr/local/bin --filename=composer
composer require predis/predis
PHP 业务代码示例:
php
<?php
require '/data/www/vendor/autoload.php';
// 连接 Sentinel 集群,自动感知主从切换
$redis = new Predis\Client([
'tcp://192.168.1.40:26379',
'tcp://192.168.1.41:26379',
'tcp://192.168.1.42:26379',
], [
'replication' => 'sentinel',
'service' => 'mymaster',
'parameters' => [
'password' => 'RedisStrongPass123!',
'database' => 0,
],
]);
// 写操作自动路由到 Master
$redis->set('user:1001:name', '张三');
$redis->expire('user:1001:name', 3600);
// 读操作自动路由到 Slave(负载均衡读请求)
$name = $redis->get('user:1001:name');
// 哈希存储示例
$redis->hset('config:site', 'title', '我的网站');
$redis->hset('config:site', 'logo', '/logo.png');
// 列表(队列)示例
$redis->lpush('queue:email', json_encode(['to' => 'user@example.com', 'subject' => '欢迎']));
?>
Predis + Sentinel 工作机制:
- PHP 连接任意一个 Sentinel(26379端口)
- Sentinel 返回当前 Master 的 IP 和端口
- Predis 自动建立到 Master 的连接执行写操作
- Predis 自动建立到 Slave 的连接执行读操作
- 如果 Master 挂了,Sentinel 选举出新 Master 后,Predis 下次请求会自动重连到新地址
15.4 纯 phpredis 扩展连接 Sentinel(备选)
如果你不想引入 Composer 依赖,可以用 phpredis 5.3.0+ 的 RedisSentinel 类:
php
<?php
// 获取当前 Master 地址
$sentinel = new RedisSentinel('192.168.1.40', 26379, 2.0, 'RedisStrongPass123!');
$master = $sentinel->getMasterAddrByName('mymaster');
// 连接 Master
$redis = new Redis();
$redis->connect($master[0], $master[1]);
$redis->auth('RedisStrongPass123!');
$redis->set('key', 'value');
?>
⚠️ 注意 :这种方式需要自己在代码里处理 Master 切换后的重连逻辑,不如 Predis 自动化。推荐业务层用 Predis,Session 层用 phpredis 的 save_path。
十六、Redis 日志优化
16.1 Redis 日志轮转
Redis 自身不会切割日志,长期运行日志文件会撑爆磁盘:
bash
cat > /etc/logrotate.d/redis << 'EOF'
/data/redis/logs/*.log {
daily
rotate 30
missingok
compress
delaycompress
notifempty
create 0644 redis redis
sharedscripts
postrotate
/bin/kill -SIGUSR1 $(cat /data/redis/redis.pid 2>/dev/null) 2>/dev/null || true
/bin/kill -SIGUSR1 $(cat /data/redis/sentinel.pid 2>/dev/null) 2>/dev/null || true
endscript
}
EOF
16.2 AOF 文件自动重写
Redis 会自动处理 AOF 重写(由 auto-aof-rewrite-percentage 触发),但也可以手动触发:
bash
# 手动触发 AOF 重写(减小AOF文件体积)
redis-cli -a RedisStrongPass123! bgrewriteaof
16.3 大Key和内存监控
bash
# 查看内存使用情况
redis-cli -a RedisStrongPass123! info memory
# 查看每个DB的key数量
redis-cli -a RedisStrongPass123! info keyspace
# 扫描大key(超过1MB的key)
redis-cli -a RedisStrongPass123! --bigkeys
十七、Redis 层踩坑点清单
| 序号 | 踩坑点 | 现象 | 解决方案 |
|---|---|---|---|
| 1 | bind 127.0.0.1 未改 |
从节点 master_link_status: down,Sentinel 无法连接 |
改为 bind 0.0.0.0 |
| 2 | 只配 requirepass 不配 masterauth |
主节点故障恢复后无法作为从节点挂上新主 | 主从节点都配 masterauth |
| 3 | protected-mode yes 且没密码 |
外网连不上,报错 DENIED |
设密码后关保护模式,或只绑定内网IP |
| 4 | appendonly no |
Redis 重启后数据全丢 | 生产环境必须 appendonly yes |
| 5 | 没设 maxmemory |
Redis 吃光内存,触发系统 OOM,可能杀掉 MySQL | 设置 maxmemory 和淘汰策略 |
| 6 | appendfsync always |
写入吞吐量暴跌到几百 QPS | 用 everysec,兼顾安全和性能 |
| 7 | Sentinel 目录不可写 | Sentinel 启动后崩溃,报错 Permission denied |
chown -R redis:redis /data/redis |
| 8 | 防火墙没开 26379 | Sentinel 间无法通信,无法达成客观下线共识 | 开放 26379 和 6379 |
| 9 | down-after-milliseconds 太小 |
网络抖动导致频繁误切换 | 建议 5000ms(5秒) |
| 10 | PHP Session 用 save_path 单节点 |
Redis 主节点挂了,Session 全部丢失,用户全部掉线 | 配置多实例或改用 Predis + Sentinel |
十八、完整架构拓扑与流量走向
用户请求
│
▼
┌────────────────────────┐
│ VIP: 192.168.1.100 │
│ Keepalived (lb-master) │◄──── 故障时漂移到 lb-backup
└───────────┬────────────┘
│
┌───────────┴───────────┐
▼ ▼
Nginx (Web-01) Nginx (Web-02)
PHP-FPM + redis PHP-FPM + redis
│ │
└───────────┬───────────┘
▼
┌─────────────────────────────────────┐
│ Redis Sentinel 集群 (26379) │
│ 192.168.1.40 192.168.1.41 192.168.1.42 │
└──────────────────┬──────────────────┘
│
┌──────────────────┼──────────────────┐
▼ ▼ ▼
Redis-Master Redis-Slave1 Redis-Slave2
192.168.1.40 192.168.1.41 192.168.1.42
:6379 :6379 :6379
写 + 读 只读 只读
│ │ │
└──────────────────┼──────────────────┘
▼
┌────────────────────────┐
│ MySQL 主从复制层 │
│ 192.168.1.30 ←── 192.168.1.31 │
└────────────────────────┘
各层高可用机制总结:
| 层级 | 高可用方案 | 故障切换时间 | 数据一致性 |
|---|---|---|---|
| 接入层 | Keepalived VRRP | 1-3 秒 | 无状态 |
| 负载层 | Nginx 反向代理 + 健康检查 | 实时剔除 | 无状态 |
| 应用层 | 2台 Web 并行 | 实时切换 | Session 存 Redis |
| 缓存层 | Redis Sentinel 一主二从 | 10-30 秒 | 异步复制,可能丢少量数据 |
| 数据层 | MySQL GTID 主从复制 | 手动/脚本分钟级 | 半同步,基本不丢数据 |
十九、最终全链路验证清单
19.1 Redis 基础验证
bash
# 在主节点写入
redis-cli -a RedisStrongPass123! set testkey "hello"
# 在从节点读取(应该能读到)
redis-cli -a RedisStrongPass123! get testkey
# 期望输出: "hello"
# 在从节点尝试写入(应该被拒绝)
redis-cli -a RedisStrongPass123! set testkey2 "world"
# 期望输出: (error) READONLY You can't write against a read only replica.
19.2 Sentinel 故障转移验证
bash
# 在主节点执行,模拟宕机
systemctl stop redis
# 在从节点1执行,等待10秒后检查
redis-cli -a RedisStrongPass123! info replication | grep role
# 期望输出: role:master
# 恢复旧主
systemctl start redis
# 它应该自动变成从节点
19.3 PHP Session 共享验证
bash
# 在 web-01 创建测试文件
cat > /data/www/session_test.php << 'EOF'
<?php
session_start();
$_SESSION['server'] = $_SERVER['SERVER_ADDR'];
$_SESSION['time'] = date('Y-m-d H:i:s');
echo "Session ID: " . session_id() . "<br>";
echo "Server: " . $_SESSION['server'] . "<br>";
echo "Time: " . $_SESSION['time'] . "<br>";
EOF
# 浏览器访问 http://192.168.1.100/session_test.php
# 刷新多次(被负载到不同Web服务器),Session 内容应保持一致
19.4 Predis 业务连接验证
bash
cat > /data/www/redis_test.php << 'EOF'
<?php
require '/data/www/vendor/autoload.php';
try {
$redis = new Predis\Client([
'tcp://192.168.1.40:26379',
'tcp://192.168.1.41:26379',
'tcp://192.168.1.42:26379',
], [
'replication' => 'sentinel',
'service' => 'mymaster',
'parameters' => ['password' => 'RedisStrongPass123!'],
]);
$redis->set('test', 'OK');
echo "Redis连接成功: " . $redis->get('test');
} catch (Exception $e) {
echo "错误: " . $e->getMessage();
}
EOF
curl http://192.168.1.100/redis_test.php
# 期望输出: Redis连接成功: OK
至此,完整的 LNMP + Redis Sentinel 高可用架构部署完毕。
你的系统现在具备:
- 7×24 不间断服务:任意单点故障(LB/Web/Redis/MySQL)都不会导致业务中断
- 会话共享:用户请求被负载到任意 Web 服务器,登录状态不丢失
- 读写分离:Redis 读请求分散到 3 个节点,缓存层无单点瓶颈
- 自动故障恢复:Redis Master 宕机 10 秒内自动选举新主,PHP 应用自动重连
- 数据安全:MySQL 半同步复制 + Redis AOF+RDB 混合持久化,双重保障