MD原文档下载:百度网盘
NoSQL 数据库 Redis 服务
一、相关概念
1.1 NoSQL 数据库
NoSQL(Not Only SQL)非关系型数据库
NoSQL 分类:
- 键值型数据库 : Redis、Memcached
- 文档型数据库 : MongoDB
- 列存储数据库 : HBase、Cassandra
- 图数据库 : Neo4j
1.2 Redis 特点
Redis 是键值型数据库的标杆产品
- 内存存储
- 单线程命令执行(无锁竞争)
- 基于 Epoll 实现 IO 多路复用
- 丰富数据类型
1.3 Redis 适用场景
- 缓存
- 会话存储
- 消息队列
- 计数器
- 排行榜
- 分布式锁
- 限流
1.4 Redis 核心数据类型
- String
- Hash
- List
- Set
- ZSet
二、Redis 部署和基础配置
2.1 系统仓库安装
# 查看仓库提供版本
apt list redis
redis/noble-updates,noble-security 5:7.0.15-1ubuntu0.24.04.4 all
# 安装
apt install -y redis-server
# 查看端口监听
ss -ntlp
LISTEN 0 511 127.0.0.1:6379 0.0.0.0:* users:(("redis–server",pid=1797,fd=6))
LISTEN 0 511 [::1]:6379 [::]:* users:(("redis–server",pid=1797,fd=7))
# 卸载
apt purge -y redis*
2.2 官方仓库安装
# 安装官方源
apt-get install lsb-release curl gpg
curl -fsSL https://packages.redis.io/gpg | sudo gpg –dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb noble main" | sudo tee /etc/apt/sources.list.d/redis.list
# 更新软件源
apt update
# 查看仓库提供版本
apt list redis
redis/noble,noble 6:8.10.1-1rl1~noble1 all
# 安装
apt install -y redis-server
# 查看版本信息
redis-cli -v
redis-cli 8.10.1
# 查看相关进程
ps auxf | grep redis | grep -v grep
redis 2730 0.1 1.3 170180 26524 ? Ssl 15:28 0:00 /usr/bin/redis-server 127.0.0.1:6379
# 查看相关线程
pstree -p | grep redis
|-redis-server(2730)-+-{redis-server}(2737)
| |–{redis-server}(2738)
| |–{redis-server}(2739)
| |–{redis-server}(2740)
| |–{redis-server}(2741)
| `-{redis-server}(2742)
# 查看所有的工作线程
ps -L -p 2730
PID LWP TTY TIME CMD
2730 2730 ? 00:00:00 redis-server # 主线程,命令执行线程
2730 2737 ? 00:00:00 iou-sqp-2730 # IO 线程
2730 2738 ? 00:00:00 bio_close_file # BIO 后台线程1 : 异步关闭大文件
2730 2739 ? 00:00:00 bio_aof # BIO 后台线程2 : AOF 刷盘 fsync
2730 2740 ? 00:00:00 bio_lazy_free # BIO 后台线程3 : 大 key 异步删除 unlink
2730 2741 ? 00:00:00 jemalloc_bg_thd # jemalloc 内存分配器后台线程,整理内存碎片
2730 2742 ? 00:00:00 jemalloc_bg_thd # jemalloc 内存分配器后台线程
2.3 源码安装
源码包下载地址: https://download.redis.io/releases/
安装文档 : https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/ubuntu-noble/
# 安装依赖
apt install -y –no-install-recommends ca-certificates wget dpkg-dev gcc g++ libc6-dev libssl-dev make git cmake python3 python3-pip python3-venv python3-dev unzip rsync clang automake autoconf libtool
# 精简版安装依赖
apt install -y –no-install-recommends ca-certificates wget gcc g++ libc6-dev libssl-dev make automake autoconf libtool pkg-config build-essential libssl-dev libjemalloc-dev libsystemd-dev
# 下载解压源码包
version=8.4.0
cd /usr/local/src
wget -O redis-$version.tar.gz http://download.redis.io/releases/redis-$version.tar.gz
tar xvf redis-$version.tar.gz
cd redis-$version
# 编译安装
export BUILD_TLS=no
export BUILD_WITH_MODULES=no
export INSTALL_RUST_TOOLCHAIN=no
export DISABLE_WERRORS=yes
make -j "$(nproc)" all USE_SYSTEMD=yes
# 转移编译后的文件到指定目录,默认为 /usr/local/bin/ 目录
make install
# 查看相关二进制文件
ls -l /usr/local/bin/
-rwxr-xr-x 1 root root 6768520 Aug 25 15:56 redis-benchmark # 压测工具
lrwxrwxrwx 1 root root 12 Aug 25 15:56 redis-check-aof –> redis-server # 日志修复工具
lrwxrwxrwx 1 root root 12 Aug 25 15:56 redis-check-rdb –> redis-server # 快照修复工具
-rwxr-xr-x 1 root root 7800528 Aug 25 15:56 redis-cli # 命令行客户端
lrwxrwxrwx 1 root root 12 Aug 25 15:56 redis-sentinel –> redis-server # 哨兵程序
-rwxr-xr-x 1 root root 19805192 Aug 25 15:56 redis-server # 服务主程序
# 查看生成的服务文件
ls -l utils/*.service
-rw-rw-r– 1 root root 1310 Nov 18 2025 utils/systemd-redis_multiple_servers@.service # 单实例模板
-rw-rw-r– 1 root root 1550 Nov 18 2025 utils/systemd-redis_server.service # 多实例模板
# 使用指定的配置文件测试启动
./src/redis-server redis.conf
# 消除 WARNING
echo 'vm.overcommit_memory = 1' | tee -a /etc/sysctl.conf
# 加载内核参数
sysctl -p
vm.overcommit_memory = 1
# 重新测试启动,不再出现 WARNING
./src/redis-server redis.conf
# 服务启动
# 拷贝编译生成的单实例服务文件模板到系统服务文件目录
cp utils/systemd-redis_server.service /etc/systemd/system/redis.service
# 编辑服务文件
vim /etc/systemd/system/redis.service
# 注释此行
# ExecStart=/usr/local/bin/redis-server –supervised systemd –daemonize no
# 添加此行,指定配置文件启动
ExecStart=/usr/local/bin/redis-server /etc/redis/redis-6379.conf
# 取消以下 5 行的注释
OOMScoreAdjust=-900
PrivateTmp=yes
User=redis
Group=redis
WorkingDirectory=/var/lib/redis
# 创建 redis 用户
useradd -s /sbin/nologin redis
# 创建相关目录
mkdir -p /var/{lib,log,run}/redis /etc/redis
# 拷贝源码目录中的配置文件
cp redis.conf /etc/redis/redis-6379.conf
# 更改目录属主属组
chown -R redis:redis /var/{lib,log,run}/redis /etc/redis
# 修改 redis 配置
vim /etc/redis/redis-6379.conf
# 修改 supervised 的值为 systemd
supervised systemd
# 修改 PID 文件路径
pidfile /var/run/redis/redis_6379.pid
# 启动服务
systemctl daemon-reload && systemctl enable –now redis
systemctl status redis
# 查看监听端口
ss -ntlp
127.0.0.1:6379
[::1]:6379
2.4 多实例服务
# 清理环境
systemctl disable –now redis
rm -f /etc/systemd/system/redis.service
rm -rf /etc/redis/*
rm -rf /var/{log,var,run}/redis
userdel -r redis
# 拷贝编译生成的多实例服务文件模板到系统服务文件目录
cp utils/systemd-redis_multiple_servers@.service /etc/systemd/system/redis@.service
# 编辑服务文件
vim /etc/systemd/system/redis@.service
# 取消以下 5 行的注释
OOMScoreAdjust=-900
PrivateTmp=yes
User=redis
Group=redis
WorkingDirectory=/var/lib/redis
# 模板中的 %i 是 systemd 实例变量,启动时将 @后字符 替换 %i,例如启动 redis@6379, %i 自动变成 6379,对应配置文件就是 /etc/redis/redis_server_6379.conf
# 创建 redis 用户
useradd -s /sbin/nologin redis
# 创建相关目录
mkdir -p /var/{lib,log,run}/redis /etc/redis
# 拷贝源码目录中的配置文件
cp redis.conf /etc/redis/redis_server_6379.conf
cp redis.conf /etc/redis/redis_server_6380.conf
# 更改目录属主属组
chown -R redis:redis /var/{lib,log,run}/redis /etc/redis
# 修改 redis 配置
vim /etc/redis/redis_server_6379.conf
# 指定监听端口,每个实例监听端口不同
port 6379
# 修改 supervised 的值为 systemd
supervised systemd
# 修改 PID 文件路径,不同实例文件名不同
pidfile /var/run/redis/redis_6379.pid
# 启动服务
systemctl daemon-reload && systemctl enable –now redis@6379 redis@6380
systemctl status redis@6379 redis@6380
# 查看监听端口
ss -ntl
127.0.0.1:6379
127.0.0.1:6380
[::1]:6380
[::1]:6379
2.6 单实例脚本安装
vim deploy_redis.sh
#!/bin/bash
# *************************************
# * 功能: Redis 单实例部署脚本
# * 作者:
# * 联系:
# * 版本: 2026-08-25
# *************************************
# ====================== 颜色定义 ======================
RED='\\033[0;31m'
GREEN='\\033[0;32m'
YELLOW='\\033[1;33m'
BLUE='\\033[0;34m'
NC='\\033[0m' # 恢复默认颜色
# ====================== 全局配置 ======================
REDIS_VERSION="8.4.0"
REDIS_URL="https://download.redis.io/releases/redis-${REDIS_VERSION}.tar.gz"
SRC_BASE="/usr/local/src"
REDIS_SRC="${SRC_BASE}/redis-${REDIS_VERSION}"
CONF_DIR="/etc/redis"
REDIS_PORT="6379"
DATA_DIR="/var/lib/redis"
LOG_DIR="/var/log/redis"
RUN_DIR="/var/run/redis"
RUN_USER="redis"
MAIN_CONF="${CONF_DIR}/redis-${REDIS_PORT}.conf"
SYSTEMD_SVC="/etc/systemd/system/redis.service"
# ====================== 工具函数 ======================
info() {
echo -e "${GREEN}[INFO] $1${NC}"
}
warn() {
echo -e "${YELLOW}[WARN] $1${NC}"
}
error() {
echo -e "${RED}[ERROR] $1${NC}"
}
title() {
echo -e "\\n${BLUE}==================================== $1 ====================================${NC}"
}
# ====================== 功能函数 ======================
# 1. 安装编译依赖
install_deps() {
title "安装编译依赖包"
apt update -y
apt install -y –no-install-recommends ca-certificates wget gcc \\
g++ libc6-dev libssl-dev make automake autoconf libtool
apt install -y –no-install-recommends \\
build-essential pkg-config libssl-dev libjemalloc-dev libsystemd-dev
info "基础编译依赖安装完成"
}
# 2. 下载&解压源码
download_extract() {
title "下载并解压 Redis 源码"
mkdir -p ${SRC_BASE}
cd ${SRC_BASE}
if [ ! -f "redis-${REDIS_VERSION}.tar.gz" ]; then
info "开始下载 Redis ${REDIS_VERSION} 源码包"
wget -q ${REDIS_URL}
else
warn "源码包已存在,跳过下载"
fi
tar -xf redis-${REDIS_VERSION}.tar.gz
cd ${REDIS_SRC}
info "源码解压完成,当前目录: $(pwd)"
}
# 3. 编译 Redis(关闭模块/TLS,开启 systemd)
compile_redis() {
title "编译 Redis"
export BUILD_TLS=no
export BUILD_WITH_MODULES=no
export INSTALL_RUST_TOOLCHAIN=no
export DISABLE_WERRORS=yes
make -j "$(nproc)" all USE_SYSTEMD=yes
if [ $? -eq 0 ]; then
info "Redis 编译成功"
else
error "Redis 编译失败,退出脚本"
exit 1
fi
}
# 4. 安装二进制程序
install_bin() {
title "安装 Redis 二进制文件"
make install
}
# 5. 创建运行用户 & 目录
create_user_dir() {
title "创建运行用户与数据目录"
useradd -s /sbin/nologin ${RUN_USER} 2>/dev/null || warn "用户 ${RUN_USER} 已存在"
mkdir -p ${CONF_DIR} ${DATA_DIR} ${LOG_DIR} ${RUN_DIR}
chown -R ${RUN_USER}:${RUN_USER} ${CONF_DIR} ${DATA_DIR} ${LOG_DIR} ${RUN_DIR}
info "用户与目录创建&授权完成"
}
# 6. 初始化配置文件
init_config() {
title "初始化 Redis 配置文件"
cp ${REDIS_SRC}/redis.conf ${MAIN_CONF}
# 适配 systemd、关闭后台运行、修改pid文件
sed -i 's/^# supervised auto/supervised systemd/' ${MAIN_CONF}
sed -i "s|^pidfile.*|pidfile ${RUN_DIR}/redis_${REDIS_PORT}.pid|" ${MAIN_CONF}
info "主配置文件已生成: ${MAIN_CONF}"
}
# 7. 系统内核参数优化
sysctl_optimize() {
title "配置内存 overcommit 参数"
if ! grep -q 'vm.overcommit_memory = 1' /etc/sysctl.conf; then
echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf
fi
sysctl -p >/dev/null 2>&1
info "vm.overcommit_memory=1 已生效"
}
# 8. 配置 systemd 服务
config_systemd() {
title "配置 systemd 系统服务"
cat > ${SYSTEMD_SVC} <<-EOF
[Unit]
Description=Redis Data Structure Server
Documentation=https://redis.io/documentation
Wants=network-online.target
After=network-online.target
[Service]
ExecStart=/usr/local/bin/redis-server ${MAIN_CONF}
LimitNOFILE=10032
NoNewPrivileges=yes
OOMScoreAdjust=-900
PrivateTmp=yes
Type=notify
TimeoutStartSec=infinity
TimeoutStopSec=infinity
UMask=0077
User=${RUN_USER}
Group=${RUN_USER}
WorkingDirectory=${DATA_DIR}
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable redis
info "systemd 服务配置完成,已设置开机自启"
}
# 9. 启动服务 & 验证
start_and_check() {
title "启动 Redis 服务并验证"
systemctl restart redis
sleep 1
if systemctl is-active –quiet redis; then
info "Redis 服务启动成功!"
echo -e "\\n${BLUE}— 服务状态 —${NC}"
systemctl status redis –no-pager
echo -e "\\n${BLUE}— 连通性测试 —${NC}"
redis-cli ping
else
error "Redis 服务启动失败,请检查日志"
systemctl status redis –no-pager
exit 1
fi
}
# ====================== 主执行入口 ======================
main() {
if [ $(id -u) -ne 0 ]; then
error "请使用 root 权限执行此脚本!"
exit 1
fi
info "====== 开始执行 Redis ${REDIS_VERSION} 编译安装 ======"
install_deps
download_extract
compile_redis
install_bin
create_user_dir
init_config
sysctl_optimize
config_systemd
start_and_check
echo -e "\\n${GREEN}==================== 全部安装流程执行完毕 ====================${NC}"
}
# 调用主函数
main
bash deploy_redis.sh
2.7 redis 客户端
# 本地连接无需密码
redis-cli
# 连接后执行命令
redis-cli cmd <选项>
# 远程连接
redis-cli -h <Redis服务器IP> -p <PORT> -a <PASSWORD> –no-auth-warning
# 认证密码登录不警告
export REDISCLI_AUTH="Redis@2026"
redis-cli -h 10.0.0.221
# 测试 redis 连通性
redis-cli ping
PONG
# 查看键值对总数
redis-cli keys *
(empty array)
# 登陆后查看信息
[root@ubuntu-241 17:16:36 ~]#redis-cli
127.0.0.1:6379> info
# 查看服务端信息
127.0.0.1:6379> info server
2.8 配置监听地址和端口
vim /etc/redis/redis-6379.conf
# 监听地址配置,支持多个地址,以 空格 隔开
bind 10.0.0.221 127.0.0.1 -::1
# 监听端口配置
port 6379
# 非交互式修改监听地址
sed -i '/^bind/s/.*/bind 0.0.0.0/' /etc/redis/redis-6379.conf
# 验证
grep ^bind /etc/redis/redis-6379.conf
systemctl restart redis.service
# 查看监听地址端口
ss -ntl
10.0.0.221:6379
# 测试连接,由于未设置密码,且安全模式已打开,此时执行命令会报错
redis-cli -h 10.0.0.221
# 关闭安全模式,生产环境不推荐
protected-mode yes
sed -i '/^protected-mode/s/.*/protected-mode no/' /etc/redis/redis-6379.conf
grep ^protected-mode /etc/redis/redis-6379.conf
systemctl restart redis.service
# 此时再执行命令不再报错
redis-cli -h 10.0.0.221 keys *
(empty array)
2.9 安全配置 – 密码认证
vim /etc/redis/redis-6379.conf
# 设置认证密码
requirepass Redis@2026
systemctl restart redis.service
# 不使用密码认证报错
redis-cli -h 10.0.0.221 keys *
(error) NOAUTH Authentication required.
# 携带密码认证,命令执行成功,但警告不安全
redis-cli -h 10.0.0.221 -a Redis@2026 keys *
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
(empty array)
# 忽略警告
redis-cli -h 10.0.0.221 -a Redis@2026 –no-auth-warning keys *
(empty array)
# 使用密码登录
redis-cli -h 10.0.0.221 -a Redis@2026 –no-auth-warning
# 认证密码登录不警告
export REDISCLI_AUTH="Redis@2026"
redis-cli -h 10.0.0.221 keys *
# 持久生效
echo 'export REDISCLI_AUTH="Redis@2026"' > /etc/profile.d/redis.sh && source /etc/profile.d/redis.sh
2.10 配置运行模式
| daemonize no | Type=notify | 前台运行 | 生产环境 systemd 推荐 |
| daemonize yes | Type=forking | 后台运行 |
2.11 配置日志
vim /etc/redis/redis-6379.conf
# 修改此行指定日志路径
logfile "/var/log/redis/redis-6379.log"
systemctl restart redis.service
# 查看生成的日志文件
ll /var/log/redis/redis-6379.log
-rw——- 1 redis redis 1037 Aug 25 22:31 /var/log/redis/redis-6379.log
# 追踪查看日志
tail -f /var/log/redis/redis-6379.log
日志级别
- debug : 最详细,适合调试
- notice : 适中,记录关键信息,生产环境推荐
- warning : 最少,仅记录警告和错误
# 查看当前日志级别
grep loglevel /etc/redis/redis-6379.conf
loglevel notice
日志写入 syslog
配合 集中式日志管理、ELK 等日志收集系统
vim /etc/redis/redis-6379.conf
logfile syslog
syslog-enabled yes
syslog-ident redis
syslog-facility local0
2.12 数据持久化配置
2.12.1 dir 数据目录
# 查看默认数据目录
grep ^dir /etc/redis/redis-6379.conf
dir ./
默认配置下,数据写入启动命令执行的当前目录:
- 在 /usr/local/src/redis-8.4.0 执行 redis-server ,数据文件会生成在该目录
- 通过 systemd 启动,数据文件会生成在 服务文件中 WorkingDirectoryWorkingDirectory= 指定的路径
路径不固定,因启动方式不同导致数据文件分散,不利于备份和管理
生产环境推荐将其配置为绝对路径
mkdir -p /data/redis
chown -R redis:redis /data/redis
vim /etc/redis/redis-6379.conf
dir /data/redis
systemctl restart redis.service
# 查看数据目录配置
redis-cli CONFIG GET dir
1) "dir"
2) "/data/redis"
# 保存 redis 内数据
redis-cli bgsave
# 查看数据文件
ls /data/redis
dump.rdb
2.12.2 dbfilename 文件名
# 查看默认文件名配置
grep ^dbfilename /etc/redis/redis-6379.conf
dbfilename dump.rdb
# 修改文件名
vim /etc/redis/redis-6379.conf
dbfilename dump-6379.rdb
systemctl restart redis.service
# 保存 redis 内数据
redis-cli bgsave
# 查看数据文件
ls /data/redis
dump-6379.rdb dump.rdb
2.12.3 appendfilename AOF 文件
vim /etc/redis/redis-6379.conf
# 开启 AOF
appendonly yes
systemctl restart redis.service
# 查看 AOF 相关文件
ls /data/redis/appendonlydir/
appendonly.aof.1.base.rdb appendonly.aof.1.incr.aof appendonly.aof.manifest
2.13 动态配置
2.13.1 动态配置原理及注意事项
不重启 redis 服务实现动态修改配置,实际是修改内存中参数,无法持久保存
如果需要持久保存,又不能重启 redis 服务,可以先用 config set 动态修改配置,再执行 CONFIG REWRITE 自动写入配置文件,不重启 redis 服务
不是所有配置都可以动态修改
2.13.2 config 动态配置命令
2.13.2.1 config get 查看配置
# 列出配置文件当前生效的配置项
grep –Ev '^#|^$' /etc/redis/redis.conf
# 连接 redis‑cli
redis‑cli
# 查看当前所有生效的配置项
CONFIG GET *
# 查看端口
config get port
# 查看监听地址
config get bind
# 查看密码
config get requirepass
# 查看内存信息
redis-cli info memory
2.13.2.2 config set 动态修改配置
修改立即生效,但已连接的用户不会立即断开,断开后下次登录生效
# 动态修改端口
config set port 6380
redis-cli config set port 6379
2.13.2.3 CONFIG REWRITE 命令行永久写入配置
将动态配置写入配置文件持久生效
2.13.2.4 综合案例
配置使用最大物理内存为16G
# 动态修改
127.0.0.1:6379> config get maxmemory
1) "maxmemory"
2) "0"
127.0.0.1:6379> config set maxmemory 16gb
OK
127.0.0.1:6379> config get maxmemory
1) "maxmemory"
2) "17179869184"
# 将动态配置写入配置文件
127.0.0.1:6379> CONFIG REWRITE
OK
# 验证
[root@ubuntu-240 ~ ]#grep ^maxmemory /etc/redis/redis.conf
maxmemory 16gb
三、Redis 持久化机制
3.1 RDB 快照
3.1.1 RDB 数据持久化方式
- 关闭服务
- 手动 bgsave
- 定制定时快照规则,周期 bgsave
3.1.2 定时快照规则
# 查看默认定时快照规则
grep '# save' /etc/redis/redis-6379.conf
# save 3600 1 300 100 60 10000
# 规则说明
save 900 1 # 15 分钟(900s)内累计至少 1 个键修改,触发 bgsave
save 300 10 # 5 分钟内累计至少 10 个键修改,触发 bgsave
save 60 10000 # 1 分钟内累计至少 10000 个键修改,触发 bgsave
3.1.3 save 与 bgsave 的区别
save 与 bgsave 都可以把全量内存数据持久化,但执行逻辑,阻塞特性完全不同
- save : 同步阻塞执行,主线程暂停处理所有客户端请求,独自完成内存遍历、磁盘写入
- bgsave : 异步后台执行,主线程调用 fork() 创建子进程,由子进程完成快照写入,子进程写盘阶段主线程正常处理业务,生产环境推荐
3.1.4 RDB 配置示例
vim /etc/redis/redis-6379.conf
# 关闭 AOF
appendonly no
# 数据目录配置
dir /data/redis
# RDB 文件名
dbfilename dump-6379.rdb
# 设置认证密码
requirepass Redis@2026
systemctl restart redis.service
export REDISCLI_AUTH="Redis@2026"
# 手动 bgsave
# 登录 redis,设定数据
redis-cli
set nihao xxxx
keys *
1) "nihao"
# 手动保存
bgsave
# 确认效果
info persistence
...
rdb_changes_since_last_save:194 # 上次 RDB 落盘后,又新增了 194 条数据
rdb_bgsave_in_progress:0 # 代表后台快照进程已经执行完毕,没有正在运行的 bgsave 任务
rdb_last_bgsave_status:ok # 核心标识,上一次 bgsave 执行结果为成功,无报错
rdb_last_bgsave_time_sec:0 # 本次快照耗时小于 1 秒,正常完成
rdb_saves:1 # RDB 快照累计执行次数变为 1 次
rdb_last_cow_size:335872 # 本次 fork 子进程产生的 COW 内存拷贝大小,证明完整走完了 fork + 写 RDB 全流程
# 确认生成的文件
ll /data/redis/dump-6379.rdb
-rw——- 1 redis redis 105 Aug 25 23:34 /data/redis/dump-6379.rdb
# 校验 RDB 文件
redis-check-rdb /data/redis/dump-6379.rdb
[info] 1 keys read # 文件中写入 1 条数据
# 定时快照
vim /etc/redis/redis-6379.conf
# save 3600 1 300 100 60 10000 # 在此行下方添加
save 3600 1
save 300 100
save 60 10000
systemctl restart redis.service
# 验证规则
redis-cli config get save
1) "save"
2) "3600 1 300 100 60 10000"
# 清空 redis 内存中的数据
redis-cli
FLUSHALL
# 保存清空 RDB 文件
bgsave
info persistence
rdb_saves:1
ll /data/redis/dump-6379.rdb
-rw——- 1 redis redis 88 Aug 25 23:49 /data/redis/dump-6379.rdb
# 写入 10200 条数据
for i in {1..10200}; do redis-cli -a Redis@2026 set high$i $i >/dev/null 2>&1; done
# 由于 60 秒内写入超过 10000 条数据,触发 bgsave
ll /data/redis/dump-6379.rdb
-rw——- 1 redis redis 129589 Aug 25 23:50 /data/redis/dump-6379.rdb
# 查看 RDB 相关信息,确认出发了 bgsave
redis-cli info persistence
rdb_saves:2
# 由于在达到 10000 条时立即触发了 bgsave,部分数据并没有持久化成功
redis-check-rdb /data/redis/dump-6379.rdb
[info] 10052 keys read
# 手动触发 bgsave
redis-cli bgsave
# 10200 条数据持久化,说明 bgsave 有概率丢失数据
redis-check-rdb /data/redis/dump-6379.rdb
[info] 10200 keys read
3.2 AOF 增量命令日志
3.2.1 AOF 相关概念
AOF 记录客户端执行的所有写操作命令
默认策略为每秒写入磁盘文件
多条命令按执行顺序追加写入文件
AOF 命令存储格式
*3 # 表示这条命令包含 3 个元素
$3 # 表示后面字符占 3 个字节
SET
$4 # 表示后面字符占 4 个字节
name
$4 # 表示后面字符占 4 个字节
deng
当回放时,若因进程异常崩溃导致文件损坏,Redis 会启动失败
使用redis-check-aof –fix可修复损坏尾部,根据 *3 和 $3 对文件进行校验,如果命令不完整,截断损坏尾部,丢弃这条命令
AOF优缺点
- 优点:
- 数据丢失概率小 : 默认策略 appendfsync everysec 下最多丢失 1 秒写入
- 日志为文本格式,可用于误操作修复
- 准确性高
- 缺点:
- 文件体积远大于 RDB
- 重启恢复慢
- 文件持续膨胀,需要重写 AOF 日志
3.2.2 AOF 相关命令
redis-cli
# 动态开启 AOF
CONFIG SET appendonly yes
# 查看 AOF 持久化运行状态
info persistence
aof_enabled:1 # AOF 是否开启
aof_rewrite_in_progress # 是否正在执行 AOF 后台重写 bgrewriteaof
aof_rewrite_scheduled # 是否有待执行的重写任务
aof_last_bgrewrite_status # 最近一次 AOF 重写结果
aof_rewrites_consecutive_failures # AOF 重写连续失败次数
aof_last_write_status # AOF 刷盘写入状态
aof_current_size:88 # 当前 AOF 文件实时大小(字节)
aof_base_size # 最近一次 AOF 重写完成后 AOF 文件的基准大小
aof_pending_bio_fsync # 后台刷盘任务队列中,等待 fsync 的任务数量
aof_delayed_fsync # 被延迟的 fsync 计数
# AOF 文件损坏修复
redis-check-aof –fix /data/redis/appendonly.aof
# 手动触发 AOF 重写压缩日志(解决文件膨胀)
bgrewriteaof
3.2.3 AOF 持久化配置项
vim /etc/redis/redis-6379.conf
# 持久化目录
dir "/data/redis"
# 启用 AOF
appendonly yes
# AOF 文件名称
appendfilename "appendonly-6379.aof"
# AOF 目录名称
appenddirname "appendonlydir"
# AOF 刷盘策略,everysec 每秒,always 每次写入,no 系统策略(默认 30 秒)
appendfsync everysec
systemctl restart redis.service
3.2.4 AOF 相关文件
# 查看 AOF 相关文件
tree /data/redis/
/data/redis/
├── appendonlydir # AOF 目录
│ ├── appendonly-6379.aof.1.base.rdb # AOF 重写基准 RDB 文件
│ ├── appendonly-6379.aof.1.incr.aof # 增量 AOF 日志文件
│ ├── appendonly-6379.aof.manifest # AOF 清单元数据文件
└── dump-6379.rdb # RDB 二进制快照文件
# 查看 Redis 启动时 AOF 数据加载顺序
cat /data/redis/appendonlydir/appendonly-6379.aof.manifest
file appendonly-6379.aof.1.base.rdb seq 1 type b
file appendonly-6379.aof.1.incr.aof seq 1 type i startoffset 0
3.2.5 Redis 启动时 AOF 数据加载顺序
3.2.6 AOF 测试
# 查看 AOF 文件大小
ls -l /data/redis/appendonlydir/
-rw——- 1 redis redis 88 Aug 26 19:29 appendonly-6379.aof.1.base.rdb
-rw——- 1 redis redis 0 Aug 26 19:29 appendonly-6379.aof.1.incr.aof
-rw——- 1 redis redis 112 Aug 26 19:29 appendonly-6379.aof.manifest
# 登录 redis 写入 5000 条测试数据
redis-cli
EVAL "for i=1,5000 do redis.call('SET','demo_aof_'..i,math.random(1,9999)) end" 0
# 再次查看 AOF 文件大小
ls -l /data/redis/appendonlydir/
-rw——- 1 redis redis 88 Aug 26 19:29 appendonly-6379.aof.1.base.rdb
-rw——- 1 redis redis 213389 Aug 26 19:30 appendonly-6379.aof.1.incr.aof # 数据写入 .aof 文件
-rw——- 1 redis redis 112 Aug 26 19:29 appendonly-6379.aof.manifest
# 手动触发 AOF 重新压缩日志
bgrewriteaof
# 再次查看 AOF 文件大小
ls -l /data/redis/appendonlydir/
total 100
-rw——- 1 redis redis 88920 Aug 26 19:31 appendonly-6379.aof.2.base.rdb # 压缩后数据写入 .rdb 文件,且重复 SET 指令被合并压缩,文件总体积变小
-rw——- 1 redis redis 0 Aug 26 19:31 appendonly-6379.aof.2.incr.aof
-rw——- 1 redis redis 115 Aug 26 19:31 appendonly-6379.aof.manifest
3.2.7 AOF 自动纠错配置
一般不需要修改,默认启用
vim /etc/redis/redis-6379.conf
# 自动处理 aof 文件结尾的异常数据,防止 Redis 启动失败,默认 yes
aof-load-truncated yes
# 开启混合持久化 AOF 模式,写数据之前生成一份基准RBD文件,默认 yes
aof-use-rdb-preamble yes
# 是否在每一条 AOF 写命令前追加时间戳,默认 no
aof-timestamp-enabled no
# AOF 后台重写过程中分段刷盘策略,避免大量数据积压在操作系统页缓存,默认 yes
aof-rewrite-incremental-fsync yes
3.2.8 AOF 异常处理案例
# 环境准备
redis-cli
# 清理数据
FLUSHALL
BGSAVE
BGREWRITEAOF
# 验证
keys *
(empty array)
# 文件已清空
ls -l /data/redis/appendonlydir/
-rw——- 1 redis redis 88 Aug 26 19:44 appendonly-6379.aof.3.base.rdb
-rw——- 1 redis redis 0 Aug 26 19:44 appendonly-6379.aof.3.incr.aof
-rw——- 1 redis redis 115 Aug 26 19:44 appendonly-6379.aof.manifest
# 临时关闭自动容错机制
CONFIG SET aof-load-truncated no
# 确认效果
config get aof-load-truncated
1) "aof-load-truncated"
2) "no"
# 配置文件关闭自动容错机制
sed -i 's/aof-load-truncated yes/aof-load-truncated no/' /etc/redis/redis-6379.conf
# 配置文件验证
grep '^aof-load-truncated' /etc/redis/redis-6379.conf
aof-load-truncated no
systemctl restart redis.service
redis-cli config get aof-load-truncated
1) "aof-load-truncated"
2) "no"
# 增加测试数据
redis-cli
SET test1 111
SET test2 222
SET test3 333
SET test4 444
SET test5 555
# 确认 AOF 新增数据
ls -l /data/redis/appendonlydir/
-rw——- 1 redis redis 88 Aug 26 19:44 appendonly-6379.aof.3.base.rdb
-rw——- 1 redis redis 188 Aug 26 19:49 appendonly-6379.aof.3.incr.aof # 文件体积增加,数据添加成功
-rw——- 1 redis redis 115 Aug 26 19:48 appendonly-6379.aof.manifest
# 设置 AOF .aof 文件变量
AOF_INCR=/data/redis/appendonlydir/appendonly-6379.aof.3.incr.aof
# 获取 .aof 文件总字节数,截掉末尾 100 个字节,使 .aof 文件不完整
truncate -s $(( $(du –sb $AOF_INCR | awk '{print $1}') –100 )) $AOF_INCR
# 验证效果
ls -l /data/redis/appendonlydir/
-rw——- 1 redis redis 88 Aug 26 19:44 appendonly-6379.aof.3.base.rdb
-rw——- 1 redis redis 88 Aug 26 19:52 appendonly-6379.aof.3.incr.aof # 文件截取了 100 字节,剩余 88 个字节
-rw——- 1 redis redis 115 Aug 26 19:48 appendonly-6379.aof.manifest
# 查看 .aof 增量日志文件
cat $AOF_INCR
*2
$6
SELECT
$1
0
*3
$3
SET
$5
test1
$3
111
*3
$3
SET
$5
test2
$3 # 文件到此截止,第二条插入数据命令不完整
# 此时重启 Redis 出现报错
systemctl restart redis
# 查看错误日志
tail /var/log/redis/redis-6379.log
Unexpected end of file reading the append only file appendonly-6379.aof.3.incr.aof. You can: 1) Make a backup of your AOF file, then use ./redis-check-aof –fix <filename.manifest>. 2) Alternatively you can set the 'aof-load-truncated' configuration option to yes and restart the server. # 提示 因为aof的文件异常,无法启动
# 使用官方工具修复 AOF 增量日志
redis-check-aof –fix $AOF_INCR
Start checking Old-Style AOF
0x 54: Expected to read 5 bytes, got 4 bytes
AOF analyzed: filename=/data/redis/appendonlydir/appendonly-6379.aof.3.incr.aof, size=88, ok_up_to=56, ok_up_to_line=19, diff=32
This will shrink the AOF /data/redis/appendonlydir/appendonly-6379.aof.3.incr.aof from 88 bytes, with 32 bytes, to 56 bytes
Continue? [y/N]: y # 输入 y 确认
Successfully truncated AOF /data/redis/appendonlydir/appendonly-6379.aof.3.incr.aof # 修复成功
# 再次启动 Redis ,不再报错
systemctl start redis
# 登录终端,确认效果
redis-cli
keys *
1) "test1" # 只保留了完整的第一条数据,第二条数据命令不完整被丢弃
四、Redis 命令
4.1 redis 客户端命令
# 本地连接无需密码
redis-cli
# 连接后执行命令
redis-cli cmd <选项>
# 远程连接
redis-cli -h <Redis服务器IP> -p <PORT> -a <PASSWORD> –no-auth-warning
# 认证密码登录不警告
export REDISCLI_AUTH="Redis@2026"
redis-cli -h 10.0.0.221
# 测试 redis 连通性
redis-cli ping
PONG
# 查看键值对总数
redis-cli keys *
(empty array)
# 登陆后查看信息
[root@ubuntu-241 17:16:36 ~]#redis-cli
127.0.0.1:6379> info
# 查看服务端信息
127.0.0.1:6379> info server
额外选项
- -n 0-15 : 指定登录的数据库,redis 默认拥有 16 个数据库(0 – 15),不使用默认进入 0 号库
- –raw : 原始输出,取消 Redis 格式化转义,适合脚本读取数据
- -c : 集群模式连接,自动跳转槽位
4.2 全局通用键操作命令
4.2.1 键生命周期管理
4.2.1.1 set 写入单条键值对
语法 : set key value [EX 秒数] [NX|XX]
参数说明:
- key : 键名
- value : 键值
- [EX 秒数] : 可选,写入同时设置过期时间,单位秒
- [NX] : 可选,仅当 key 不存在时才写入
- [XX] : 可选,仅当 key 存在时才覆盖写入值
4.2.1.2 get 查询单条键值
语法 : get key
返回规则
- key 存在 : 返回对应的 value
- key 不存在 : 返回 (nil)
4.2.1.3 exists 判断 key 是否存在
语法 : exists key [key2 key3 …]
返回存在的 key 总个数
keys *
1) "test5"
2) "test1"
3) "test2"
4) "test3"
5) "test4"
exists test1 test2 test3 test 6
(integer) 3
exists test1 test2 test3 test6
(integer) 3
exists test6
(integer) 0
4.2.1.4 del 删除 key
语法 : del key [key2 key3 …]
返回成功删除的 key 数量
4.2.2 设置 key 有效期
4.2.2.1 expire 给已有 key 设置有效期 (秒)
语法 : expire key seconds
返回 1 设置成功, 0 key 不存在,设置失败
4.2.2.2 ttl 查看 key 剩余有效期
语法 : ttl key
返回值说明
- 正整数 : 剩余有效期,单位秒
- -1 : key 存在,永不过期
- -2 : key 不存在
4.2.2.3 persist 移除有效期规则,改为永久有效
语法 : persist key
返回 1 成功, 0 key 永久有效或不存在
# 创建测试 key
127.0.0.1:6379> set verify_code 444456
OK
# 设置有效期
127.0.0.1:6379> expire verify_code 120
(integer) 1
# 查看剩余有效期
127.0.0.1:6379> ttl verify_code
(integer) 107
# 移除有效期规则
127.0.0.1:6379> persist verify_code
(integer) 1
# 查看剩余有效期,返回 -1 说明永久有效
127.0.0.1:6379> ttl verify_code
(integer) -1
# 重新设置有效期
127.0.0.1:6379> expire verify_code 60
(integer) 1
# 查看剩余有效期
127.0.0.1:6379> ttl verify_code
(integer) 53
# 等待有效期结束再次查看有效期,返回 -2 说明 key 不存在
127.0.0.1:6379> ttl verify_code
(integer) -2
# 验证,判断 key 是否存在,返回 0 表示不存在
exists verify_code
(integer) 0
4.2.3 全局遍历所有键
4.2.3.1 keys 全量遍历 (仅测试环境使用)
语法 : keys 匹配通配符
支持通配符:
- * : 匹配任意长度任意字符
- ? : 匹配任意单个字符
# 查询库中所有的 key
127.0.0.1:6379> keys *
1) "test5"
2) "test1"
3) "test2"
4) "test3"
5) "test4"
# 匹配 tes 开头的 key
127.0.0.1:6379> keys tes*
1) "test5"
2) "test1"
3) "test2"
4) "test3"
5) "test4"
# 匹配单个任意字符
127.0.0.1:6379> keys tes?
(empty array)
# 匹配单个任意字符
127.0.0.1:6379> keys test?
1) "test5"
2) "test1"
3) "test2"
4) "test3"
5) "test4"
一次性遍历所有的 key, 阻塞 Redis 主线程,生产环境禁止执行
4.2.3.2 scan 游标迭代分批遍历
语法 : scan 游标 [match 匹配规则] [count 单次扫描数量]
参数说明:
- 游标 : 首次查询固定填 0 ,每次执行会返回新游标,循环传入直到游标返回 0 表示遍历完成
- match : 可选,后跟模糊匹配规则,过滤目标 key
- count : 可选,单词遍历扫描条数,数值必须 > 0
# 准备测试数据
flushall
127.0.0.1:6379> set user1 tom
OK
127.0.0.1:6379> set user2 jack
OK
127.0.0.1:6379> set order1 2026
OK
127.0.0.1:6379> set order2 666
OK
127.0.0.1:6379> set test1 111
OK
# 全量遍历
127.0.0.1:6379> keys *
1) "user2"
2) "test1"
3) "user1"
4) "order1"
5) "order2"
# 规则匹配
127.0.0.1:6379> keys user*
1) "user2"
2) "user1"
# 生产环境安全游标遍历,规则匹配
127.0.0.1:6379> scan 0 match user* count 50
1) "0" # 返回 0 表示已全库遍历完毕
2) 1) "user1"
2) "user2"
# 不做规则匹配游标遍历
127.0.0.1:6379> scan 0 count 50
1) "0"
2) 1) "user1"
2) "order1"
3) "order2"
4) "user2"
5) "test1"
# 首次遍历
127.0.0.1:6379> scan 0 count 2
1) "1" # 游标返回 1 ,非 0 表示未遍历完成,可以继续遍历
2) 1) "user1"
2) "order1"
3) "order2"
# 根据上次游标返回值继续遍历
127.0.0.1:6379> scan 1 count 2
1) "7" # 游标返回非 0 表示未遍历完成,可以继续遍历
2) 1) "user2"
2) "test1"
# 根据上次游标返回值继续遍历
127.0.0.1:6379> scan 7 count 2
1) "0" # 游标返回 0 表示遍历完成
2) (empty array)
4.2.4 清理数据
4.2.4.1 select 切换数据库
语法 : select 数据库编号
数据库编号 : Redis 单机共16个逻辑数据库,编号 0 – 15
客户端连接 Redis 时未指定数据库默认进入 0 号库
# 连接 Redis 未指定数据库默认进入 0 号库
redis-cli
127.0.0.1:6379>
# 切换到 1 号库
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> # 提示符增加 [1] ,表示当前处于 1 号库
4.2.4.2 flushdb 清空当前数据库所有 key
语法 : flushdb [async]
async : 异步后台清理,不阻塞业务,生产环境推荐
# 切换到 1 号库
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]>
# 异步清空 1 号库
127.0.0.1:6379[1]> flushdb async
OK
4.2.4.3 flushall 清空实例所有数据库所有 key
# 异步清空当前实例所有库全部缓存数据
flushall async
4.2.4.4 清理数据案例
127.0.0.1:6379> keys *
1) "user2"
2) "test1"
3) "user1"
4) "order1"
5) "order2"
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> set user2 lisi
OK
127.0.0.1:6379[1]> set order2 10000
OK
127.0.0.1:6379[1]> keys *
1) "user2"
2) "order2"
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> flushdb async
OK
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
1) "user2"
2) "order2"
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> set nihao 111
OK
127.0.0.1:6379> set nihao1 222
OK
127.0.0.1:6379> keys *
1) "nihao1"
2) "nihao"
127.0.0.1:6379> flushall async
OK
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
(empty array)
4.3 各数据类型专属操作命令
4.3.1 string 字符串
4.3.1.1 mset 批量写入多组字符串键值对
语法 : mset key1 value1 key2 value2 key3 value3 …
特点
- 原子性 : 所有 key 全部写入,不存在部分写入
- 重复 key 覆盖 : 传入命令有重复时,后面覆盖前面
- 不判断 key 是否存在,存在直接覆盖
4.3.1.2 mget 批量读取多个 key 的值
语法 : mget key1 key2 key3 …
key 不存在时,对应位置返回 (nil),不影响整体查询
# 批量写入
127.0.0.1:6379> mset id 1001 name oil age 29 score 24445
OK
# 批量查询值
127.0.0.1:6379> mget id name age score email
1) "1001"
2) "oil"
3) "29"
4) "24445"
5) (nil)
4.3.1.3 strlen 获取 值 的字节长度
语法 : strlen key
注意返回的是 值 占用的字节数量,不是字符个数
中文汉字占 3 个字节,数字、字母、符号占 1 个字节
key 不存在时返回 0
# 设置中文键值对
set nihao 你好
# 2 个汉字占 6 个字节
strlen nihao
(integer) 6
4.3.1.4 append 给 值 尾部追加内容
语法 : append key content
说明:
- key 存在时 : 在原有 值 末尾拼接内容,返回拼接后的总字节
- key 不存在时: 创建 key ,写入内容
127.0.0.1:6379> set test hello
OK
127.0.0.1:6379> append test " world"
(integer) 11
127.0.0.1:6379> get test
"hello world"
4.3.1.5 getset 设置新值并返回旧值
语法 : getset key new_value
key 不存在时返回 (nil) , 同时创建 key 写入值
127.0.0.1:6379> set count 20
OK
127.0.0.1:6379> getset count 0
"20"
127.0.0.1:6379> get count
"0"
4.3.1.6 数字自增自减
incr 数字自增 1
语法 : incr key
规则:
- key 存在且为数字 : 原值 +1 , 返回计算后结果
- key 不存在 : 创建 key ,初始值为 0 ,执行后变成 1
- key 存在但不为数字 : 报错
decr 数字自减 1
语法 : decr key
规则类似 incr , 只是每次执行值 -1
incrby 指定步长整数自增
语法 : incrby key 步长整数
新建 key 默认从 0 开始自增
返回运算后结果
decrby 指定步长整数自减
语法 : decrby key 步长整数
新建 key 默认从 0 开始自减
返回运算后结果
# 设定初始值
127.0.0.1:6379> set number 1
OK
# 自增 1
127.0.0.1:6379> incr number
(integer) 2
127.0.0.1:6379> incr number
(integer) 3
127.0.0.1:6379> incr number
(integer) 4
127.0.0.1:6379> incr number
(integer) 5
# 自减 1
127.0.0.1:6379> decr number
(integer) 4
127.0.0.1:6379> decr number
(integer) 3
127.0.0.1:6379> decr number
(integer) 2
127.0.0.1:6379> decr number
(integer) 1
# 指定步长自增
127.0.0.1:6379> incrby number 5
(integer) 6
127.0.0.1:6379> incrby number 5
(integer) 11
# 指定步长自减
127.0.0.1:6379> decrby number 6
(integer) 5
4.3.2 list 列表
4.3.2.1 创建和追加数据
lpush 从左侧 (头部) 推入元素
语法 : lpush key value1 value2 value3 …
规则 :
- key 不存在 : 创建 key ,然后推入元素
- 推入多个值,后面的值更靠左 (头部)
- key 存在 : 从左侧推入新元素,原元素下标随着新元素推入递增
- key 存在但不是 list 数据类型 : 报错
- 返回值 : 推入完成后元素总个数
rpush 从右侧 (尾部) 推入元素
语法 : rpush key value1 value2 value3 …
规则 :
- key 不存在 : 创建 key ,然后推入元素
- 推入多个值,后面的值更靠右 (头部)
- key 存在 : 从右侧推入新元素,原元素下标随着新元素推入递减
- key 存在但不是 list 数据类型 : 报错
- 返回值 : 推入完成后元素总个数
lrange 列表区间查询
语法 : lrange key start stop
根据索引区间截取列表,返回区间所有元素
索引规则:
- 正向索引(从左往右)
- 0 : 列表第 1 个元素 (头部)
- 1 : 列表第 2 个元素
- N : 列表第 N+1 个元素
- 反向索引(从右往左)
- -1 : 最后 1 个元素 (尾部)
- -2 : 倒数第 2 个元素
- -N : 倒数第 N 个元素
lrange key 0 -1 查看列表所有元素
# 右推入元素
127.0.0.1:6379> rpush list_order order1 order2 order3
(integer) 3
# 列出列表所有元素
127.0.0.1:6379> lrange list_order 0 -1
1) "order1"
2) "order2"
3) "order3"
# 左推入单个元素
127.0.0.1:6379> lpush list_order order0
(integer) 4
# 列出列表所有元素
127.0.0.1:6379> lrange list_order 0 -1
1) "order0"
2) "order1"
3) "order2"
4) "order3"
# 左推入多个元素
127.0.0.1:6379> lpush list_order order-1 order-2
(integer) 6
# 列出列表所有元素
127.0.0.1:6379> lrange list_order 0 -1
1) "order-2" # 左推入,后推入的位于头部
2) "order-1"
3) "order0"
4) "order1"
5) "order2"
6) "order3"
4.3.2.2 列表元素操作
llen 获取列表元素个数
语法 : llen key
lindex 根据索引读取单个元素
语法 : lindex key index
lrange 列表区间查询
语法 : lrange key start stop
lset 修改指定索引元素
语法 : lset key index new_value
# 右推入元素
127.0.0.1:6379> rpush ldemo a b c d e f
(integer) 6
# 查看列表元素个数
127.0.0.1:6379> llen ldemo
(integer) 6
# 根据索引读取单个元素
127.0.0.1:6379> lindex ldemo 1
"b"
127.0.0.1:6379> lindex ldemo 0
"a"
127.0.0.1:6379> lindex ldemo 5
"f"
127.0.0.1:6379> lindex ldemo -1
"f"
# 读取错误索引返回 (nil)
127.0.0.1:6379> lindex ldemo 6
(nil)
# 列表区间查询
127.0.0.1:6379> lrange ldemo 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
127.0.0.1:6379> lrange ldemo 1 3
1) "b"
2) "c"
3) "d"
127.0.0.1:6379> lrange ldemo -3 -1
1) "d"
2) "e"
3) "f"
# 列表区间查询超出索引范围不报错,返回索引范围内元素
127.0.0.1:6379> lrange ldemo 5 9
1) "f"
# 根据索引修改指定元素
127.0.0.1:6379> lset ldemo 2 C_update
OK
# 查看列表所有元素
127.0.0.1:6379> lrange ldemo 0 -1
1) "a"
2) "b"
3) "C_update"
4) "d"
5) "e"
6) "f"
# 修改超出索引范围的元素,报错
127.0.0.1:6379> lset ldemo 6 test
(error) ERR index out of range
# 修改不存在的列表,报错
127.0.0.1:6379> lset list 0 test
(error) ERR no such key
4.3.2.3 删除列表元素
lpop / rpop 弹出删除列表头部 / 尾部单个元素
语法 :
- lpop key : 删除并返回头部第一个元素
- rpop key : 删除并返回尾部第一个元素
规则 :
- 列表为空 / key 不存在,返回 (nil)
- 弹出后列表元素个数减 1
lrem 根据值批量删除匹配元素
语法 : lrem key count value
参数说明:
- count > 0 : 从表头开始匹配,删除 count 个匹配 value 的元素
- count < 0 : 从表尾开始匹配,删除 |count| 个匹配 value 的元素
- count = 0 : 删除列表中所有匹配 value 的元素
返回值 : 删除的元素个数
ltrim 区间保留删除
语法 : ltrim key start stop
删除指定区间以外所有元素
# 右推入元素
rpush list_del a b c d e f g h
(integer) 8
# 列出列表所有元素
127.0.0.1:6379> lrange list_del 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
7) "g"
8) "h"
# 左弹出
127.0.0.1:6379> lpop list_del
"a"
127.0.0.1:6379> lrange list_del 0 -1
1) "b"
2) "c"
3) "d"
4) "e"
5) "f"
6) "g"
7) "h"
# 右弹出
127.0.0.1:6379> rpop list_del
"h"
127.0.0.1:6379> lrange list_del 0 -1
1) "b"
2) "c"
3) "d"
4) "e"
5) "f"
6) "g"
# 左弹出不存在列表,返回 (nil)
127.0.0.1:6379> lpop list
(nil)
# 区间保留删除
127.0.0.1:6379> ltrim list_del 1 3
OK
127.0.0.1:6379> lrange list_del 0 -1
1) "c"
2) "d"
3) "e"
# 右推入元素
127.0.0.1:6379> rpush l_del 1 2 3 1 2 3 1 2 3
(integer) 9
127.0.0.1:6379> lrange l_del 0 -1
1) "1"
2) "2"
3) "3"
4) "1"
5) "2"
6) "3"
7) "1"
8) "2"
9) "3"
# 根据值匹配从左开始删除 2 个匹配元素
127.0.0.1:6379> lrem l_del 2 1
(integer) 2
127.0.0.1:6379> lrange l_del 0 -1
1) "2"
2) "3"
3) "2"
4) "3"
5) "1"
6) "2"
7) "3"
# 根据值匹配从右开始删除 2 个匹配元素
127.0.0.1:6379> lrem l_del -2 3
(integer) 2
127.0.0.1:6379> lrange l_del 0 -1
1) "2"
2) "3"
3) "2"
4) "1"
5) "2"
# 根据值匹配删除所有匹配元素
127.0.0.1:6379> lrem l_del 0 2
(integer) 3
127.0.0.1:6379> lrange l_del 0 -1
1) "3"
2) "1"
4.3.3 set 集合
4.3.3.1 集合管理
sadd 创建集合
语法 : sadd key member1 member2 member3 …
特点:
- 集合不存在时自动创建
- 集合元素自动去重
- 返回值 : 本次真正新增的元素个数
smembers 查询集合所有元素
语法 : smembers key
特点:
- 输出无序,每次查询顺序可能不一致
scard 获取集合元素总个数
语法 : scard key
sismember 判断集合是否存在指定元素
语法 : sismember key member
存在 返回 1 ,不存在 / key 不存在 返回 0
srandmember 随机获取集合元素
语法 : srandmember key [count]
不写数量默认获取 1 个,不消耗元素
srem 删除集合指定元素
语法 : srem key member1 member2 …
特点:
- 按值删除,不需要索引
- 返回值 : 成功删除元素个数
- 删除不存在元素不报错
# 创建集合并批量新增元素(包含重复元素)
127.0.0.1:6379> sadd sstu tom jerry jack tom lucy
(integer) 4 # 自动去重,新增 5 个元素,实际去重后只新增 4 个元素
# 查看集合内所有元素
127.0.0.1:6379> smembers sstu
1) "tom"
2) "jerry"
3) "jack"
4) "lucy"
# 查看集合元素总数
127.0.0.1:6379> scard sstu
(integer) 4
# 判断集合是否存在指定元素,包含返回 1 ,不包含返回 0
127.0.0.1:6379> sismember sstu jack
(integer) 1
127.0.0.1:6379> sismember sstu lisi
(integer) 0
# 随机获取元素,不指定数量获取 1 个
127.0.0.1:6379> srandmember sstu
"jack"
127.0.0.1:6379> srandmember sstu 2
1) "jerry"
2) "jack"
# 删除集合指定元素
127.0.0.1:6379> srem sstu jack lucy
(integer) 2
# 验证
127.0.0.1:6379> smembers sstu
1) "tom"
2) "jerry"
4.3.3.2 集合运算: 交集、并集、差集
sinter 求集合交集
语法 : sinter key1 key2 key3 …
取出所有集合共同拥有的元素
sunion 求集合并集
语法 : sunion key1 key2 key3 …
合并所有集合所有元素并去重
sdiff 求集合差集
语法 : sdiff key1 key2 key3 …
以第一个集合为基准,剔除后面集合中存在的元素
即保留第一个集合独有的元素
# 创建集合并批量插入元素
127.0.0.1:6379> sadd class1 tom jerry jack lucy
(integer) 4
127.0.0.1:6379> sadd class2 jack lucy mike lisa
(integer) 4
# 求集合交集
127.0.0.1:6379> sinter class1 class2
1) "jack"
2) "lucy"
# 求集合并集
127.0.0.1:6379> sunion class1 class2
1) "lucy"
2) "tom"
3) "lisa"
4) "jack"
5) "mike"
6) "jerry"
# 求第一个集合独有的元素,差集
127.0.0.1:6379> sdiff class1 class2
1) "tom"
2) "jerry"
127.0.0.1:6379> sdiff class2 class1
1) "mike"
2) "lisa"
4.3.4 sorted set 有序集合
4.3.4.1 zadd 创建有序集合
语法 : zadd key score1 member1 score2 member2 score3 memeber3 …
特点 :
- 重复元素覆盖原有 score分数
- score 支持整数、小数、负数
- 集合默认按从小到大升序排序
- 返回值 : 新增的元素个数,重复元素更新分数不计入新增
# 创建有序集合,批量添加分数和元素
127.0.0.1:6379> zadd ztest 85 wang 70 liu 92 hou 67 li
(integer) 4
# 查看集合所有元素
127.0.0.1:6379> zrange ztest 0 -1
1) "li"
2) "liu"
3) "wang"
4) "hou"
# 查看集合所有元素及对应分数,默认按从小到大升序排序
127.0.0.1:6379> zrange ztest 0 -1 withscores
1) "li"
2) "67"
3) "liu"
4) "70"
5) "wang"
6) "85"
7) "hou"
8) "92"
# 重复元素更新分数
127.0.0.1:6379> zadd ztest 88 wang
(integer) 0 # 元素已存在,仅更新分数,无新增元素
# 确认效果
127.0.0.1:6379> zrange ztest 0 -1 withscores
1) "li"
2) "67"
3) "liu"
4) "70"
5) "wang"
6) "88" # 分数更新
7) "hou"
8) "92"
4.3.4.2 查看有序集合
zrange 按索引区间升序查看元素
语法 : zrange key start stop [withscores]
参数说明:
- start stop : 元素索引编号,0 -1 表示全部成员
- withscores : 返回成员对应的分数
zrevrange 按索引区间降序查看元素
语法 : zrevrange key start stop [withscores]
特点 :
- 按分数从大到小降序输出,可用于排行榜
zscore 根据元素查询对应分数
语法 : zscore key member
zrank 查看元素升序排名
语法 : zrank key member
zrevrank 查看元素降序排名
语法 : zrevrank key member
zcard 查看有序集合元素个数
语法 : zcard key
用于统计排行榜总人数
4.3.4.3 删除有序集合元素
zrem 根据元素删除
语法 : zrem key member1 member2 member3 …
特点 :
- 根据成员名称精准删除
- 返回值 : 成功删除的元素个数
zremrangebyrank 按索引区间删除元素
语法 : zremrangebyrank key start stop
特点 :
- 根据升序索引区间批量删除
- 0 -1 清除所有元素
- 返回值 : 成功删除的元素个数
zremrangebyscore 按分数区间删除元素
语法 : zremrangebyscore key min max
特点 :
- 删除分数在 min ~ max 区间内的所有元素
- 包含 min 和 max 边界值
4.3.5 hash 哈希
4.3.5.1 hset 创建哈希 / 单个 / 批量设置字段
语法 : hset key field1 value1 field2 value2 field3 value3 …
特点 :
- 字段 field 存在则覆盖原值
- 字段不存在则新增字段
- 返回值 : 新增的字段数量
4.3.5.2 查看哈希
hget 查看单个字段的值
语法 : hget key field
hmget 批量查询多个字段的值
语法 : hmget key field1 field2 field3 …
hgetall 查询全部字段 和 值
语法 : hgetall key
hkeys 查询所有字段名
语法 : hkeys key
仅返回所有 field 字段名称
hvals 查询所有字段值
语法 : hvals key
hlen 获取字段总数
语法 : hlen key
# 创建哈希并批量设置字段
127.0.0.1:6379> hset user1 name lisi age 27 gender male address shanghai
(integer) 4
# 获取字段总数
127.0.0.1:6379> hlen user1
(integer) 4
# 查看单个字段的值
127.0.0.1:6379> hget user1 name
"lisi"
# 批量查询多个字段的值,不存在返回 (nil)
127.0.0.1:6379> hmget user1 name age phone
1) "lisi"
2) "27"
3) (nil)
# 查询所有字段名
127.0.0.1:6379> hkeys user1
1) "name"
2) "age"
3) "gender"
4) "address"
# 查询所有字段值
127.0.0.1:6379> hvals user1
1) "lisi"
2) "27"
3) "male"
4) "shanghai"
# 查询所有字段名和值
127.0.0.1:6379> hgetall user1
1) "name"
2) "lisi"
3) "age"
4) "27"
5) "gender"
6) "male"
7) "address"
8) "shanghai"
4.3.5.3 删除哈希
hdel 删除哈希内的一个或多个字段
语法 : hdel key field1 field2 field3 …
返回成功删除的字段总数
del 删除整条哈希
语法 : del key
返回 1 代表成功, 0 代表哈希不存在
# 创建哈希并批量设置字段
127.0.0.1:6379> hset user2 name zhangsan age 29 gender male address shanghai
(integer) 4
# 获取字段总数
127.0.0.1:6379> hlen user2
(integer) 4
# 批量删除字段
127.0.0.1:6379> hdel user2 age gender
(integer) 2
# 查询所有字段名和值
127.0.0.1:6379> hgetall user2
1) "name"
2) "zhangsan"
3) "address"
4) "shanghai"
# 删除不存在字段
127.0.0.1:6379> hdel user2 mail
(integer) 0
# 删除整条哈希
127.0.0.1:6379> del user2
(integer) 1
# 查询所有字段名和值,哈希不存在
127.0.0.1:6379> hgetall user2
(empty array)
五、应用架构
5.1 主从复制
5.1.1 相关概念
一台主节点负责接收写请求
多台从节点完整同步主节点数据,提供读服务,不支持写入
典型应用场景:
- 读写分类
- 数据备份
- 数据离线操作不影响主库
- 高可用集群前置基础
5.1.2 配置项
vim /etc/redis/redis-6379.conf
# 指定主节点 IP 和端口,当前节点作为从节点同步主库
replicaof 10.0.0.240 6379
# 若主节点配置认证密码,必须配置复制密码
masterauth Redis@2026
# 从库配置只读,拒绝所有写入,默认配置
replica-read-only yes
systemctl restart redis
# 动态临时命令
reolicaof 10.0.0.221 6379
# 取消主从复制关系,当前节点恢复独立,已有数据保留
replicaof no one
5.1.3 主从复制状态信息
# 主节点状态信息
127.0.0.1:6379> info replication
# Replication
role:master # 当前节点角色为主库
connected_slaves:1 # 从节点数量
slave0:ip=10.0.0.241,port=6379,state=online,offset=126,lag=1 # 从节点信息
master_failover_state:no-failover
master_replid:2a79ac8cdcb63cce35f44e39307359db4b1ee998
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:126 # 主库全局复制偏移量
second_repl_offset:-1
repl_backlog_active:1 # 复制缓冲区已启用
repl_backlog_size:1048576 # 复制缓冲区总大小 1M
repl_backlog_first_byte_offset:1 # 缓冲区最旧数据起始 offset
repl_backlog_histlen:126 # 缓冲区有效数据长度
# 从节点状态信息
127.0.0.1:6379> info replication
# Replication
role:slave # 当前节点角色为从库
master_host:10.0.0.240 # 主节点 IP
master_port:6379 # 主节点端口
master_link_status:up # 复制链路状态,up 正常,down 断开
master_last_io_seconds_ago:4 # 距离主库最后一次数据传输间隔秒数,>10代表链路异常
master_sync_in_progress:0
slave_read_repl_offset:28
slave_repl_offset:28 # 从库已同步 offset,差值即为延迟字节
replica_full_sync_buffer_size:0
replica_full_sync_buffer_peak:0
master_current_sync_attempts:1
master_total_sync_attempts:1
master_link_up_since_seconds:10
total_disconnect_time_sec:0
slave_priority:100
slave_read_only:1 # 从库只读开关开启
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:2a79ac8cdcb63cce35f44e39307359db4b1ee998
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:28 # 主库最新 offset
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:15
repl_backlog_histlen:14
# 主节点查看节点角色
127.0.0.1:6379> role
1) "master" # 角色主库
2) (integer) 98 # 全局 offset
3) 1) 1) "10.0.0.241" # 从库 IP
2) "6379" # 从库端口
3) "98" # 从库 offset
# 从节点查看角色信息
127.0.0.1:6379> role
1) "slave" # 角色从库
2) "10.0.0.240" # 主库 IP
3) (integer) 6379 # 主库端口
4) "connected" # 复制链路状态
5) (integer) 42 # 已同步 offset
5.1.4 主从复制部署案例
5.1.4.1 环境准备
# 基于单实例脚本部署 Redis
bash deploy_redis.sh
# 基础环境配置
MAIN_CONF='/etc/redis/redis-6379.conf'
HOST_ADDR=$(hostname -I)
# 修改 bind 为本机IP + 本地
sed -i "s|^bind 127.0.0.1 -::1|bind 127.0.0.1 ${HOST_ADDR} -::1|" ${MAIN_CONF}
# 设置日志文件
sed -i "s|^logfile \\"\\"|logfile /var/log/redis/redis-6379.log|" ${MAIN_CONF}
# 指定数据目录
sed -i "s|^dir ./|dir /var/lib/redis|" ${MAIN_CONF}
# 增加 RDB 定时快照规则,开启 RDB 持久化
grep -q "^save 900 1" ${MAIN_CONF} || echo -e "save 900 1\\nsave 300 10\\nsave 60 10000" >> ${MAIN_CONF}
# 设置认证密码
REDIS_PASS="Redis@2026"
grep -q "^requirepass" ${MAIN_CONF} || echo "requirepass ${REDIS_PASS}" >> ${MAIN_CONF}
# 将认证密码写入全局变量,方便客户端使用
echo 'export REDISCLI_AUTH="Redis@2026"' >> /etc/profile.d/redis.sh && source /etc/profile.d/redis.sh
systemctl restart redis
# 当前各节点角色都是 master
127.0.0.1:6379> role
1) "master"
2) (integer) 0
3) (empty array)
# 查看节点一监听信息
ss -ntl
127.0.0.1:6379
10.0.0.240:6379
[::1]:6379
# 查看节点二监听信息
ss -ntl
127.0.0.1:6379
10.0.0.241:6379
[::1]:6379
5.1.4.2 动态配置主从复制
# 节点二配置为从节点
[root@ubuntu-241 14:34:34 ~]#redis-cli
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:4f421e18b21492d4b06e9dd1a0b2c458da7387f9
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6379> config get masterauth
1) "masterauth"
2) ""
127.0.0.1:6379> config set masterauth Redis@2026
OK
127.0.0.1:6379> replicaof 10.0.0.240 6379
OK
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:10.0.0.240
master_port:6379
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_read_repl_offset:28
slave_repl_offset:28
replica_full_sync_buffer_size:0
replica_full_sync_buffer_peak:0
master_current_sync_attempts:1
master_total_sync_attempts:1
master_link_up_since_seconds:10
total_disconnect_time_sec:0
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:2a79ac8cdcb63cce35f44e39307359db4b1ee998
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:28
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:15
repl_backlog_histlen:14
# 从节点测试写入,由于开启只读,写入报错
127.0.0.1:6379> set nihao 1
(error) READONLY You can't write against a read only replica.
5.1.4.3 验证主从复制
# 主节点查看主从复制信息
127.0.0.1:6379> role
1) "master"
2) (integer) 98
3) 1) 1) "10.0.0.241"
2) "6379"
3) "98"
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=10.0.0.241,port=6379,state=online,offset=126,lag=1
master_failover_state:no-failover
master_replid:2a79ac8cdcb63cce35f44e39307359db4b1ee998
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:126
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:126
# 主节点测试写入
127.0.0.1:6379> set hello 1
OK
127.0.0.1:6379> get hello
"1"
# 从节点验证同步
127.0.0.1:6379> get hello
"1"
# 从节点取消主从复制
127.0.0.1:6379> replicaof no one
OK
# 确认主从复制状态
127.0.0.1:6379> role
1) "master"
2) (integer) 1146
3) (empty array)
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:bd3b6ee211e003ed6b4b1af002516b634290efd8
master_replid2:2a79ac8cdcb63cce35f44e39307359db4b1ee998
master_repl_offset:1146
second_repl_offset:1147
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:15
repl_backlog_histlen:1132
# 尝试写入,关闭只读,写入成功
127.0.0.1:6379> set nihao 2
OK
5.1.4.4 持久配置主从复制
# 持久配置主从复制
vim /etc/redis/redis-6379.conf
# 指定主节点 IP 和端口,当前节点作为从节点同步主库
replicaof 10.0.0.240 6379
# 若主节点配置认证密码,必须配置复制密码
masterauth Redis@2026
systemctl restart redis
# 查看主从复制信息
[root@ubuntu-241 14:56:30 ~]#redis-cli role
1) "slave"
2) "10.0.0.240"
3) (integer) 6379
4) "connected"
5) (integer) 1244
[root@ubuntu-241 14:56:46 ~]#redis-cli info replication
# Replication
role:slave
master_host:10.0.0.240
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_read_repl_offset:1272
slave_repl_offset:1272
replica_full_sync_buffer_size:0
replica_full_sync_buffer_peak:0
master_current_sync_attempts:1
master_total_sync_attempts:1
master_link_up_since_seconds:76
total_disconnect_time_sec:0
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:2a79ac8cdcb63cce35f44e39307359db4b1ee998
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1272
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1161
repl_backlog_histlen:112
5.1.5 replication backlog 调优
- 复制缓冲区 (repl-backlog) 是 Redis 主从增量同步核心内存区域\\
- 主节点写命令先写入 backlog 缓冲区
- 从节点断连重连后,通过 offset 对比回放断连期间缺失的写指令
- backlog 没被覆盖,进行增量同步即可
- backlog 被覆盖,需要进行全量 RDB 同步
- 默认 backlog 大小 1M 存在严重缺陷,极易被打满覆盖
- 从库只能使用 全量同步 ,造成延迟暴涨、业务抖动
# 测试案例
# 从节点断开主从关系
redis-cli replicaof no one
# 在主节点高频写入,覆盖旧 offset ,打满 1M 缓冲区
for i in {1..20000};do redis-cli SET test_$i $i >/dev/null;done
# 从节点重新加入主从复制
redis-cli replicaof 10.0.0.240 6379
# 从节点查看主从复制信息
redis-cli info replication | egrep 'role|conn'
role:slave
total_disconnect_time_sec:836
connected_slaves:0
# 从节点查看日志
[root@ubuntu-241 16:54:15 ~]#tail /var/log/redis/redis-6379.log -n20
# 从库尝试复用历史缓存主库信息,进行增量同步
30086:S 27 Aug 2026 16:53:22.431 * Before turning into a replica, using my own master parameters to synthesize a cached master: I may be able to synchronize with the new master with just a partial transfer.
30086:S 27 Aug 2026 16:53:22.431 * Connecting to MASTER 10.0.0.240:6379
30086:S 27 Aug 2026 16:53:22.431 * MASTER <–> REPLICA sync started
30086:S 27 Aug 2026 16:53:22.431 * REPLICAOF 10.0.0.240:6379 enabled (user request from 'id=12 addr=127.0.0.1:40816 laddr=127.0.0.1:6379 fd=13 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 ssub=0 multi=-1 watch=0 qbuf=46 qbuf-free=20428 argv-mem=23 multi-mem=0 rbs=16384 rbp=16384 obl=0 oll=0 omem=0 tot-mem=37935 events=r cmd=replicaof user=default redir=-1 resp=2 lib-name= lib-ver= io-thread=0 tot-net-in=77 tot-net-out=5 tot-cmds=1')
30086:S 27 Aug 2026 16:53:22.432 * Non blocking connect for SYNC fired the event.
30086:S 27 Aug 2026 16:53:22.432 * Master replied to PING, replication can continue...
# 建立 TCP 复制通道,发起 PSYNC 增量同步请求
# 缓冲区日志ID,末尾数字 : 9883 (断开前从库同步到的偏移量)
30086:S 27 Aug 2026 16:53:22.433 * Trying a partial resynchronization (request 82daec890df75be585617f95a20a7233ebe763b0:9883).
# 关键报错,PSYNC 同步不可行,offset 已被 backlog 覆盖,开始全量备份
30086:S 27 Aug 2026 16:53:22.433 * PSYNC is not possible, initialize RDB channel.
# 开启并行 RDB 传输通道,接收主库全量快照
30086:S 27 Aug 2026 16:53:27.990 * Starting to receive RDB and replication stream in parallel.
30086:S 27 Aug 2026 16:53:27.992 * MASTER <–> REPLICA sync: receiving streamed RDB from master with EOF to disk
30086:S 27 Aug 2026 16:53:28.000 * Discarding previously cached master state.
30086:S 27 Aug 2026 16:53:28.000 * MASTER <–> REPLICA sync: Loading DB in memory
# 清空从库本地全部数据
30086:S 27 Aug 2026 16:53:28.001 * MASTER <–> REPLICA sync: Flushing old data
30086:S 27 Aug 2026 16:53:28.002 * Loading RDB produced by version 8.4.0
30086:S 27 Aug 2026 16:53:28.002 * RDB age 1 seconds
# RDB 快照占用内存空间,大于 1M
30086:S 27 Aug 2026 16:53:28.002 * RDB memory usage when created 2.57 Mb
# 加载 RDB 快照到内存,共加载 20001 条键
30086:S 27 Aug 2026 16:53:28.007 * Done loading RDB, keys loaded: 20001, keys expired: 0.
# 全量同步执行完毕
30086:S 27 Aug 2026 16:53:28.007 * MASTER <–> REPLICA sync: Finished with success
# 回放同步期间新增复制流
30086:S 27 Aug 2026 16:53:28.007 * MASTER <–> REPLICA sync: Starting to stream replication buffer into the db (0 bytes).
30086:S 27 Aug 2026 16:53:28.007 * MASTER <–> REPLICA sync: Successfully streamed replication buffer into the db (0 bytes in total)
# 主节点永久扩容 backlog 缓冲区
vim /etc/redis/redis-6379.conf
repl-backlog-size 64mb
5.1.6 主从复制故障处理
# 准备三个 Redis 节点,完成节点初始化
# 节点二和节点三配置为从节点
vim /etc/redis/redis-6379.conf
# 指定主节点 IP 和端口,当前节点作为从节点同步主库
replicaof 10.0.0.221 6379
# 若主节点配置认证密码,必须配置复制密码
masterauth Redis@2026
systemctl restart redis
# 从节点确认主从复制状态
redis-cli role
1) "slave"
2) "10.0.0.221"
3) (integer) 6379
4) "connected"
5) (integer) 28
# 主节点停止 Redis 服务,模拟服务器宕机、进程崩溃
systemctl stop redis
# 所有从节点查看复制偏移量,数字越大表示数据越新,现在数字大的提升为新主节点
redis-cli info replication | egrep 'master_repl_offset'
# 将第二个节点提升为主节点
# 取消主从复制
[root@Ubuntu-222 18:15:31 ~]#redis-cli replicaof no one
OK
# 验证复制信息
[root@Ubuntu-222 18:18:06 ~]#redis-cli info replication | egrep 'role|conn'
role:master
connected_slaves:0
# 测试写入数据,验证节点能否正常写入
[root@Ubuntu-222 18:19:08 ~]#redis-cli set test 1
OK
# 写入正常,将业务写入切换到新主节点
# 从节点切换主节点
# 在第三个节点操作
# 动态配置主从复制
redis-cli replicaof 10.0.0.222 6379
# 确认主从复制状态
redis-cli role
1) "slave"
2) "10.0.0.222"
3) (integer) 6379
4) "connect"
5) (integer) -1
# 永久配置主从复制
vim /etc/redis/redis-6379.conf
replicaof 10.0.0.222 6379 # 修改主节点 IP 为新的主节点
# 原主节点恢复,配置为从节点
# 在第一个节点操作
# 永久配置主从复制
vim /etc/redis/redis-6379.conf
# 添加以下 2 行主从复制配置
replicaof 10.0.0.222 6379
masterauth Redis@2026
# 启动 redis 服务
systemctl start redis
# 确认主从复制状态
[root@ubuntu-221 18:28:24 ~]#redis-cli role
1) "slave"
2) "10.0.0.222"
3) (integer) 6379
4) "connect"
5) (integer) -1
redis-cli info replication | egrep 'role|master'
role:slave
master_host:10.0.0.222
master_port:6379
master_link_status:up
# 新主节点确认复制状态
[root@Ubuntu-222 18:32:07 ~]#redis-cli role
1) "master"
2) (integer) 263
3) 1) 1) "10.0.0.221"
2) "6379"
3) "263"
2) 1) "10.0.0.223"
2) "6379"
3) "263"
redis-cli info replication | egrep 'role|slave'
role:master
connected_slaves:2
slave0:ip=10.0.0.221,port=6379,state=online,offset=347,lag=0
slave1:ip=10.0.0.223,port=6379,state=online,offset=347,lag=0
5.1.7 主从复制部署脚本
vim deploy_redis_ms.sh
#!/bin/bash
# *************************************
# * 功能: Redis一键编译部署脚本 支持主/从节点区分部署
# * 作者:
# * 联系:
# * 版本: 2026-08-27
# * 使用示例:
# 主节点10.0.0.221 执行:bash deploy_redis.sh master
# 从节点10.0.0.222 执行:bash deploy_redis.sh slave
# *************************************
# ====================== 颜色定义 ======================
RED='\\033[0;31m'
GREEN='\\033[0;32m'
YELLOW='\\033[1;33m'
BLUE='\\033[0;34m'
NC='\\033[0m' # 恢复默认颜色
# ====================== 全局业务配置(适配你的一主两从架构) ======================
REDIS_VERSION="8.4.0"
REDIS_URL="https://download.redis.io/releases/redis-${REDIS_VERSION}.tar.gz"
SRC_BASE="/usr/local/src"
REDIS_SRC="${SRC_BASE}/redis-${REDIS_VERSION}"
CONF_DIR="/etc/redis"
REDIS_PORT="6379"
DATA_DIR="/var/lib/redis"
LOG_DIR="/var/log/redis"
RUN_DIR="/var/run/redis"
RUN_USER="redis"
MAIN_CONF="${CONF_DIR}/redis-${REDIS_PORT}.conf"
SYSTEMD_SVC="/etc/systemd/system/redis.service"
# 主从固定IP
MASTER_IP="10.0.0.221"
REDIS_PWD="Redis@2026"
REPL_BACKLOG_SIZE="32mb"
SLAVE_PRIORITY="100"
# 节点类型 master/slave
NODE_TYPE="$1"
# ====================== 工具函数 ======================
info() {
echo -e "${GREEN}[INFO] $1${NC}"
}
warn() {
echo -e "${YELLOW}[WARN] $1${NC}"
}
error() {
echo -e "${RED}[ERROR] $1${NC}"
}
title() {
echo -e "\\n${BLUE}==================================== $1 ====================================${NC}"
}
# 出错直接退出
check_ret() {
if [ $? -ne 0 ]; then
error "$1 执行失败,终止脚本"
exit 1
fi
}
# ====================== 前置校验 ======================
pre_check() {
title "前置参数校验"
if [ $(id -u) -ne 0 ]; then
error "请使用 root 权限执行此脚本!"
exit 1
fi
if [ "${NODE_TYPE}" != "master" ] && [ "${NODE_TYPE}" != "slave" ]; then
error "必须传入节点类型参数:bash deploy_redis.sh master | slave"
exit 1
fi
info "当前节点类型:${NODE_TYPE},主库IP:${MASTER_IP}"
}
# 1. 安装编译依赖
install_deps() {
title "安装编译依赖包"
apt update -y
apt install -y –no-install-recommends ca-certificates wget gcc \\
g++ libc6-dev libssl-dev make automake autoconf libtool ufw
apt install -y –no-install-recommends \\
build-essential pkg-config libssl-dev libjemalloc-dev libsystemd-dev
# 放行Redis端口 内网全通
ufw allow 6379/tcp comment 'Redis主从复制端口'
ufw reload >/dev/null 2>&1
info "基础编译依赖、防火墙端口放行完成"
}
# 2. 下载&解压源码
download_extract() {
title "下载并解压 Redis 源码"
mkdir -p ${SRC_BASE}
cd ${SRC_BASE}
if [ ! -f "redis-${REDIS_VERSION}.tar.gz" ]; then
info "开始下载 Redis ${REDIS_VERSION} 源码包"
wget -q –retry-connrefused –tries=3 ${REDIS_URL}
check_ret "源码下载"
else
warn "源码包已存在,跳过下载"
fi
# 存在目录先清理,避免旧文件冲突
[ -d "${REDIS_SRC}" ] && rm -rf ${REDIS_SRC}
tar -xf redis-${REDIS_VERSION}.tar.gz
cd ${REDIS_SRC}
info "源码解压完成,当前目录: $(pwd)"
}
# 3. 编译 Redis(jemalloc内存、systemd、关闭多余模块)
compile_redis() {
title "编译 Redis"
export BUILD_TLS=no
export BUILD_WITH_MODULES=no
export INSTALL_RUST_TOOLCHAIN=no
export DISABLE_WERRORS=yes
make -j "$(nproc)" all USE_SYSTEMD=yes MALLOC=jemalloc
check_ret "Redis编译"
info "Redis 编译成功,使用jemalloc内存分配器"
}
# 4. 安装二进制程序
install_bin() {
title "安装 Redis 二进制文件"
make install
check_ret "二进制安装"
}
# 5. 创建运行用户 & 目录
create_user_dir() {
title "创建运行用户与数据目录"
useradd -s /sbin/nologin ${RUN_USER} 2>/dev/null || warn "用户 ${RUN_USER} 已存在"
mkdir -p ${CONF_DIR} ${DATA_DIR} ${LOG_DIR} ${RUN_DIR}
chown -R ${RUN_USER}:${RUN_USER} ${CONF_DIR} ${DATA_DIR} ${LOG_DIR} ${RUN_DIR}
info "用户与目录创建&授权完成"
}
# 6. 初始化配置文件(区分主库/从库,适配主从复制)
init_config() {
title "初始化 Redis 配置文件 节点类型:${NODE_TYPE}"
cp ${REDIS_SRC}/redis.conf ${MAIN_CONF}
chown ${RUN_USER}:${RUN_USER} ${MAIN_CONF}
# 1. systemd后台适配
sed -i 's/^daemonize yes/daemonize no/' ${MAIN_CONF}
sed -i 's/^# supervised auto/supervised systemd/' ${MAIN_CONF}
sed -i "s|^pidfile.*|pidfile ${RUN_DIR}/redis_${REDIS_PORT}.pid|" ${MAIN_CONF}
# 2. 网络配置 允许内网互通
sed -i 's/^bind 127.0.0.1/#bind 127.0.0.1/' ${MAIN_CONF}
sed -i 's/^protected-mode yes/protected-mode no/' ${MAIN_CONF}
sed -i "s|^# logfile .*|logfile ${LOG_DIR}/redis-${REDIS_PORT}.log|" ${MAIN_CONF}
sed -i "s|^dir .*|dir ${DATA_DIR}|" ${MAIN_CONF}
sed -i "s/^# requirepass .*/requirepass ${REDIS_PWD}/" ${MAIN_CONF}
sed -i "s/^# repl-backlog-size .*/repl-backlog-size ${REPL_BACKLOG_SIZE}/" ${MAIN_CONF}
# 3. RDB持久化
sed -i 's/^# save 3600 .*/save 3600 1\\nsave 300 100\\nsave 60 10000/' ${MAIN_CONF}
# 主从差异化配置
if [ "${NODE_TYPE}" = "slave" ]; then
# 从库配置复制、只读、优先级
echo "masterauth ${REDIS_PWD}" >> ${MAIN_CONF}
echo "replicaof ${MASTER_IP} ${REDIS_PORT}" >> ${MAIN_CONF}
echo "replica-read-only yes" >> ${MAIN_CONF}
echo "replica-priority ${SLAVE_PRIORITY}" >> ${MAIN_CONF}
info "已写入从库专属主从复制配置"
fi
info "主配置文件已生成: ${MAIN_CONF}"
}
# 7. 系统内核参数完整优化
sysctl_optimize() {
title "系统内核全量优化(Redis生产必备)"
# 内存提交策略
sed -i '/vm.overcommit_memory/d' /etc/sysctl.conf
echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf
# 网络监听队列
sed -i '/net.core.somaxconn/d' /etc/sysctl.conf
echo 'net.core.somaxconn = 65535' >> /etc/sysctl.conf
sysctl -p >/dev/null 2>&1
# 永久关闭透明大页THP(内存碎片元凶)
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
grep -q 'transparent_hugepage' /etc/rc.local || echo 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' >> /etc/rc.local
grep -q 'transparent_hugepage' /etc/rc.local || echo 'echo never > /sys/kernel/mm/transparent_hugepage/defrag' >> /etc/rc.local
info "内核内存、网络、THP优化完成并永久生效"
}
# 8. 配置 systemd 服务
config_systemd() {
title "配置 systemd 系统服务"
cat > ${SYSTEMD_SVC} <<-EOF
[Unit]
Description=Redis Data Structure Server
Documentation=https://redis.io/documentation
Wants=network-online.target
After=network-online.target
[Service]
ExecStart=/usr/local/bin/redis-server ${MAIN_CONF}
LimitNOFILE=65535
NoNewPrivileges=yes
OOMScoreAdjust=-900
PrivateTmp=yes
Type=notify
TimeoutStartSec=infinity
TimeoutStopSec=infinity
UMask=0077
User=${RUN_USER}
Group=${RUN_USER}
WorkingDirectory=${DATA_DIR}
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable redis
info "systemd 服务配置完成,已设置开机自启"
}
# 9. 启动服务 & 验证主从状态
start_and_check() {
title "启动 Redis 服务并验证"
systemctl restart redis
sleep 2
if systemctl is-active –quiet redis; then
info "Redis 服务启动成功!"
echo -e "\\n${BLUE}— 服务状态 —${NC}"
systemctl status redis –no-pager
echo -e "\\n${BLUE}— 连通性测试 ping —${NC}"
redis-cli -a ${REDIS_PWD} -p ${REDIS_PORT} ping
echo -e "\\n${BLUE}— 主从复制状态 INFO replication —${NC}"
redis-cli -a ${REDIS_PWD} -p ${REDIS_PORT} INFO replication
else
error "Redis 服务启动失败,请检查日志 ${LOG_DIR}/redis-${REDIS_PORT}.log"
systemctl status redis –no-pager
exit 1
fi
}
# ====================== 主执行入口 ======================
main() {
pre_check
info "====== 开始执行 Redis ${REDIS_VERSION} 编译安装 节点类型:${NODE_TYPE} ======"
install_deps
download_extract
compile_redis
install_bin
create_user_dir
init_config
sysctl_optimize
config_systemd
start_and_check
echo -e "\\n${GREEN}==================== 全部部署流程执行完毕 ====================${NC}"
if [ "${NODE_TYPE}" = "slave" ]; then
warn "当前为从节点,主库IP:${MASTER_IP},可在主库执行INFO replication查看从节点连接状态"
fi
}
# 调用主函数
main
# 主节点执行
bash deploy_redis_ms.sh master
# 从节点执行
bash deploy_redis_ms.sh slave
5.1.8 主从复制测试脚本
vim redis_repl_test.sh
#!/bin/bash
# *************************************
# * 功能: Redis主从复制全自动专项测试脚本
# * 作者:
# * 联系:
# * 版本: 2026-08-27
# * 功能:Redis主从复制全自动专项测试脚本
# * 适配:主 10.0.0.221、从 10.0.0.222
# * 测试项:连通性、角色校验、读写分离、增量同步、offset一致性、只读权限
# *************************************
# 颜色定义
RED='\\033[0;31m'
GREEN='\\033[0;32m'
YELLOW='\\033[1;33m'
BLUE='\\033[0;34m'
NC='\\033[0m'
# 全局变量(与部署脚本保持一致)
MASTER_IP="10.0.0.221"
SLAVE_IP="10.0.0.222"
REDIS_PORT="6379"
REDIS_PWD="Redis@2026"
# 封装统一命令,自带静默参数消除密码警告
MASTER_CLI="redis-cli –no-auth-warning -h ${MASTER_IP} -p ${REDIS_PORT} -a ${REDIS_PWD}"
SLAVE_CLI="redis-cli –no-auth-warning -h ${SLAVE_IP} -p ${REDIS_PORT} -a ${REDIS_PWD}"
info() { echo -e "${GREEN}[PASS] $1${NC}"; }
warn() { echo -e "${YELLOW}[WARN] $1${NC}"; }
error() { echo -e "${RED}[FAIL] $1${NC}"; }
title() { echo -e "\\n${BLUE}========== $1 ==========${NC}"; }
# 1. 基础连通性测试
test_ping() {
title "1. 基础连通性 PING 测试"
master_ping=$($MASTER_CLI PING)
slave_ping=$($SLAVE_CLI PING)
if [ "$master_ping" = "PONG" ]; then
info "主节点 $MASTER_IP 连通正常"
else
error "主节点连通失败"
fi
if [ "$slave_ping" = "PONG" ]; then
info "从节点 $SLAVE_IP 连通正常"
else
error "从节点连通失败"
fi
}
# 2. 主从角色 & 链路状态校验
test_role_link() {
title "2. 主从角色与复制链路状态校验"
master_role=$($MASTER_CLI INFO replication | grep role | awk -F: '{print $2}' | tr -d '\\r' | xargs)
slave_role=$($SLAVE_CLI INFO replication | grep role | awk -F: '{print $2}' | tr -d '\\r' | xargs)
link_status=$($SLAVE_CLI INFO replication | grep master_link_status | awk -F: '{print $2}' | tr -d '\\r' | xargs)
repl_count=$($MASTER_CLI INFO replication | grep -E 'connected_replicas|connected_slaves' | awk -F: '{print $2}' | tr -d '\\r' | xargs)
if [ "$master_role" = "master" ]; then
info "主节点角色正确:master"
else
error "主节点角色异常"
fi
if [ "$slave_role" = "slave" ]; then
info "从节点角色正确:slave"
else
error "从节点角色异常"
fi
if [ "$link_status" = "up" ]; then
info "主从复制链路正常:up"
else
error "主从复制链路断开:down"
fi
# 修复空值判断报错
if [[ -z "$repl_count" ]]; then
warn "副本计数获取为空,瞬时状态抖动,跳过校验"
elif [[ "$repl_count" -eq 1 ]]; then
info "主节点成功识别 1 台从节点"
else
error "从节点接入数量异常,当前:$repl_count"
fi
}
# 3. 主写从读数据同步测试
test_data_sync() {
title "3. 主写从读数据同步测试"
TEST_KEY="repl_test_$(date +%s)"
TEST_VAL="success_2026"
# 主库写入
$MASTER_CLI SET $TEST_KEY $TEST_VAL >/dev/null 2>&1
# 从库读取
SLAVE_GET=$($SLAVE_CLI GET $TEST_KEY)
if [ "$SLAVE_GET" = "$TEST_VAL" ]; then
info "主从数据同步正常,key:$TEST_KEY"
else
error "主从数据同步失败"
fi
}
# 4. 从库只读禁止写入测试
test_slave_readonly() {
title "4. 从库只读权限校验"
# 从库尝试写入
WRITE_RET=$($SLAVE_CLI SET write_test 123 2>&1)
if [[ $WRITE_RET == *"READONLY"* ]]; then
info "从库只读保护生效,禁止写入成功"
else
error "从库只读失效,存在数据分裂风险"
fi
}
# 5. offset 一致性校验(修复算术运算报错)
test_offset() {
title "5. 主从 offset 偏移量一致性校验"
master_offset=$($MASTER_CLI INFO replication | grep master_repl_offset | awk -F: '{print $2}' | tr -d '\\r' | xargs)
slave_offset=$($SLAVE_CLI INFO replication | grep slave_repl_offset | awk -F: '{print $2}' | tr -d '\\r' | xargs)
info "主库offset:$master_offset"
info "从库offset:$slave_offset"
# 修复空值、非法字符算术报错
if [[ -n "$master_offset" && -n "$slave_offset" ]]; then
diff=$((master_offset – slave_offset))
if [ $diff -ge 0 ] && [ $diff -lt 100 ]; then
info "主从偏移量一致,无延迟或延迟极小"
else
warn "主从存在复制延迟,差值:$diff"
fi
else
error "offset数据获取异常,无法校验延迟"
fi
}
# 6. 断连增量同步测试(核心原理验证)
test_partial_sync() {
title "6. 断连重连增量同步测试"
# 获取断开之前从库的master_replid 和 offset
old_replid=$($SLAVE_CLI INFO replication | grep master_replid | awk -F: '{print $2}' | tr -d '\\r' | xargs)
old_offset=$($SLAVE_CLI INFO replication | grep slave_repl_offset | awk -F: '{print $2}' | tr -d '\\r' | xargs)
echo "断开前 replid: $old_replid , offset: $old_offset"
# 从库断开主从
$SLAVE_CLI replicaof no one >/dev/null
sleep 1
# 主库批量写入
for i in {1..200};do
$MASTER_CLI SET sync_$i $i >/dev/null
done
# 从库重新连接主库
$SLAVE_CLI replicaof $MASTER_IP $REDIS_PORT >/dev/null
sleep 8
# 获取重连后的复制信息
new_replid=$($SLAVE_CLI INFO replication | grep master_replid | awk -F: '{print $2}' | tr -d '\\r' | xargs)
new_offset=$($SLAVE_CLI INFO replication | grep slave_repl_offset | awk -F: '{print $2}' | tr -d '\\r' | xargs)
echo -e "\\n${BLUE}当前从库复制状态摘要${NC}"
$SLAVE_CLI INFO replication | grep -E "master_link_status|master_repl_offset|slave_repl_offset"
# 核心判断:replid没变=增量同步成功
if [[ "${old_replid}" == "${new_replid}" ]];then
info "断连重连增量同步成功,backlog缓冲区生效"
else
warn "断开期间写入的数据未保留,backlog缓冲区过小触发Full resync全量同步;"
warn "优化方案:修改主库redis.conf repl-backlog-size 32mb,重启redis服务生效"
fi
}
# 清理测试脏数据
clean_test_data() {
title "7. 清理测试数据"
$MASTER_CLI FLUSHDB >/dev/null
info "测试数据清理完成"
}
# 主流程
main() {
test_ping
test_role_link
test_data_sync
test_slave_readonly
test_offset
test_partial_sync
clean_test_data
echo -e "\\n${GREEN}====== 所有主从复制测试项执行完毕 ======${NC}"
}
main
# 执行效果测试
bash redis_repl_test.sh
========== 1. 基础连通性 PING 测试 ==========
[PASS] 主节点 10.0.0.221 连通正常
[PASS] 从节点 10.0.0.222 连通正常
========== 2. 主从角色与复制链路状态校验 ==========
[PASS] 主节点角色正确:master
[PASS] 从节点角色正确:slave
[PASS] 主从复制链路正常:up
[PASS] 主节点成功识别 1 台从节点
========== 3. 主写从读数据同步测试 ==========
[PASS] 主从数据同步正常,key:repl_test_1787830323
========== 4. 从库只读权限校验 ==========
[PASS] 从库只读保护生效,禁止写入成功
========== 5. 主从 offset 偏移量一致性校验 ==========
[PASS] 主库offset:43816
[PASS] 从库offset:43816
[PASS] 主从偏移量一致,无延迟或延迟极小
========== 6. 断连重连增量同步测试 ==========
当前从库复制状态摘要
master_link_status:up
slave_repl_offset:50800
master_repl_offset:50800
[PASS] 断连重连增量同步成功,backlog缓冲区生效
========== 7. 清理测试数据 ==========
[PASS] 测试数据清理完成
====== 所有主从复制测试项执行完毕 ======
5.2 Sentinel 哨兵集群
5.2.1 哨兵集群相关概念
基于主从复制
哨兵机制用于自动故障转移
哨兵为独立守护进程,一般每个节点安装 哨兵 与 redis
哨兵拥有三个工作能力:
- 监控者 : 持续心跳检测节点在线状态
- 仲裁者 : 当某个哨兵判断节点主观下线,多个哨兵投票判断节点是否真正故障
- 故障转移执行者 : 当集群判定节点客观下线后,选举领头哨兵执行故障转移(主从切换、从节点重置、架构重构等)
节点应该部署为奇数个,避免脑裂,一般为3个节点(一主2从)
所有节点都需要配置安全认证密码和主从复制密码,且 2 个密码一致
5.2.2 哨兵集群核心配置
配置 Redis 集群信息:
sentinel monitor <监控集群名> <主节点 IP> <主节点端口> <法定票数>
参数解析:
- 监控集群名 : 自定义全局唯一,客户端通过该名称查询主节点信息
- 主节点 IP 和端口 : 初始主节点 IP 和 端口
- 法定票数 : 设置为哨兵数量一半以上,例如 3 个哨兵设置为 2
哨兵配置连接 Redis 密码:
sentinel auth-pass mymaster Redis@2026
5.2.3 哨兵集群部署案例
5.2.3.1 环境准备
# 先部署主从复制
# 主节点查看主从复制状态
redis-cli info replication
# Replication
role:master
connected_slaves:2
slave0:ip=10.0.0.222,port=6379,state=online,offset=53080,lag=1
slave1:ip=10.0.0.223,port=6379,state=online,offset=53080,lag=1
# 所有节点配置 安全认证密码 和 主从复制密码
vim /etc/redis/redis-6379.conf
requirepass "Redis@2026"
masterauth "Redis@2026"
systemctl restart redis
5.2.3.2 哨兵搭建
# 创建标准化目录,用于存放配置、日志、持久化数据
mkdir -p /apps/redis/etc /apps/redis/log /apps/redis/data
# 准备配置文件
cat > /apps/redis/etc/redis-sentinel.conf << 'EOF'
# 监听本机所有网卡,允许其他服务器访问哨兵
bind 0.0.0.0
# 关闭保护模式,支持跨机哨兵互相通信自动发现
protected-mode no
# 三台哨兵统一端口 26379,不同主机不存在端口冲突
port 26379
# 设定登录哨兵的认证密码
requirepass "Redis@2026"
# 后台守护进程运行哨兵
daemonize yes
pidfile /var/run/redis-sentinel.pid
loglevel notice
logfile "/apps/redis/log/sentinel.log"
dir /apps/redis/data
# 核心监控配置:集群名mymaster、初始主库地址、法定票数2
sentinel monitor mymaster 10.0.0.221 6379 2
# Redis 集群安全认证密码,和所有节点 requirepass 保持一致
sentinel auth-pass mymaster Redis@2026
# PING 超时 3000 毫秒标记节点主观下线 sdown,修改该值为 3s
sentinel down-after-milliseconds mymaster 3000
# 故障切换后,新主库同时同步到从节点的数量
sentinel parallel-syncs mymaster 1
# 完整故障切换最大超时时间 180000 毫秒
sentinel failover-timeout mymaster 180000
# 安全限制
sentinel deny-scripts-reconfig yes
SENTINEL resolve-hostnames no
SENTINEL announce-hostnames no
SENTINEL master-reboot-down-after-period mymaster 0
EOF
# 创建服务文件
cat > /etc/systemd/system/redis-sentinel.service << 'EOF'
[Unit]
Description=Redis Sentinel Service
Documentation=https://redis.io/docs/manual/sentinel/
After=network.target
[Service]
Type=forking
# 编译安装的哨兵二进制程序路径
ExecStart=/usr/local/src/redis-8.4.0/src/redis-sentinel /apps/redis/etc/redis-sentinel.conf
# 优雅停止哨兵
ExecStop=/usr/local/src/redis-8.4.0/src/redis-cli -p 26379 -a Redis@2026 shutdown
ExecReload=/bin/kill -USR2 $MAINPID
# PID文件,和sentinel.conf内pidfile保持一致
PIDFile=/var/run/redis-sentinel.pid
# 进程异常自动重启
Restart=on-failure
RestartSec=3
# 安全权限限制(生产推荐)
PrivateTmp=true
LimitNOFILE=65535
[Install]
# 开机自启
WantedBy=multi-user.target
EOF
# 启动服务
systemctl daemon-reload && systemctl enable –now redis-sentinel.service
# 查看哨兵日志
tail /apps/redis/log/sentinel.log
# 查看监听信息
ss -ntl
0.0.0.0:26379
5.2.3.3 集群状态验证
# 登录哨兵集群,携带哨兵认证密码
redis-cli -p 26379 -a Redis@2026
# 或者配置全局变量,不再明文携带密码
echo 'export REDISCLI_AUTH="Redis@2026"' >> /etc/profile.d/redis.sh
source /etc/profile.d/redis.sh
# 需要指定端口,否则连接默认端口 6379
redis-cli -p 26379
# 查看监控的 Redis 主节点信息
127.0.0.1:26379> sentinel masters
1) 1) "name"
2) "mymaster" # 集群名称
3) "ip"
4) "10.0.0.221" # 主节点 IP
5) "port"
6) "6379" # 主节点端口
...
31) "num-slaves"
32) "2" # 检测到的从节点数量
33) "num-other-sentinels"
34) "2" # 检测到的其他哨兵数量
35) "quorum"
36) "2" # 仲裁投票数
# 查看从节点信息
127.0.0.1:26379> sentinel slaves mymaster
1) 1) "name"
2) "10.0.0.222:6379"
3) "ip"
4) "10.0.0.222"
5) "port"
6) "6379"
31) "master-link-status"
32) "ok" # 主从复制链路正常
37) "slave-priority"
38) "100" # 默认从库优先级,数字越小优先级越高,优先级相同则选择偏移量最大的从库
2) 1) "name"
2) "10.0.0.223:6379"
3) "ip"
4) "10.0.0.223"
5) "port"
6) "6379"
31) "master-link-status"
32) "ok"
37) "slave-priority"
38) "100"
# 查看当前节点哨兵信息
127.0.0.1:26379> INFO sentinel
master0:name=mymaster,status=ok,address=10.0.0.221:6379,slaves=2,sentinels=3 # 主节点信息,从节点数量,哨兵数量
# 查看集群其他哨兵信息
sentinel sentinels mymaster
5.2.3.4 故障切换
# 在 221 主节点关闭 Redis
redis-cli shutdown
# 查看主节点信息
redis-cli -p 26379 sentinel masters
1) 1) "name"
2) "mymaster"
3) "ip"
4) "10.0.0.223" # 已切换主节点
5) "port"
6) "6379"
# 在 223 节点关闭 Redis
redis-cli shutdown
# 查看主节点信息
redis-cli -p 26379 sentinel masters
1) 1) "name"
2) "mymaster"
3) "ip"
4) "10.0.0.222" # 已切换主节点
5) "port"
6) "6379"
# 查看从节点信息
redis-cli -p 26379 SENTINEL slaves mymaster
1) 1) "name"
2) "10.0.0.223:6379"
3) "ip"
4) "10.0.0.223"
5) "port"
6) "6379"
9) "flags"
10) "s_down,slave,disconnected" # s_down 主观下线, slave 哨兵记录它曾经是从节点, disconnected 哨兵与该 Redis 实例网络断开
33) "master-link-status"
34) "err" # 主从复制链路错误
2) 1) "name"
2) "10.0.0.221:6379"
3) "ip"
4) "10.0.0.221"
5) "port"
6) "6379"
9) "flags"
10) "s_down,slave,disconnected"
33) "master-link-status"
34) "err" # 主从复制链路错误
# 在 223 和 222 节点查看配置文件中哨兵自动生成配置
cat /etc/redis/redis-6379.conf
# Generated by CONFIG REWRITE
save 3600 1
save 300 100
save 60 10000
latency-tracking-info-percentiles 50 99 99.9
user default on sanitize-payload #11ac9b0d1980f1c618b15fe515c4c20410c985e59ad2396a5eb594a8ffc9f6c2 ~* &* +@all
# 在 223 和 222 节点查看哨兵配置文件中哨兵自动生成配置
cat /apps/redis/etc/redis-sentinel.conf
# Generated by CONFIG REWRITE
latency-tracking-info-percentiles 50 99 99.9
user default on sanitize-payload #11ac9b0d1980f1c618b15fe515c4c20410c985e59ad2396a5eb594a8ffc9f6c2 ~* &* +@all
sentinel myid 3659787f370ff231d8a8d76d5c067d2d480f349c
sentinel config-epoch mymaster 2
sentinel leader-epoch mymaster 2
sentinel current-epoch 2
sentinel known-replica mymaster 10.0.0.223 6379
sentinel known-replica mymaster 10.0.0.221 6379
sentinel known-sentinel mymaster 10.0.0.222 26379 40f0917ea5beb6932cad7c183a9cbf33c5ebac16
sentinel known-sentinel mymaster 10.0.0.221 26379 12dd698622cb9f6f3d68aa1c39756cad01f59e8a
# 启动停机节点,模拟故障恢复
systemctl start redis.service
# 查看从节点信息,原主节点恢复不抢占主节点,成为新主节点的从节点
redis-cli -p 26379 SENTINEL slaves mymaster
1) 1) "name"
2) "10.0.0.221:6379"
3) "ip"
4) "10.0.0.221"
5) "port"
6) "6379"
9) "flags"
10) "slave"
31) "master-link-status"
32) "ok"
2) 1) "name"
2) "10.0.0.223:6379"
3) "ip"
4) "10.0.0.223"
5) "port"
6) "6379"
9) "flags"
10) "slave"
31) "master-link-status"
32) "ok"
5.3 Redis Cluster 集群
5.3.1 相关概念
解决的问题 : master 节点的性能瓶颈
特点:
多主多从架构,奇数组节点,生产环境最低 6 个节点(3组一主一从)
主节点之间通过 Gossip (流言协议) 通信
主从故障自动切换
Redis 数据共分为16384(0 – 16383) 个 hash 槽位,根据主节点数量分片
5.3.2 部署案例
5.3.2.1 部署环境
| 缓存层 | redis-master1 | 主节点 1 | 10.0.0.240 | Redis 8.4.0 |
| 缓存层 | redis-slave1 | 主机点 1 的从节点 | 10.0.0.241 | Redis 8.4.0 |
| 缓存层 | redis-master2 | 主节点 2 | 10.0.0.242 | Redis 8.4.0 |
| 缓存层 | redis-slave2 | 主机点 2 的从节点 | 10.0.0.243 | Redis 8.4.0 |
| 缓存层 | redis-master3 | 主节点 3 | 10.0.0.244 | Redis 8.4.0 |
| 缓存层 | redis-slave3 | 主机点 3 的从节点 | 10.0.0.245 | Redis 8.4.0 |
| 缓存层 | redis-master4 | 主节点 4 | 10.0.0.246 | Redis 8.4.0 |
| 缓存层 | redis-slave4 | 主机点 4 的从节点 | 10.0.0.247 | Redis 8.4.0 |
| 缓存层 | redis-master5 | 主节点 5 | 10.0.0.248 | Redis 8.4.0 |
| 缓存层 | redis-slave5 | 主机点 5 的从节点 | 10.0.0.249 | Redis 8.4.0 |
# 操作系统版本: Ubuntu 24.04
# 配置主机名
hostnamectl set-hostname redis-master1
hostnamectl set-hostname redis-slave1
hostnamectl set-hostname redis-master2
hostnamectl set-hostname redis-slave2
hostnamectl set-hostname redis-master3
hostnamectl set-hostname redis-slave3
hostnamectl set-hostname redis-master4
hostnamectl set-hostname redis-slave4
hostnamectl set-hostname redis-master5
hostnamectl set-hostname redis-slave5
# 配置 DNS 解析,修改所有主机 hosts 解析记录
cat >> /etc/hosts << 'EOF'
10.0.0.240 redis-master1
10.0.0.241 redis-slave1
10.0.0.242 redis-master2
10.0.0.243 redis-slave2
10.0.0.244 redis-master3
10.0.0.245 redis-slave3
10.0.0.246 redis-master4
10.0.0.247 redis-slave4
10.0.0.248 redis-master5
10.0.0.249 redis-slave5
EOF
5.3.2.2 部署单实例 Redis
vim deploy_redis.sh
#!/bin/bash
# *************************************
# * 功能: Redis 单实例部署脚本
# * 作者:
# * 联系:
# * 版本: 2026-08-25
# *************************************
# ====================== 颜色定义 ======================
RED='\\033[0;31m'
GREEN='\\033[0;32m'
YELLOW='\\033[1;33m'
BLUE='\\033[0;34m'
NC='\\033[0m' # 恢复默认颜色
# ====================== 全局配置 ======================
REDIS_VERSION="8.4.0"
REDIS_URL="https://download.redis.io/releases/redis-${REDIS_VERSION}.tar.gz"
SRC_BASE="/usr/local/src"
REDIS_SRC="${SRC_BASE}/redis-${REDIS_VERSION}"
CONF_DIR="/etc/redis"
REDIS_PORT="6379"
DATA_DIR="/var/lib/redis"
LOG_DIR="/var/log/redis"
RUN_DIR="/var/run/redis"
RUN_USER="redis"
MAIN_CONF="${CONF_DIR}/redis-${REDIS_PORT}.conf"
SYSTEMD_SVC="/etc/systemd/system/redis.service"
# ====================== 工具函数 ======================
info() {
echo –e "${GREEN}[INFO] $1${NC}"
}
warn() {
echo –e "${YELLOW}[WARN] $1${NC}"
}
error() {
echo –e "${RED}[ERROR] $1${NC}"
}
title() {
echo –e "\\n${BLUE}==================================== $1 ====================================${NC}"
}
# ====================== 功能函数 ======================
# 1. 安装编译依赖
install_deps() {
title "安装编译依赖包"
apt update –y
apt install –y —no-install-recommends ca-certificates wget gcc \\
g++ libc6-dev libssl-dev make automake autoconf libtool
apt install –y —no-install-recommends \\
build-essential pkg-config libssl-dev libjemalloc-dev libsystemd-dev
info "基础编译依赖安装完成"
}
# 2. 下载&解压源码
download_extract() {
title "下载并解压 Redis 源码"
mkdir –p ${SRC_BASE}
cd ${SRC_BASE}
if [ ! –f "redis-${REDIS_VERSION}.tar.gz" ]; then
info "开始下载 Redis ${REDIS_VERSION} 源码包"
wget –q ${REDIS_URL}
else
warn "源码包已存在,跳过下载"
fi
tar –xf redis-${REDIS_VERSION}.tar.gz
cd ${REDIS_SRC}
info "源码解压完成,当前目录: $(pwd)"
}
# 3. 编译 Redis(关闭模块/TLS,开启 systemd)
compile_redis() {
title "编译 Redis"
export BUILD_TLS=no
export BUILD_WITH_MODULES=no
export INSTALL_RUST_TOOLCHAIN=no
export DISABLE_WERRORS=yes
make –j "$(nproc)" all USE_SYSTEMD=yes
if [ $? -eq 0 ]; then
info "Redis 编译成功"
else
error "Redis 编译失败,退出脚本"
exit 1
fi
}
# 4. 安装二进制程序
install_bin() {
title "安装 Redis 二进制文件"
make install
}
# 5. 创建运行用户 & 目录
create_user_dir() {
title "创建运行用户与数据目录"
useradd –s /sbin/nologin ${RUN_USER} 2>/dev/null || warn "用户 ${RUN_USER} 已存在"
mkdir –p ${CONF_DIR} ${DATA_DIR} ${LOG_DIR} ${RUN_DIR}
chown –R ${RUN_USER}:${RUN_USER} ${CONF_DIR} ${DATA_DIR} ${LOG_DIR} ${RUN_DIR}
info "用户与目录创建&授权完成"
}
# 6. 初始化配置文件
init_config() {
title "初始化 Redis 配置文件"
cp ${REDIS_SRC}/redis.conf ${MAIN_CONF}
# 适配 systemd、关闭后台运行、修改pid文件
sed –i 's/^# supervised auto/supervised systemd/' ${MAIN_CONF}
sed –i "s|^pidfile.*|pidfile ${RUN_DIR}/redis_${REDIS_PORT}.pid|" ${MAIN_CONF}
info "主配置文件已生成: ${MAIN_CONF}"
}
# 7. 系统内核参数优化
sysctl_optimize() {
title "配置内存 overcommit 参数"
if ! grep –q 'vm.overcommit_memory = 1' /etc/sysctl.conf; then
echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf
fi
sysctl –p >/dev/null 2>&1
info "vm.overcommit_memory=1 已生效"
}
# 8. 配置 systemd 服务
config_systemd() {
title "配置 systemd 系统服务"
cat > ${SYSTEMD_SVC} <<–EOF
[Unit]
Description=Redis Data Structure Server
Documentation=https://redis.io/documentation
Wants=network-online.target
After=network-online.target
[Service]
ExecStart=/usr/local/bin/redis-server ${MAIN_CONF}
LimitNOFILE=65535
NoNewPrivileges=yes
OOMScoreAdjust=–900
PrivateTmp=yes
Type=notify
TimeoutStartSec=infinity
TimeoutStopSec=infinity
UMask=0077
User=${RUN_USER}
Group=${RUN_USER}
WorkingDirectory=${DATA_DIR}
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable redis
info "systemd 服务配置完成,已设置开机自启"
}
# 9. 启动服务 & 验证
start_and_check() {
title "启动 Redis 服务并验证"
systemctl restart redis
sleep 1
if systemctl is-active —quiet redis; then
info "Redis 服务启动成功!"
echo –e "\\n${BLUE}— 服务状态 —${NC}"
systemctl status redis —no-pager
echo –e "\\n${BLUE}— 连通性测试 —${NC}"
redis-cli ping
else
error "Redis 服务启动失败,请检查日志"
systemctl status redis —no-pager
exit 1
fi
}
# ====================== 主执行入口 ======================
main() {
if [ $(id –u) -ne 0 ]; then
error "请使用 root 权限执行此脚本!"
exit 1
fi
info "====== 开始执行 Redis ${REDIS_VERSION} 编译安装 ======"
install_deps
download_extract
compile_redis
install_bin
create_user_dir
init_config
sysctl_optimize
config_systemd
start_and_check
echo –e "\\n${GREEN}==================== 全部安装流程执行完毕 ====================${NC}"
}
# 调用主函数
main
bash deploy_redis.sh
5.3.2.3 修改节点配置
将配置文件 /etc/redis/redis-6379.conf 以下配置项
bind 127.0.0.1 –::1
logfile ""
dir ./
# save 3600 1 300 100 60 10000
appendonly no
appendfilename "appendonly.aof"
# cluster-enabled yes
# cluster-config-file nodes-6379.conf
# cluster-node-timeout 15000
# maxclients 10000
# requirepass foobared
# masterauth <master-password>
# maxmemory <bytes>
# maxmemory-policy noeviction
修改为
bind 0.0.0.0
logfile /var/log/redis/redis-6379.log
dir /var/lib/redis/6379/
save 3600 1
save 300 100
save 60 10000
appendonly yes
appendfilename appendonly-6379.aof
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 15000
maxclients 10000
requirepass Redis@2026
masterauth Redis@2026
maxmemory 1gb
maxmemory-policy allkeys-lru
# 注释原配置
sed –i.bak \\
–e '/^bind 127.0.0.1 -::1/s/^/#/' \\
–e '/^logfile ""/s/^/#/' \\
–e '/^dir \\./s/^/#/' \\
–e '/^appendonly no/s/^/#/' \\
–e '/^appendfilename "appendonly.aof"/s/^/#/' \\
/etc/redis/redis-6379.conf
# 验证
grep –E '^(bind|logfile|dir|appendonly|appendfilename)' /etc/redis/redis-6379.conf
# 配置文件末尾追加自定义配置
cat >> /etc/redis/redis-6379.conf <<'EOF'
# 本机监听 IP
bind 0.0.0.0
# 日志目录
logfile /var/log/redis/redis-6379.log
# 持久化文件目录
dir /var/lib/redis/6379/
# 定时快照规则
save 3600 1
save 300 100
save 60 10000
# 启用 AOF
appendonly yes
# AOF 文件名称
appendfilename appendonly-6379.aof
# 集群核心开关
cluster-enabled yes
# 集群节点配置文件(自动生成,无需手动编辑)
cluster-config-file nodes-6379.conf
# 节点超时时间
cluster-node-timeout 15000
# 最大客户端连接数
maxclients 10000
# 认证密码
requirepass Redis@2026
# 主从复制密码
masterauth Redis@2026
# 限制物理内存最大使用量
maxmemory 1gb
# 内存清理策略
maxmemory-policy allkeys-lru
EOF
# 创建相关目录并更改权限
mkdir –p /var/log/redis /var/lib/redis/6379
chown redis:redis /var/log/redis /var/lib/redis/6379
# 重启服务
systemctl restart redis.service
# 认证密码登录不警告
echo 'export REDISCLI_AUTH="Redis@2026"' > /etc/profile.d/redis.sh && source /etc/profile.d/redis.sh
5.3.2.4 创建集群
# 在任一节点执行,创建集群;–cluster-replicas 1, 每个主节点对应一个从节点,所有节点数据必须清空
redis-cli —cluster create 10.0.0.240:6379 10.0.0.242:6379 10.0.0.244:6379 10.0.0.245:6379 10.0.0.241:6379 10.0.0.243:6379 —cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
# 自动分配槽位
Master[0] –> Slots 0 – 5460
Master[1] –> Slots 5461 – 10922
Master[2] –> Slots 10923 – 16383
# 自动配置前一个节点为后一个节点的从
Adding replica 10.0.0.241:6379 to 10.0.0.240:6379
Adding replica 10.0.0.243:6379 to 10.0.0.242:6379
Adding replica 10.0.0.245:6379 to 10.0.0.244:6379
# 节点 id 信息
M: 93925ab45a5d5c2e04acff1653259d21217b72d9 10.0.0.240:6379
slots:[0-5460] (5461 slots) master
M: c582fed4124c7703cd3c696e35a282f57a5643bb 10.0.0.242:6379
slots:[5461-10922] (5462 slots) master
M: dee27391bab6d2d008aeee9e968e6c6994364cda 10.0.0.244:6379
slots:[10923-16383] (5461 slots) master
S: 144860c9caf84f0cfbde2c64deb15bedee79a939 10.0.0.245:6379
replicates dee27391bab6d2d008aeee9e968e6c6994364cda
S: 25dd799481a5d00607b8d55a524fadf6902bf5e8 10.0.0.241:6379
replicates 93925ab45a5d5c2e04acff1653259d21217b72d9
S: daca3ec0baa680c41a704a7bd8ae1a3f28877e86 10.0.0.243:6379
replicates c582fed4124c7703cd3c696e35a282f57a5643bb
# 输入 yes 同意自动创建方案,如果不是想要的主从对应关系,可以输入 no 后调整命令后三个节点顺序达到满意效果
Can I set the above configuration? (type 'yes' to accept):yes
# 同步更新所有节点的集群配置文件
>>> Nodes configuration updated
# 为每个节点分配独立集群纪元(用于集群冲突判定、主从切换)
>>> Assign a different config epoch to each node
# 发送CLUSTER MEET指令,让所有节点互相发现、组建集群
>>> Sending CLUSTER MEET messages to join the cluster
# 等待节点完成互相握手组网
Waiting for the cluster to join
# 以6379节点为基准,校验整个集群完整性
>>> Performing Cluster Check (using node 10.0.0.240:6379)
M: 93925ab45a5d5c2e04acff1653259d21217b72d9 10.0.0.240:6379
slots:[0-5460] (5461 slots) master# 主节点信息
1 additional replica(s) # 附带1个副本节点
M: c582fed4124c7703cd3c696e35a282f57a5643bb 10.0.0.242:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 25dd799481a5d00607b8d55a524fadf6902bf5e8 10.0.0.241:6379
slots: (0 slots) slave# 从节点信息
replicates 93925ab45a5d5c2e04acff1653259d21217b72d9 # 对应主节点 id
S: 144860c9caf84f0cfbde2c64deb15bedee79a939 10.0.0.245:6379
slots: (0 slots) slave
replicates dee27391bab6d2d008aeee9e968e6c6994364cda
S: daca3ec0baa680c41a704a7bd8ae1a3f28877e86 10.0.0.243:6379
slots: (0 slots) slave
replicates c582fed4124c7703cd3c696e35a282f57a5643bb
M: dee27391bab6d2d008aeee9e968e6c6994364cda 10.0.0.244:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
# 校验通过:所有节点槽位分配信息完全一致
[OK] All nodes agree about slots configuration.
# 检查是否存在未分配、孤立的空槽位
>>> Check for open slots...
# 完整校验 16384 个哈希槽是否全部分配完毕无缺失
>>> Check slots coverage...
# 集群创建成功:全部16384个槽位分配完整,集群可用
[OK] All 16384 slots covered.
5.3.2.5 查看集群信息
# 查看所有主从节点、ID、槽位、主从关系
redis-cli –c cluster nodes
c582fed4124c7703cd3c696e35a282f57a5643bb 10.0.0.242:6379@16379 master – 0 1788526685000 2 connected 5461-10922
25dd799481a5d00607b8d55a524fadf6902bf5e8 10.0.0.241:6379@16379 slave 93925ab45a5d5c2e04acff1653259d21217b72d9 0 1788526682000 1 connected
144860c9caf84f0cfbde2c64deb15bedee79a939 10.0.0.245:6379@16379 slave dee27391bab6d2d008aeee9e968e6c6994364cda 0 1788526684000 3 connected
daca3ec0baa680c41a704a7bd8ae1a3f28877e86 10.0.0.243:6379@16379 slave c582fed4124c7703cd3c696e35a282f57a5643bb 0 1788526683969 2 connected
93925ab45a5d5c2e04acff1653259d21217b72d9 10.0.0.240:6379@16379 myself,master – 0 0 1 connected 0-5460
dee27391bab6d2d008aeee9e968e6c6994364cda 10.0.0.244:6379@16379 master – 0 1788526685983 3 connected 10923-16383
# 查看集群整体状态(槽位总数、集群状态、节点数量)
redis-cli –c cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:578
cluster_stats_messages_pong_sent:614
cluster_stats_messages_sent:1192
cluster_stats_messages_ping_received:609
cluster_stats_messages_pong_received:578
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:1192
total_cluster_links_buffer_limit_exceeded:0
cluster_slot_migration_active_tasks:0
cluster_slot_migration_active_trim_running:0
cluster_slot_migration_active_trim_current_job_keys:0
cluster_slot_migration_active_trim_current_job_trimmed:0
cluster_slot_migration_stats_active_trim_started:0
cluster_slot_migration_stats_active_trim_completed:0
cluster_slot_migration_stats_active_trim_cancelled:0
# 外部校验集群完整性
redis-cli —cluster check 10.0.0.240:6379
10.0.0.240:6379 (93925ab4...) –> 0 keys | 5461 slots | 1 slaves.
10.0.0.242:6379 (c582fed4...) –> 0 keys | 5462 slots | 1 slaves.
10.0.0.244:6379 (dee27391...) –> 0 keys | 5461 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 10.0.0.240:6379)
M: 93925ab45a5d5c2e04acff1653259d21217b72d9 10.0.0.240:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: c582fed4124c7703cd3c696e35a282f57a5643bb 10.0.0.242:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 25dd799481a5d00607b8d55a524fadf6902bf5e8 10.0.0.241:6379
slots: (0 slots) slave
replicates 93925ab45a5d5c2e04acff1653259d21217b72d9
S: 144860c9caf84f0cfbde2c64deb15bedee79a939 10.0.0.245:6379
slots: (0 slots) slave
replicates dee27391bab6d2d008aeee9e968e6c6994364cda
S: daca3ec0baa680c41a704a7bd8ae1a3f28877e86 10.0.0.243:6379
slots: (0 slots) slave
replicates c582fed4124c7703cd3c696e35a282f57a5643bb
M: dee27391bab6d2d008aeee9e968e6c6994364cda 10.0.0.244:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
5.3.2.6 数据写入读取
-c : 当读写数据槽位不位于此节点时自动跳转到对应节点
# 使用 -c 选项连接从节点,写入自动跳转槽位对应主节点
redis-cli –c –h 10.0.0.243
10.0.0.243:6379> set name zhangsan
# name 经过 CRC16 计算得到槽位 5798,该槽位归属主节点 10.0.0.242,集群模式 -c 自动跳转连接 10.0.0.242 执行写入
–> Redirected to slot [5798] located at 10.0.0.242:6379
OK # 写入成功,客户端会话自动切换到 10.0.0.242 节点
10.0.0.242:6379> set age 32
–> Redirected to slot [741] located at 10.0.0.240:6379
OK
10.0.0.240:6379> set user1 lisi
–> Redirected to slot [8106] located at 10.0.0.242:6379
OK
# 连接 10.0.0.242 主节点
redis-cli –c –h 10.0.0.242
# name 的哈希槽位于 10.0.0.242 主节点,无需跳转,直接本地读取
10.0.0.242:6379> get name
"zhangsan"
10.0.0.242:6379> get age
# age 的哈希槽位于 10.0.0.240 主节点,需要跳转读取
–> Redirected to slot [741] located at 10.0.0.240:6379
"32"
10.0.0.240:6379> get user1
–> Redirected to slot [8106] located at 10.0.0.242:6379
"lisi"
# readonly 开启在当前会话读取本节点数据,不会跳转到主节点,实现读写分离,一般在业务代码配置
redis-cli –c –h 10.0.0.243
10.0.0.243:6379> readonly
OK
10.0.0.243:6379> get name
"zhangsan"
10.0.0.243:6379> get age
–> Redirected to slot [741] located at 10.0.0.240:6379
"32"
10.0.0.240:6379>
哈希标签批量写入数据强制所有 key 落到同一个槽
# 以哈希槽 {goods:100} 做 hash 运行,槽位完全一致
10.0.0.240:6379> mset {goods:100} zuo:k1 v1 {goods:100}:k2 v2 {goods:100}:k3 v3
OK
# 读取
10.0.0.240:6379> mget {goods:100}:k1 {goods:100}:k2 {goods:100}:k3
1) "v1"
2) "v2"
3) "v3"
5.3.2.7 集群数据校验
# 连接集群任一节点校验
redis-cli —cluster check 10.0.0.240:6379
# 统计3台主节点数据概况:key数量、槽位总数、从节点数量
# 存储4个key,负责5461个哈希槽,配有1台从节点
10.0.0.240:6379 (93925ab4...) –> 4 keys | 5461 slots | 1 slaves.
# 存储2个key,负责5462个哈希槽,配有1台从节点
10.0.0.242:6379 (c582fed4...) –> 2 keys | 5462 slots | 1 slaves.
# 存储0个key,负责5461个哈希槽,配有1台从节点
10.0.0.244:6379 (dee27391...) –> 0 keys | 5461 slots | 1 slaves.
# 汇总校验:3台主节点合计存在6条key,集群数据总量确认
[OK] 6 keys in 3 masters.
# 平均每个哈希槽存放0条数据(测试数据量极少)
0.00 keys per slot on average.
# 以 10.0.0.240:6379 节点为基准,深度遍历全集群拓扑、槽位分配、主从关系
>>> Performing Cluster Check (using node 10.0.0.240:6379)
# M=Master主节点:10.0.0.240 节点ID、IP端口,负责槽位0~5460,拥有1个副本
M: 93925ab45a5d5c2e04acff1653259d21217b72d9 10.0.0.240:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: c582fed4124c7703cd3c696e35a282f57a5643bb 10.0.0.242:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
# S=Slave从节点:10.0.0.241 是 10.0.0.240 的副本,不分配独立槽位,同步主节点全部数据;主节点宕机后,对应从节点会自动升级为主节点接管槽位
S: 25dd799481a5d00607b8d55a524fadf6902bf5e8 10.0.0.241:6379
slots: (0 slots) slave
replicates 93925ab45a5d5c2e04acff1653259d21217b72d9 # ID 对应 10.0.0.240 主节点 ID
S: 144860c9caf84f0cfbde2c64deb15bedee79a939 10.0.0.245:6379
slots: (0 slots) slave
replicates dee27391bab6d2d008aeee9e968e6c6994364cda
S: daca3ec0baa680c41a704a7bd8ae1a3f28877e86 10.0.0.243:6379
slots: (0 slots) slave
replicates c582fed4124c7703cd3c696e35a282f57a5643bb
M: dee27391bab6d2d008aeee9e968e6c6994364cda 10.0.0.244:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
# 校验通过:所有节点保存的槽位分配信息完全一致,无分歧
[OK] All nodes agree about slots configuration.
# 检查是否存在孤立、未绑定主节点的空槽位
>>> Check for open slots...
# 完整校验全部16384个哈希槽是否都分配给主节点,无遗漏
>>> Check slots coverage...
# 最终核心校验通过标志:集群完整可用,无槽位缺失、集群状态OK
[OK] All 16384 slots covered.
5.3.2.8 集群管理
集群配置参数 cluster-node-timeout 15000 ,节点失联超过 15 秒判定故障,从节点自动发起选举升级为主节点,接管对应槽位,实现故障转移
# 关闭 10.0.0.240 主节点
[root@redis-master1 ~ ]#systemctl stop redis.service
redis-cli –c –h 10.0.0.242 cluster nodes
c582fed4124c7703cd3c696e35a282f57a5643bb 10.0.0.242:6379@16379 myself,master – 0 0 2 connected 5461-10922
# 原从 10.0.0.241 状态变成 master,并且绑定了完整槽位 0-5460
25dd799481a5d00607b8d55a524fadf6902bf5e8 10.0.0.241:6379@16379 master – 0 1788528951174 7 connected 0-5460
daca3ec0baa680c41a704a7bd8ae1a3f28877e86 10.0.0.243:6379@16379 slave c582fed4124c7703cd3c696e35a282f57a5643bb 0 1788528953188 2 connected
144860c9caf84f0cfbde2c64deb15bedee79a939 10.0.0.245:6379@16379 slave dee27391bab6d2d008aeee9e968e6c6994364cda 0 1788528950165 3 connected
# 原主 10.0.0.240 标记 master,fail + disconnected,代表集群判定 10.0.0.240 故障失联,不再持有任何槽位
93925ab45a5d5c2e04acff1653259d21217b72d9 10.0.0.240:6379@16379 master,fail – 1788528899769 1788528898000 1 disconnected
dee27391bab6d2d008aeee9e968e6c6994364cda 10.0.0.244:6379@16379 master – 0 1788528952182 3 connected 10923-16383
验证故障后读写可用性
# 原存储于 10.0.0.240 的 key,正常写入 10.0.0.241 新主节点
[root@redis-master1 ~ ]#redis-cli -c -h 10.0.0.242
10.0.0.242:6379> set age 30
–> Redirected to slot [741] located at 10.0.0.241:6379
OK
10.0.0.241:6379> get age
"30"
恢复故障原主节点
systemctl start redis.service
# 查看集群节点状态
redis-cli –c –h 10.0.0.242 cluster nodes
c582fed4124c7703cd3c696e35a282f57a5643bb 10.0.0.242:6379@16379 myself,master – 0 0 2 connected 5461-10922
25dd799481a5d00607b8d55a524fadf6902bf5e8 10.0.0.241:6379@16379 master – 0 1788529291842 7 connected 0-5460
daca3ec0baa680c41a704a7bd8ae1a3f28877e86 10.0.0.243:6379@16379 slave c582fed4124c7703cd3c696e35a282f57a5643bb 0 1788529291000 2 connected
144860c9caf84f0cfbde2c64deb15bedee79a939 10.0.0.245:6379@16379 slave dee27391bab6d2d008aeee9e968e6c6994364cda 0 1788529292849 3 connected
# 原故障主节点 10.0.0.240 重启后,自动变成 10.0.0.241 的从节点,不抢占槽位
93925ab45a5d5c2e04acff1653259d21217b72d9 10.0.0.240:6379@16379 slave 25dd799481a5d00607b8d55a524fadf6902bf5e8 0 1788529290836 7 connected
dee27391bab6d2d008aeee9e968e6c6994364cda 10.0.0.244:6379@16379 master – 0 1788529293856 3 connected 10923-16383
# 校验集群状态,结果正常
redis-cli —cluster check 10.0.0.240:6379
10.0.0.241:6379 (25dd7994...) –> 4 keys | 5461 slots | 1 slaves.
10.0.0.244:6379 (dee27391...) –> 0 keys | 5461 slots | 1 slaves.
10.0.0.242:6379 (c582fed4...) –> 2 keys | 5462 slots | 1 slaves.
[OK] 6 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 10.0.0.240:6379)
S: 93925ab45a5d5c2e04acff1653259d21217b72d9 10.0.0.240:6379
slots: (0 slots) slave
replicates 25dd799481a5d00607b8d55a524fadf6902bf5e8
M: 25dd799481a5d00607b8d55a524fadf6902bf5e8 10.0.0.241:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: dee27391bab6d2d008aeee9e968e6c6994364cda 10.0.0.244:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: daca3ec0baa680c41a704a7bd8ae1a3f28877e86 10.0.0.243:6379
slots: (0 slots) slave
replicates c582fed4124c7703cd3c696e35a282f57a5643bb
S: 144860c9caf84f0cfbde2c64deb15bedee79a939 10.0.0.245:6379
slots: (0 slots) slave
replicates dee27391bab6d2d008aeee9e968e6c6994364cda
M: c582fed4124c7703cd3c696e35a282f57a5643bb 10.0.0.242:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
5.3.2.9 集群扩容
5.3.2.9.1 将新主节点加入集群
redis-cli –h <集群任意节点> —cluster add-node <新增主节点IP>:6379 <集群任意节点>:6379
# 添加主节点到集群
redis-cli –h 10.0.0.244 —cluster add-node 10.0.0.246:6379 10.0.0.244:6379
# 开始将新节点 10.0.0.246:6379 加入集群(集群入口节点为10.0.0.244:6379)
>>> Adding node 10.0.0.246:6379 to cluster 10.0.0.244:6379
# 以 10.0.0.244:6379 为基准,校验当前集群完整拓扑
>>> Performing Cluster Check (using node 10.0.0.244:6379)
M: dee27391bab6d2d008aeee9e968e6c6994364cda 10.0.0.244:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: 25dd799481a5d00607b8d55a524fadf6902bf5e8 10.0.0.241:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: c582fed4124c7703cd3c696e35a282f57a5643bb 10.0.0.242:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 93925ab45a5d5c2e04acff1653259d21217b72d9 10.0.0.240:6379
slots: (0 slots) slave
replicates 25dd799481a5d00607b8d55a524fadf6902bf5e8
S: 144860c9caf84f0cfbde2c64deb15bedee79a939 10.0.0.245:6379
slots: (0 slots) slave
replicates dee27391bab6d2d008aeee9e968e6c6994364cda
S: daca3ec0baa680c41a704a7bd8ae1a3f28877e86 10.0.0.243:6379
slots: (0 slots) slave
replicates c582fed4124c7703cd3c696e35a282f57a5643bb
# 校验通过:集群所有节点的槽位分配信息完全一致
[OK] All nodes agree about slots configuration.
# 检查是否存在孤立、未绑定的空槽位
>>> Check for open slots...
# 校验全部16384个哈希槽是否完整分配
>>> Check slots coverage...
# 校验通过:16384个槽全部分配完毕,集群原始状态健康
[OK] All 16384 slots covered.
# 同步集群内Lua函数信息
>>> Getting functions from cluster
# 向新节点 10.0.0.246:6379 发送指令,确认其无自定义 Lua 函数
>>> Send FUNCTION LIST to 10.0.0.246:6379 to verify there is no functions in it
# 将集群现有 Lua 函数同步恢复至新节点 10.0.0.246:6379
>>> Send FUNCTION RESTORE to 10.0.0.246:6379
# 发送 CLUSTER MEET 握手指令,让 10.0.0.246:6379 和集群所有节点互相识别,正式加入集群
>>> Send CLUSTER MEET to node 10.0.0.246:6379 to make it join the cluster.
# 执行成功:新节点已正确加入集群
[OK] New node added correctly.
# 将另一个主节点添加到集群
redis-cli –h 10.0.0.244 —cluster add-node 10.0.0.248:6379 10.0.0.244:6379
# 查看新加入主节点信息
redis-cli –h 10.0.0.244 cluster nodes
25dd799481a5d00607b8d55a524fadf6902bf5e8 10.0.0.241:6379@16379 master – 0 1788530211680 7 connected 0-5460
c582fed4124c7703cd3c696e35a282f57a5643bb 10.0.0.242:6379@16379 slave daca3ec0baa680c41a704a7bd8ae1a3f28877e86 0 1788530212687 8 connected
93925ab45a5d5c2e04acff1653259d21217b72d9 10.0.0.240:6379@16379 slave 25dd799481a5d00607b8d55a524fadf6902bf5e8 0 1788530209000 7 connected
144860c9caf84f0cfbde2c64deb15bedee79a939 10.0.0.245:6379@16379 slave dee27391bab6d2d008aeee9e968e6c6994364cda 0 1788530209666 3 connected
e37bd3b9d926f3b6a17f76d1537252c2b49d3b02 10.0.0.248:6379@16379 master – 0 1788530212000 0 connected
dee27391bab6d2d008aeee9e968e6c6994364cda 10.0.0.244:6379@16379 myself,master – 0 0 3 connected 10923-16383
d57e98adbf7d252961418dab485d25212d223753 10.0.0.246:6379@16379 master – 0 1788530210000 0 connected
daca3ec0baa680c41a704a7bd8ae1a3f28877e86 10.0.0.243:6379@16379 master – 0 1788530210673 8 connected 5461-10922
# 如果长时间集群其余主节点,Gossip 没同步到这个新主节点,手动强制触发节点握手(加速同步)
# 在集群任意老主节点,主动 meet 新节点
redis-cli –h 10.0.0.244 cluster meet 10.0.0.248 6379
5.3.2.9.2 重新分配槽位
redis-cli –h <集群任意节点> —cluster reshard <集群任意节点>:6379
自动均分槽位,迁移 16384/5≈3278 个槽到新主 10.0.0.246
# 连接集群任一节点开始重新分配槽位
redis-cli –h 10.0.0.244 —cluster reshard 10.0.0.244:6379
# 先自动校验集群整体状态
>>> Performing Cluster Check (using node 10.0.0.244:6379)
M: dee27391bab6d2d008aeee9e968e6c6994364cda 10.0.0.244:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: 25dd799481a5d00607b8d55a524fadf6902bf5e8 10.0.0.241:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: c582fed4124c7703cd3c696e35a282f57a5643bb 10.0.0.242:6379
slots: (0 slots) slave
replicates daca3ec0baa680c41a704a7bd8ae1a3f28877e86
S: 93925ab45a5d5c2e04acff1653259d21217b72d9 10.0.0.240:6379
slots: (0 slots) slave
replicates 25dd799481a5d00607b8d55a524fadf6902bf5e8
S: 144860c9caf84f0cfbde2c64deb15bedee79a939 10.0.0.245:6379
slots: (0 slots) slave
replicates dee27391bab6d2d008aeee9e968e6c6994364cda
# 新节点 10.0.0.248 当前为主节点,暂时未分配任何哈希槽
M: e37bd3b9d926f3b6a17f76d1537252c2b49d3b02 10.0.0.248:6379
slots: (0 slots) master
# 新节点 10.0.0.246 当前为主节点,暂时未分配任何哈希槽
M: d57e98adbf7d252961418dab485d25212d223753 10.0.0.246:6379
slots: (0 slots) master
M: daca3ec0baa680c41a704a7bd8ae1a3f28877e86 10.0.0.243:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
# 校验1:所有节点槽位分配信息完全一致,无分歧
[OK] All nodes agree about slots configuration.
# 校验2:检查是否存在孤立、未归属的空槽
>>> Check for open slots...
# 校验3:确认16384个哈希槽全部有主节点承载,集群基础状态正常
>>> Check slots coverage...
# 最终核心校验通过标志:集群完整可用,无槽位缺失、集群状态OK
[OK] All 16384 slots covered.
# 交互提问1:需要迁移多少个哈希槽到新节点
How many slots do you want to move (from 1 to 16384)?3278
# 交互提问2:接收槽位的节点ID,粘贴 10.0.0.246 主节点 ID
What is the receiving node ID?d57e98adbf7d252961418dab485d25212d223753
# 交互提示:输入槽位来源节点ID
# all = 自动从全部现有3台主节点均匀抽取槽;也可手动逐个输入节点ID,输入done结束
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1:all
# 程序自动生成分片迁移方案,展示源节点、目标节点信息
Ready to move 3278 slots.
Source nodes:
M: dee27391bab6d2d008aeee9e968e6c6994364cda 10.0.0.244:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: 25dd799481a5d00607b8d55a524fadf6902bf5e8 10.0.0.241:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: e37bd3b9d926f3b6a17f76d1537252c2b49d3b02 10.0.0.248:6379
slots: (0 slots) master
M: daca3ec0baa680c41a704a7bd8ae1a3f28877e86 10.0.0.243:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
Destination node:
M: d57e98adbf7d252961418dab485d25212d223753 10.0.0.246:6379
slots: (0 slots) master
# 分片迁移计划:逐条列出要移动的槽号、来源主节点 ID
Resharding plan:
Moving slot 5461 from daca3ec0baa680c41a704a7bd8ae1a3f28877e86
...
Moving slot 1091 from 25dd799481a5d00607b8d55a524fadf6902bf5e8
# 确认是否执行本次槽位迁移计划,yes开始迁移,no放弃退出
Do you want to proceed with the proposed reshard plan (yes/no)?yes
# 输入 yes 等待槽位迁移完自动退出
Moving slot 5461 from 10.0.0.243:6379 to 10.0.0.246:6379:
...
Moving slot 1091 from 10.0.0.241:6379 to 10.0.0.246:6379:
执行新增主节点 10.0.0.248 槽位分配,依然从原有三个主节点分配槽位,3278/3=1092.66, 2 个主节点分配 1093 个槽位,一个主节点分配 1092 个槽位
redis-cli –h 10.0.0.244 —cluster reshard 10.0.0.244:6379
# 填写第一个主节点重新分配的槽位数
How many slots do you want to move (from 1 to 16384)? 1093
# 添加接收主节点 10.0.0.248 ID
What is the receiving node ID? e37bd3b9d926f3b6a17f76d1537252c2b49d3b02
# 填写第一个主节点 10.0.0.240 ID
Source node #1: 25dd799481a5d00607b8d55a524fadf6902bf5e8
# 完成
Source node #2: done
# 确认重新分配
Do you want to proceed with the proposed reshard plan (yes/no)? yes
# 再重复执行 2 次,从剩余 2 个原主节点重新分配槽位
# 查看节点及槽位信息
redis-cli –h 10.0.0.244 cluster nodes
c582fed4124c7703cd3c696e35a282f57a5643bb 10.0.0.242:6379@16379 slave daca3ec0baa680c41a704a7bd8ae1a3f28877e86 0 1788533573000 13 connected
25dd799481a5d00607b8d55a524fadf6902bf5e8 10.0.0.241:6379@16379 master – 0 1788533573961 12 connected 2185-5460
144860c9caf84f0cfbde2c64deb15bedee79a939 10.0.0.245:6379@16379 slave dee27391bab6d2d008aeee9e968e6c6994364cda 0 1788533573000 14 connected
dee27391bab6d2d008aeee9e968e6c6994364cda 10.0.0.244:6379@16379 myself,master – 0 0 14 connected 13107-16383
93925ab45a5d5c2e04acff1653259d21217b72d9 10.0.0.240:6379@16379 slave 25dd799481a5d00607b8d55a524fadf6902bf5e8 0 1788533572955 12 connected
edca22192be476251b8617a88caaf2eb0af5d259 10.0.0.249:6379@16379 slave e37bd3b9d926f3b6a17f76d1537252c2b49d3b02 0 1788533572000 15 connected
e37bd3b9d926f3b6a17f76d1537252c2b49d3b02 10.0.0.248:6379@16379 master – 0 1788533571950 15 connected 1092-2184 6554-7646 12015-13106
607600889cc8fa7c5565e5eb57e8454f6f013f23 10.0.0.247:6379@16379 slave d57e98adbf7d252961418dab485d25212d223753 0 1788533569000 11 connected
daca3ec0baa680c41a704a7bd8ae1a3f28877e86 10.0.0.243:6379@16379 master – 0 1788533574969 13 connected 7647-10922
d57e98adbf7d252961418dab485d25212d223753 10.0.0.246:6379@16379 master – 0 1788533569535 11 connected 0-1091 5461-6553 10923-12014
5.3.2.9.3 为新主节点添加从节点
redis-cli –h <集群任意节点> —cluster add-node <新增从节点IP>:6379 <集群任意节点>:6379 —cluster-slave —cluster-master-id <新主节点id>
# 将 10.0.0.247:6379 节点绑定为 10.0.0.246:6379 的副本
# 10.0.0.246:6379 ID : d57e98adbf7d252961418dab485d25212d223753
redis-cli –h 10.0.0.244 —cluster add-node 10.0.0.247:6379 10.0.0.244:6379 —cluster-slave —cluster-master-id d57e98adbf7d252961418dab485d25212d223753
>>> Adding node 10.0.0.247:6379 to cluster 10.0.0.244:6379
>>> Performing Cluster Check (using node 10.0.0.244:6379)
M: dee27391bab6d2d008aeee9e968e6c6994364cda 10.0.0.244:6379
slots:[12890-16383] (3494 slots) master
1 additional replica(s)
M: 25dd799481a5d00607b8d55a524fadf6902bf5e8 10.0.0.241:6379
slots:[1966-5460] (3495 slots) master
1 additional replica(s)
S: c582fed4124c7703cd3c696e35a282f57a5643bb 10.0.0.242:6379
slots: (0 slots) slave
replicates daca3ec0baa680c41a704a7bd8ae1a3f28877e86
S: 93925ab45a5d5c2e04acff1653259d21217b72d9 10.0.0.240:6379
slots: (0 slots) slave
replicates 25dd799481a5d00607b8d55a524fadf6902bf5e8
S: 144860c9caf84f0cfbde2c64deb15bedee79a939 10.0.0.245:6379
slots: (0 slots) slave
replicates dee27391bab6d2d008aeee9e968e6c6994364cda
M: e37bd3b9d926f3b6a17f76d1537252c2b49d3b02 10.0.0.248:6379
slots:[0-654],[1092-1965],[6554-7427],[12015-12889] (3278 slots) master
M: d57e98adbf7d252961418dab485d25212d223753 10.0.0.246:6379
slots:[655-1091],[5461-6553],[10923-12014] (2622 slots) master
M: daca3ec0baa680c41a704a7bd8ae1a3f28877e86 10.0.0.243:6379
slots:[7428-10922] (3495 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 10.0.0.247:6379 to make it join the cluster.
Waiting for the cluster to join
>>> Configure node as replica of 10.0.0.246:6379.
[OK] New node added correctly.
# 将 10.0.0.249:6379 节点绑定为 10.0.0.248:6379 的副本
redis-cli –h 10.0.0.244 —cluster add-node 10.0.0.249:6379 10.0.0.244:6379 —cluster-slave —cluster-master-id e37bd3b9d926f3b6a17f76d1537252c2b49d3b02
# 查看节点信息
redis-cli –h 10.0.0.244 cluster nodes
c582fed4124c7703cd3c696e35a282f57a5643bb 10.0.0.242:6379@16379 slave daca3ec0baa680c41a704a7bd8ae1a3f28877e86 0 1788533573000 13 connected
25dd799481a5d00607b8d55a524fadf6902bf5e8 10.0.0.241:6379@16379 master – 0 1788533573961 12 connected 2185-5460
144860c9caf84f0cfbde2c64deb15bedee79a939 10.0.0.245:6379@16379 slave dee27391bab6d2d008aeee9e968e6c6994364cda 0 1788533573000 14 connected
dee27391bab6d2d008aeee9e968e6c6994364cda 10.0.0.244:6379@16379 myself,master – 0 0 14 connected 13107-16383
93925ab45a5d5c2e04acff1653259d21217b72d9 10.0.0.240:6379@16379 slave 25dd799481a5d00607b8d55a524fadf6902bf5e8 0 1788533572955 12 connected
edca22192be476251b8617a88caaf2eb0af5d259 10.0.0.249:6379@16379 slave e37bd3b9d926f3b6a17f76d1537252c2b49d3b02 0 1788533572000 15 connected
e37bd3b9d926f3b6a17f76d1537252c2b49d3b02 10.0.0.248:6379@16379 master – 0 1788533571950 15 connected 1092-2184 6554-7646 12015-13106
607600889cc8fa7c5565e5eb57e8454f6f013f23 10.0.0.247:6379@16379 slave d57e98adbf7d252961418dab485d25212d223753 0 1788533569000 11 connected
daca3ec0baa680c41a704a7bd8ae1a3f28877e86 10.0.0.243:6379@16379 master – 0 1788533574969 13 connected 7647-10922
d57e98adbf7d252961418dab485d25212d223753 10.0.0.246:6379@16379 master – 0 1788533569535 11 connected 0-1091 5461-6553 10923-12014
# 查看集群校验信息
redis-cli —cluster check 10.0.0.240:6379
10.0.0.246:6379 (d57e98ad...) –> 2 keys | 3277 slots | 1 slaves.
10.0.0.244:6379 (dee27391...) –> 0 keys | 3277 slots | 1 slaves.
10.0.0.248:6379 (e37bd3b9...) –> 0 keys | 3278 slots | 1 slaves.
10.0.0.241:6379 (25dd7994...) –> 3 keys | 3276 slots | 1 slaves.
10.0.0.243:6379 (daca3ec0...) –> 1 keys | 3276 slots | 1 slaves.
[OK] 6 keys in 5 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 10.0.0.240:6379)
S: 93925ab45a5d5c2e04acff1653259d21217b72d9 10.0.0.240:6379
slots: (0 slots) slave
replicates 25dd799481a5d00607b8d55a524fadf6902bf5e8
M: d57e98adbf7d252961418dab485d25212d223753 10.0.0.246:6379
slots:[0-1091],[5461-6553],[10923-12014] (3277 slots) master
1 additional replica(s)
M: dee27391bab6d2d008aeee9e968e6c6994364cda 10.0.0.244:6379
slots:[13107-16383] (3277 slots) master
1 additional replica(s)
S: edca22192be476251b8617a88caaf2eb0af5d259 10.0.0.249:6379
slots: (0 slots) slave
replicates e37bd3b9d926f3b6a17f76d1537252c2b49d3b02
S: 144860c9caf84f0cfbde2c64deb15bedee79a939 10.0.0.245:6379
slots: (0 slots) slave
replicates dee27391bab6d2d008aeee9e968e6c6994364cda
M: e37bd3b9d926f3b6a17f76d1537252c2b49d3b02 10.0.0.248:6379
slots:[1092-2184],[6554-7646],[12015-13106] (3278 slots) master
1 additional replica(s)
M: 25dd799481a5d00607b8d55a524fadf6902bf5e8 10.0.0.241:6379
slots:[2185-5460] (3276 slots) master
1 additional replica(s)
S: 607600889cc8fa7c5565e5eb57e8454f6f013f23 10.0.0.247:6379
slots: (0 slots) slave
replicates d57e98adbf7d252961418dab485d25212d223753
M: daca3ec0baa680c41a704a7bd8ae1a3f28877e86 10.0.0.243:6379
slots:[7647-10922] (3276 slots) master
1 additional replica(s)
S: c582fed4124c7703cd3c696e35a282f57a5643bb 10.0.0.242:6379
slots: (0 slots) slave
replicates daca3ec0baa680c41a704a7bd8ae1a3f28877e86
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
5.3.2.10 集群缩容
5.3.2.10.1 重新分配槽位
# 连接集群任一节点开始重新分配槽位
redis-cli –h 10.0.0.244 —cluster reshard 10.0.0.244:6379
# 如果是扩容节点缩减,可根据扩容时分配的槽位信息归还给原主节点,数量填写槽位信息计算出的数量
How many slots do you want to move (from 1 to 16384)?1093
# 如果是扩容节点缩减,将槽位还给原主节点,此处填写原主节点 id
What is the receiving node ID? 25dd799481a5d00607b8d55a524fadf6902bf5e8
# 填写缩容主节点 id
Source node #1: e37bd3b9d926f3b6a17f76d1537252c2b49d3b02
Source node #2: done
Do you want to proceed with the proposed reshard plan (yes/no)? yes
# 重复执行,将所有槽位重新分配给原主节点
5.3.2.10.2 从集群删除节点
redis-cli –h <集群任意节点> —cluster del–node <任意集群节点的IP>:6379 需要删除节点的id
# 查看集群信息
redis-cli —cluster check 10.0.0.240:6379
10.0.0.246:6379 (d57e98ad...) –> 2 keys | 3277 slots | 1 slaves.
# 此时移动槽位完成的这组主从节点自动变成其他主节点的从节点
10.0.0.244:6379 (dee27391...) –> 0 keys | 4369 slots | 3 slaves.
10.0.0.241:6379 (25dd7994...) –> 3 keys | 4369 slots | 1 slaves.
10.0.0.243:6379 (daca3ec0...) –> 1 keys | 4369 slots | 1 slaves.
[OK] 6 keys in 4 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 10.0.0.240:6379)
S: 93925ab45a5d5c2e04acff1653259d21217b72d9 10.0.0.240:6379
slots: (0 slots) slave
replicates 25dd799481a5d00607b8d55a524fadf6902bf5e8
M: d57e98adbf7d252961418dab485d25212d223753 10.0.0.246:6379
slots:[0-1091],[5461-6553],[10923-12014] (3277 slots) master
1 additional replica(s)
M: dee27391bab6d2d008aeee9e968e6c6994364cda 10.0.0.244:6379
slots:[12015-16383] (4369 slots) master
3 additional replica(s)
# 变为 10.0.0.244 的从节点
S: edca22192be476251b8617a88caaf2eb0af5d259 10.0.0.249:6379
slots: (0 slots) slave
replicates dee27391bab6d2d008aeee9e968e6c6994364cda
S: 144860c9caf84f0cfbde2c64deb15bedee79a939 10.0.0.245:6379
slots: (0 slots) slave
replicates dee27391bab6d2d008aeee9e968e6c6994364cda
# 变为 10.0.0.244 的从节点
S: e37bd3b9d926f3b6a17f76d1537252c2b49d3b02 10.0.0.248:6379
slots: (0 slots) slave
replicates dee27391bab6d2d008aeee9e968e6c6994364cda
M: 25dd799481a5d00607b8d55a524fadf6902bf5e8 10.0.0.241:6379
slots:[1092-5460] (4369 slots) master
1 additional replica(s)
S: 607600889cc8fa7c5565e5eb57e8454f6f013f23 10.0.0.247:6379
slots: (0 slots) slave
replicates d57e98adbf7d252961418dab485d25212d223753
M: daca3ec0baa680c41a704a7bd8ae1a3f28877e86 10.0.0.243:6379
slots:[6554-10922] (4369 slots) master
1 additional replica(s)
S: c582fed4124c7703cd3c696e35a282f57a5643bb 10.0.0.242:6379
slots: (0 slots) slave
replicates daca3ec0baa680c41a704a7bd8ae1a3f28877e86
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
# 删除这组主从节点
redis-cli –h 10.0.0.244 —cluster del–node 10.0.0.244:6379 edca22192be476251b8617a88caaf2eb0af5d259
>>> Removing node edca22192be476251b8617a88caaf2eb0af5d259 from cluster 10.0.0.244:6379
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.
redis-cli –h 10.0.0.244 —cluster del–node 10.0.0.244:6379 e37bd3b9d926f3b6a17f76d1537252c2b49d3b02
# 再次校验
redis-cli —cluster check 10.0.0.240:6379
10.0.0.246:6379 (d57e98ad...) –> 2 keys | 3277 slots | 1 slaves.
10.0.0.244:6379 (dee27391...) –> 0 keys | 4369 slots | 1 slaves.
10.0.0.241:6379 (25dd7994...) –> 3 keys | 4369 slots | 1 slaves.
10.0.0.243:6379 (daca3ec0...) –> 1 keys | 4369 slots | 1 slaves.
[OK] 6 keys in 4 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 10.0.0.240:6379)
S: 93925ab45a5d5c2e04acff1653259d21217b72d9 10.0.0.240:6379
slots: (0 slots) slave
replicates 25dd799481a5d00607b8d55a524fadf6902bf5e8
M: d57e98adbf7d252961418dab485d25212d223753 10.0.0.246:6379
slots:[0-1091],[5461-6553],[10923-12014] (3277 slots) master
1 additional replica(s)
M: dee27391bab6d2d008aeee9e968e6c6994364cda 10.0.0.244:6379
slots:[12015-16383] (4369 slots) master
1 additional replica(s)
S: 144860c9caf84f0cfbde2c64deb15bedee79a939 10.0.0.245:6379
slots: (0 slots) slave
replicates dee27391bab6d2d008aeee9e968e6c6994364cda
M: 25dd799481a5d00607b8d55a524fadf6902bf5e8 10.0.0.241:6379
slots:[1092-5460] (4369 slots) master
1 additional replica(s)
S: 607600889cc8fa7c5565e5eb57e8454f6f013f23 10.0.0.247:6379
slots: (0 slots) slave
replicates d57e98adbf7d252961418dab485d25212d223753
M: daca3ec0baa680c41a704a7bd8ae1a3f28877e86 10.0.0.243:6379
slots:[6554-10922] (4369 slots) master
1 additional replica(s)
S: c582fed4124c7703cd3c696e35a282f57a5643bb 10.0.0.242:6379
slots: (0 slots) slave
replicates daca3ec0baa680c41a704a7bd8ae1a3f28877e86
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
六、Redis 常见问题管理
6.1 大 key
6.1.1 大 key 判定阈值
- String 类型 : 单键内存 ≥ 100 KB
- List / Set 集合 : 元素数量 ≥ 10000
- Hash / ZSet : 字段 / 有序成员 ≥ 5000
- 任意类型键内存占用超过 512 KB
6.1.2 配套校验命令
# 扫描大 key, 建议在从节点执行
redis-cli –bigkeys
# 查看 String 内存
MEMORY USAGE key
# 统计 Hash 字段数量
HLEN key
# 统计 List 长度
LLEN key
# 统计 ZSet 成员数
ZCARD key
6.1.3 大 key 案例
# 生成 String 大 key
printf 'a%.0s' {1..204800} | redis-cli -x SET big_string
# 给大 key 设置过期时间
redis-cli EXPIRE big_string 86400
# 生成 Hash 大 key
redis-cli EVAL "for i=1,6000 do redis.call('HSET',KEYS[1],'field_'..i,'value_data') end" 1 big_hash
# 生成 List 大 key
redis-cli EVAL "for i=1,12000 do redis.call('LPUSH',KEYS[1],'msg_'..i) end" 1 big_list
# 生成 ZSet 大 key
redis-cli EVAL "for i=1,8000 do redis.call('ZADD',KEYS[1],i,'rank_'..i) end" 1 big_zset
# 验证
redis-cli STRLEN big_string
(integer) 204800
redis-cli MEMORY USAGE big_string
(integer) 229413
redis-cli hlen big_hash
(integer) 6000
redis-cli llen big_list
(integer) 12000
redis-cli zcard big_zset
(integer) 8000
# 遍历所有 key ,统计各种数据结构中占用内存最大的 key
redis-cli –bigkeys
# Scanning the entire keyspace to find biggest keys as well as
# average sizes per key type. You can use -i 0.1 to sleep 0.1 sec
# per 100 SCAN commands (not usually needed).
100.00% ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Keys sampled: 5
——– summary ——-
Total key length in bytes is 50 (avg len 10.00)
Biggest list found "big:list:message" has 12000 items
Biggest hash found "big_hash" has 6000 fields
Biggest string found "big_string" has 204800 bytes
Biggest zset found "big_zset" has 8000 members
2 lists with 24000 items (40.00% of keys, avg size 12000.00)
1 hashs with 6000 fields (20.00% of keys, avg size 6000.00)
0 streams with 0 entries (00.00% of keys, avg size 0.00)
1 strings with 204800 bytes (20.00% of keys, avg size 204800.00)
0 sets with 0 members (00.00% of keys, avg size 0.00)
1 zsets with 8000 members (20.00% of keys, avg size 8000.00)
6.1.4 大 key 危害
- RDB/AOF 持久化 fork COW 阻塞
- 网络 IO 延迟,接口超时、连接断开
- 集群内存分片不均匀
- 删除阻塞风险
6.1.5 大 key 优化方案
- 拆分
- 压缩
- 异步删除
6.2 三大缓存问题及解决方案
6.2.1 缓存雪崩
大量缓存 key 同时过期,所有请求直接穿透到数据库,数据库扛不住崩溃,从而导致整个业务系统崩溃
解决方案:
-
key 过期时间随机偏移
Redis 配置打开 lazyfree-lazy-expire yes ,过期 key 后台清理不阻塞主线程
-
多级缓存架构 : 应用本地缓存 + Redis 分布式缓存
-
熔断与降级
-
永不过期热点 key
6.2.2 缓存穿透
查询数据库不存在数据,Redis 没有对应缓存,所有请求转向数据库,把数据库压垮
解决方案:
- 接口参数前置校验 : 业务代码 / 网关 提前拦截非法参数
- 空值缓存 : 数据库查不到数据时,在 Redis 存一条短期有效的空标记(5分钟)
- 布隆过滤器
6.2.3 缓存击穿
热点 key 过期瞬间,海量请求同时绕过缓存直达数据库,瞬间压垮数据库
解决方案:
- 分布式互斥锁
- 逻辑过期
- 热点 key 预加载
- 热点 key 分片
6.3 内存治理与淘汰策略
6.3.1 配置使用最大物理内存
maxmemory 限制 Redis 实例最大可用内存,内存达到阈值后,触发淘汰策略清理 key
建议配置为物理内存的 70% – 80%
vim /etc/redis/redis-6379.conf
# 物理内存 16G,配置为 12G
maxmemory 12gb
# 查看 Redi 内存相关信息
redis-cli INFO memory
# Memory
maxmemory:0
maxmemory_human:0B # 使用内存上限,默认 0 表示无限制
maxmemory_policy:noeviction # 达到使用内存上限时的清理策略,默认 noeviction 表示不清理
6.3.2 内存清理策略
- noeviction : 默认策略,不清零,生产环境必须修改
- volatile-lru : 在设置了过期时间的 key 里,淘汰最久未访问的, 常用
- volatile-lfu : 在设置了过期时间的 key 里,淘汰访问次数最少的
- volatile-random : 在设置了过期时间的 key 里,随机淘汰,不推荐,无法保留热点数据
- volatile-ttl : 在设置了过期时间的 key 里,淘汰剩余存活时间最短的
- allkeys-lru : 在所有 key 里,淘汰最久未访问的
- allkeys-lfu : 在所有 key 里,淘汰访问次数最少的
生产环境推荐
- 大多数业务 : volatile-lru
- 纯临时缓存,无永久配置数据 : allkeys-lru
- 禁止使用 : noeviction 、volatile-random
# 永久修改配置文件
maxmemory-policy volatile-lru
# 动态修改
redis-cli CONFIG SET maxmemory-policy volatile-lru
# 验证
redis-cli CONFIG get maxmemory-policy
6.4 缓存与数据库一致性方案
生产环境推荐
先更新数据库 → 删除缓存
执行流程 :
6.5 慢查询
6.5.1 慢查询定义
Redis 慢查询日志:记录执行耗时超过指定阈值的命令,保存命令执行的耗时、发生时间、命令内容、客户端信息
慢命令会阻塞后续所有命令,引发大面积请求超时
命令在 Redis 服务端执行耗时 > slowlog‑log‑slower‑than 则触发慢查询,写入慢查询记录
6.5.2 慢查询配置
vim /etc/redis/redis.conf
单位为 us,指定超过 10ms 即为慢的指令,默认值为 10000us
slowlog-log-slower-than 10000
指定只保存最近的 1024 条慢记录,默认值为 128
slowlog-max-len 1024
systemctl restart redis.service
6.5.3 查看慢查询记录
查看慢日志记录的条数
127.0.0.1:6379> SLOWLOG LEN
(integer) 0
查看慢日志的最近 n 条记录,默认为10
127.0.0.1:6379> SLOWLOG GET
(empty array)
查看慢日志的最近 20 条记录
127.0.0.1:6379> SLOWLOG GET 20
6.5.4 清空慢查询日志
SLOWLOG RESET
6.5.5 运维实战:利用慢查询定位性能问题 & 优化方案
6.5.5.1 发现高危阻塞命令(最常见)
慢日志抓到:KEYS *、HGETALL bighash、SMEMBERS huge-set
问题根因:
- KEYS 遍历整个数据库所有 key,O (n);大数据量会严重阻塞主线程
- HGETALL 超大 Hash、SMEMBERS超大集合,一次性返回海量数据,长时间占用单线程
优化方案:
6.5.5.2 大量删除
删除一个百万元素的集合、超大 String,Redis 释放内存是同步操作,会阻塞主线程。
优化:
- Redis 4.0+ 使用惰性删除 + 异步删除 lazyfree-lazy-user-del yes,删除大 key 后台异步执行,不阻塞主线程
- 大 key 分批删除,用 SCAN 循环取出元素逐个删,不要一次性 DEL
6.5.5.3 ZSet 范围查询 ZRANGE huge_rank 0 -1 WITHSCORES
一次性拉取全量排行榜,元素几十万,执行时间超长
优化:分页查询,限制返回条数,ZRANGE key start end 给定分页偏移,禁止 0 -1 查全部
6.5.5.4 频繁出现随机慢查询,无明显大命令
慢日志里面单条命令耗时偶尔超过阈值,但是命令本身简单 GET/SET 排查方向:
- 优化:调整 auto-aof-rewrite-percentage,错开业务高峰;开启 no-appendfsync-on-rewrite yes
- 优化:key 过期时间打散,避免大批量 key 同一时刻过期;拆分热点数据
- 优化 : SSD 替代机械硬盘
6.5.5.5 集群环境,慢查询分散在不同节点
Redis‑Cluster 每个节点独立维护自己的慢查询日志,互不共享
排查操作:必须逐个登录每一个主节点执行 SLOWLOG GET,不能只查一个节点





