官方网址:Redis - Real-time data for agents & apps
一键生成 CentOS Stream 阿里云镜像源 repo 文件
-
cat > xxx <<EOF ... EOF:Here-Document 语法,把 EOF 包裹的内容覆盖写入/etc/yum.repos.d/aliyun.repo -
文件路径
/etc/yum.repos.d/是 RHEL/CentOS Stream RockyLinux 系统 yum/dnf 软件源存放目录 -
里面全是阿里云 CentOS Stream 国内镜像源配置,替换官方国外源,下载软件更快
cat >/etc/yum.repos.d/aliyun.repo<<EOF
[baseos]
name=CentOS Stream $releasever - BaseOS
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/BaseOS/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1[baseos-debug]
name=CentOS Stream $releasever - BaseOS - Debug
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/BaseOS/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0[baseos-source]
name=CentOS Stream $releasever - BaseOS - Source
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/BaseOS/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0[appstream]
name=CentOS Stream $releasever - AppStream
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/AppStream/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1[appstream-debug]
name=CentOS Stream $releasever - AppStream - Debug
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/AppStream/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0[appstream-source]
name=CentOS Stream $releasever - AppStream - Source
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/AppStream/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0[crb]
name=CentOS Stream $releasever - CRB
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/CRB/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0[crb-debug]
name=CentOS Stream $releasever - CRB - Debug
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/CRB/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0[crb-source]
name=CentOS Stream $releasever - CRB - Source
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/CRB/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0[highavailability]
name=CentOS Stream $releasever - HighAvailability
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/HighAvailability/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0[highavailability-debug]
name=CentOS Stream $releasever - HighAvailability - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/HighAvailability/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0[highavailability-source]
name=CentOS Stream $releasever - HighAvailability - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/HighAvailability/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0[nfv]
name=CentOS Stream $releasever - NFV
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/NFV/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0[nfv-debug]
name=CentOS Stream $releasever - NFV - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/NFV/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0[nfv-source]
name=CentOS Stream $releasever - NFV - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/NFV/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0[rt]
name=CentOS Stream $releasever - RT
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/RT/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0[rt-debug]
name=CentOS Stream $releasever - RT - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/RT/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0[rt-source]
name=CentOS Stream $releasever - RT - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/RT/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0[resilientstorage]
name=CentOS Stream $releasever - ResilientStorage
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/ResilientStorage/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0[resilientstorage-debug]
name=CentOS Stream $releasever - ResilientStorage - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/ResilientStorage/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0[resilientstorage-source]
name=CentOS Stream $releasever - ResilientStorage - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/ResilientStorage/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0[extras-common]
name=CentOS Stream $releasever - Extras packages
baseurl=http://mirrors.aliyun.com/centos-stream/SIGs/\$stream/extras/\$basearch/extras-common/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-Extras-SHA512
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1[extras-common-source]
name=CentOS Stream $releasever - Extras packages - Source
baseurl=http://mirrors.aliyun.com/centos-stream/SIGs/\$stream/extras/source/extras-common/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-Extras-SHA512
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
EOF
redis的安装配置
安装
可以通过yum方式在线安装,也可以通过源码编译方式安装
采用源码编译方式安装:
第一步:找到对应的安装包资源,使用wget命令下载,这里安装的7.4.0版本
安装包资源地址:https://download.redis.io/releases/
第二步:上传或者下载Redis到Linux系统中
wget https://download.redis.io/releases/redis-7.4.0.tar.gz
第三步:配置=>编译=>安装
tar -zxvf redis-7.4.0.tar.gz
cd redis-7.4.0
yum install epel-release -y
yum install make gcc jemalloc jemalloc-devel -y
make
make install PREFIX=/usr/local/redis
第四步:配置环境变量
echo 'PATH=$PATH:/usr/local/redis/bin/' >> /etc/profile && source /etc/profile
redis-cli -v
我这里直接把安装包rz上去

mkdir -p /export/software
cd /export/software
ll
rz

解压后 移动到/export/server下 并且重命名为一个短名字
tar -zxvf redis-7.4.0.tar.gz
ll
mkdir /export/server
mv redis-7.4.0 /export/server/redis
cd /export/server
ll

配置
我的安装路径在/export/server
mkdir -p /export/server/redis/conf
cp /export/server/redis/redis.conf /export/server/redis/conf/
# 修改配置
vim /export/server/redis/conf/redis.conf
88: bind 127.0.0.1 -::1 --> bind 0.0.0.0
310: daemonize no --> daemonize yes
#过量使用内存设置为1
echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf
#配置好后执行以下命令让配置生效
sysctl -p
vm.overcommit_memory 的 3 种模式
0(默认)内核根据启发式算法决定是否允许超分配"看情况给,不一定准"
1允许内存超分配,不做任何限制,想分就分,不管你有没有这么多内存
2严格限制,不允许超分配非常安全,但可能导致程序申请内存失败

bind 127.0.0.1 -::1→bind 0.0.0.0只允许 IPv4 访问,IPv6 客户端连不上,- bind 0.0.0.0 -::1 允许所有 IPv4 地址远程连接,同时排除 IPv6 本地地址,兼容双协议栈服务器

daemonize no→daemonize yes开启后台守护进程,执行 redis-server 后终端不会卡住,Redis 在后台运行。

启动
#查看版本
redis-cli -v
#启动
redis-server /export/server/redis/conf/redis.conf
#查看服务进程
ps aux | grep redis
ps -ef | grep redis
netstat -pantul|grep redis
netstat -pantul|grep 6379
ss -pantul|grep redis
ss -pantul|grep 6379
#登录
redis-cli
127.0.0.1:6379> info
没编译redis bin不存在
cd /export/server/redis
dnf install gcc gcc-c++ make -y
make
make install PREFIX=/export/server/redis
ln -s /export/server/redis/bin/redis-server /usr/bin/redis-server
ln -s /export/server/redis/bin/redis-cli /usr/bin/redis-cli
redis-cli -v
redis-server /export/server/redis/conf/redis.conf
ps -ef | grep redis
redis-cli = Redis 客户端工具,作用:连接 Redis 服务,发送指令操作 Redis
- 直接敲
redis-cli:默认连本机127.0.0.1:6379的 Redis - 远程连接:
redis-cli -h 服务IP -p 6379进入后界面127.0.0.1:6379>就是交互输入框,在这里输入 Redis 指令操作数据库。
服务配置
cat >/etc/systemd/system/redis.service<<EOF
[Unit]
Description=redis-server
After=network.target
[Service]
Type=forking
ExecStart=/export/server/redis/bin/redis-server /export/server/redis/conf/redis.conf
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
pkill redis-server
systemctl daemon-reload
systemctl start redis
systemctl enable redis
systemctl status redis --no-pager
Redis命令行客户端简单使用
redis属于c/s架构软件,telnet可以连接redis,没有本身redis-cli更加好用
① 简单的数据操作
string类型:字符串类型、文本类型,用于保存文本信息
# redis-cli
127.0.0.1:6379 > set post-name devops
OK
127.0.0.1:6379 > get post-name
"devops"
② 查看操作语法帮助
127.0.0.1:6379 > help
127.0.0.1:6379 > help set
③ 系统状态信息
127.0.0.1:6379 > info
Redis 的 Key-Value
16个逻辑库
默认在redis配置文件redis.conf中,提供了16个库,查看配置文件:
redis-cli
127.0.0.1:6379> config get databases
1) "databases"
2) "16"
127.0.0.1:6379>
数据库的编号都是从0开始,最大值为16-1=15
grep 16 /export/server/redis/conf/redis.conf
-
Redis 的 16 个数据库只是逻辑隔离,底层共用一套内存、持久化、线程,不是真正独立实例;
-
Redis 默认创建 16 个逻辑数据库 ,编号
db0 ~ db15。 -
登录 redis-cli 默认在
db0; -
切换库命令:
select 数字,比如select 5切到 db5; -
集群模式下此配置失效,集群只能用 db0。
-
想要更多数据库,可以修改
redis.conf的databases 数字,改完重启 Redis 生效。

Key 操作
NoSQL数据库,存储形式,键值对,键类似身份证号(不能重复,必须唯一)
key的命名规则不同于一般语言,键盘上除了空格、\n换行符外其他的大部分字符都可以使用。
但是像"my key"和"mykey\n"这样包含空格和换行的key是不允许的。
我们在使用的时候可以自己定义一个key的格式,但是要特别注意:
key不要太长,占内存,查询慢。
key不要太短,像u:1000:pwd:123456 就不如 user:1000:password:123456可读性好
☆ 判断键是否存在 exists
# exists key 键名
存在1,不存在0

☆ 删除键 del
# del 键名
存在1,不存在0

☆ 获取键类型 type
# type 键名

☆ 设置过期时间(单位s)expire
set name test
expire name 25
set name test
作用:创建键值对
-
key =
name -
value =
test类型:字符串 string
expire name 25
作用:给 key 设置过期时间 ,单位:秒含义:name 这个键,25 秒后自动被 Redis 删除。
查看剩余过期时间,(Time To Live)
ttl name
|-----------------|----------------------------------------------------|
| 返回值(整数) | 含义 |
| 正整数(如 3600) | 键存在,并且设置了过期时间,返回的是 剩余的秒数(例如 3600 表示还有 1 小时过期)。 |
| -1 | 键存在,但没有设置过期时间(永久有效)。 |
| -2 | 键不存在(可能从未设置过,或已过期并被自动删除)。 |

库操作
☆ 查看当前库key数量 dbsize
dbsize


☆ 切换数据库 select
Redis 共16个库,index = number - 1
select 0到15
☆ 清空当前库 flushdb
flushdb
清空所有库
flushall
Redis 五大核心数据结构
Redis 命令是按数据类型划分的,不同结构有专属写入命令:
| 数据类型 | 新增元素专用命令 |
|---|---|
| String 字符串 | set |
| Hash 哈希 | hset |
| List 列表 | lpush / rpush |
| Set 集合 | sadd(set add 集合添加) |
| ZSet 有序集合 | zadd |
这五个数据类型是 Redis 存数据的五种盒子,是存储结构
string字符串
tring是redis最基本的类型
redis的string可以包含任何数据,包括jpg图片、base64编码或者序列化的对象
单个value值最大上限是512MB
如果只用string类型,redis就可以被看作加上持久化特性的memcached
set key value 设置 key 对应的值为 string 类型的 value
mset key1 value1 ... keyN valueN 一次设置多个 key 的值
mget key1 key2 ... keyN 一次获取多个 key 的值
incr key 对 key 的值做加加操作,并返回新的值
decr key 同上,但是做的是减减操作
incrby key integer 同 incr,加指定值
decrby key integer 同 decr,减指定值
append key value 给指定 key 的字符串值追加 value
substr key start end 返回截取过的 key 的字符串值
☆ 设置与批量设置 set|mset
#创建字符串键值对,key=name,values=小咪
set name xiaomi
#根据 key 读取对应 value
get name
set 原子操作:要么全部设置成功,要么全部失败,不会出现部分 key 写入、部分没写入的情况。
批量一次性设置多个 key-value,等价于连续执行 3 条 set
mset name redis age 25 address canton
#批量取值
mget name age address
- key:
name→ value:redis - key:
age→ value:25 - key:
address→ value:canton
☆ 获取与批量获取 get|mget
其实get和mget上面讲set的时候已经演示过了,这里讲一个hgetall
像是有时候我们想拿一个哈希类型的键值的时候 又忘记了里面有什么字段 就用这个命令
hgetall user:1005

☆ 增加与减少 incr|decr
- 只能对纯数字字符串生效 如果
key的值是文字(比如name存的redis),执行incr name会直接报错。 - 底层原子操作,适合做:计数器、点赞数、访问量、库存。
- 没有
incrbyfloat才支持小数,incr/incrby/decr/decrby只支持整数。
自增1
incr age
get age
自增减1
decr age
get age

指定增加/减少数值
incrby age 2
get age
decrby age 3
get age
一个键只能存一个值 那我存很多用户 他们都有年龄这一key 我怎么自增自减呢?
方法1:每个用户做独立唯一key
# 每个用户单独的age键 set user:1001:age 20 set user:1002:age 23 set user:1003:age 19 incr user:1001:age # 只改1001的年龄 incr user:1002:age # 只改1002的年龄 #取数据 get user:1001:age方法2:把一个用户的所有信息放一起(不用一堆分散 key)
哈希类型
一个 key
user:1001里面可以存多个字段 name/age/address
# hset key 字段1 值1 字段2 值2 ... hset user:1001 name zhangsan age 20 address beijing # 单独取age hget user:1001 age # 对age自增1 hincrby user:1001 age 1
☆ 追加 append
append 是 String 字符串类型 的命令,只能操作普通字符串 key,不能直接操作 Hash (hset) 里面的字段
127.0.0.1:6379> append name 666
(integer) 8
127.0.0.1:6379> get name
"redis666"
- 第一个参数
name:目标 key,也就是要追加内容的对象 - 第二个参数
666:要追加到末尾的字符串返回(integer) 8是追加后整个字符串的总长度
☆ 截取 substr
substr 只能操作 String,不能直接截取 Hash 内部字段
# substr name start end
start:从哪里开始截取,默认从0,第一个字符开始
end:到哪里截取结束,必须要添加结束字符的索引号
下面这一串就是hush不能操作substr

这个是string(一串连续的字节 / 文字 就叫 string)

list列表
Redis 的 List 类型本质是一个双向链表,支持通过 LPUSH/RPUSH(头部/尾部插入)和 LPOP/RPOP(头部/尾部删除)操作灵活管理元素。
队列(先进先出 FIFO)核心要求:一头存、另一头取
栈(后进先出 LIFO)核心要求:同一头存、同一头取,存入和弹出必须是同一侧,第一个拿出来的是最后存入的最新的数据
案例:获取最后登录的用户
# 1. 首先清空测试环境(可选)
127.0.0.1:6379> DEL lastlogin
(integer) 1
# 2. 模拟用户登录 - 使用 LPUSH 从左侧添加
# 这样最新登录的用户总是在最前面
127.0.0.1:6379> LPUSH lastlogin xiaohua
(integer) 1
127.0.0.1:6379> LPUSH lastlogin xiaoming
(integer) 2
127.0.0.1:6379> LPUSH lastlogin xiaobai
(integer) 3
# 3. 查看当前列表中的所有用户
# 语法:LRANGE key start stop
# 0表示第一个元素,-1表示最后一个元素
127.0.0.1:6379> LRANGE lastlogin 0 -1
1) "xiaobai" # 最新登录的(最后push的)
2) "xiaoming" # 中间登录的
3) "xiaohua" # 最早登录的(最先push的)
# 4. 获取最后登录的2个用户
127.0.0.1:6379> LRANGE lastlogin 0 1
1) "xiaobai"
2) "xiaoming"
# 5. 再模拟一个新用户登录
127.0.0.1:6379> LPUSH lastlogin xiaohong
(integer) 4
# 6. 再次查看最新登录的2个用户
127.0.0.1:6379> LRANGE lastlogin 0 1
1) "xiaohong" # 最新登录的
2) "xiaobai" # 之前最新登录的

Set 集合(sadd)
- Set :Redis 五大基础数据结构之一,无序、元素自动去重的字符串集合(数据类型)
- sadd :是操作 Set 类型的写入命令,全称
set add,往集合里添加元素, 只能操作 Set(集合)类型,完全不能操作 String、Hash、List、ZSet,数据类型不匹配直接报错。
Set是 数据类型,Set 是独立的数据结构,不是 set 命令!
语法:sadd key 元素1 元素2 元素3...
特点:1 个 key 对应 一堆不重复、无序的元素(多个值)
- 小写
set(命令) → 操作 String,一键单一串内容;- 大写 Set(数据类型) → 操作集合,一键多个独立元素,用
sadd增数据。
# 重复添加小咪,集合只会存1个
sadd pet 小咪 猫 小咪 兔子
# 查看全部成员
smembers pet
# 输出:兔子、猫、小咪(顺序随机,无序)
唯一性(自动去重) 同一个集合里不能存重复内容,重复 sadd 相同数据只会保留一份
无序 每次用 smembers 查看,元素展示顺序不固定,没有下标概念,不能按序号取值
| 命令 | 全称 / 含义 | 作用 |
|---|---|---|
| sadd key 元素 1 元素 2 | set add | 往集合添加元素 |
| smembers key | set members | 查看集合所有元素 |
| sismember key 元素 | set is member | 判断元素是否在集合里(存在返回 1,不存在 0) |
| srem key 元素 | set remove | 删除集合指定元素 |
| scard key | set card(card = 基数,元素总数) | 获取集合内元素个数 |
# 给用户1005的好友集合添加好友
sadd user:1005:friend 小明 小红 小咪
# 判断小咪是不是好友
sismember user:1005:friend 小咪 # 返回1
# 统计好友数量
scard user:1005:friend # 返回3
# 删除好友小明
srem user:1005:friend 小明
SINTER 交集(intersection 交叉)
Set 灵魂功能:多集合运算
找出多个集合同时存在的元素 → 共同好友、共同关注
# user1好友:小明、小红、小咪
sadd u1 小明 小红 小咪
# user2好友:小红、小蓝、小咪
sadd u2 小红 小蓝 小咪
# 求共同好友
sinter u1 u2
# 输出:小红、小咪
SUNION 并集(union 合并)
把所有集合元素合并,自动去重 → 全部好友汇总
sunion u1 u2
# 输出:小明、小红、小咪、小蓝
SDIFF 差集(difference 差异)
只保留「第一个集合有、其他集合没有」的元素 → 我有但对方没有的好友
sdiff u1 u2
# u1独有的好友:小明
综合案例:朋友圈
添加了四条类型为Set的数据 键分别是小咪的朋友/啵啵的朋友/小汇的朋友/小衡的朋友
sadd 小咪的朋友 啵啵 小衡 小汇
sadd 啵啵的朋友 小咪 小衡 小汇
sadd 小汇的朋友 小咪 啵啵 小衡 奇怪的美工叔叔
sadd 小衡的朋友 小咪 啵啵 小汇 猪佩奇老师
打印小咪的朋友和小汇的朋友smembers
smembers 小咪的朋友
smembers 小汇的朋友

小汇的朋友里是否有小咪(集合在前,元素在后)
sismember 小汇的朋友 小咪
有返回1,没有返回0

找小咪和小汇的共同好友(交集)sinter
sinter 小咪的朋友 小汇的朋友

找出小衡和小汇的所有好友(并集)sunion
sunion 小汇的朋友 小衡的朋友

小汇给小衡推荐好友,应该推荐谁(求差集)
sdiff A B= 只保留 A 里有、B 里完全没有的元素只看第一个集合,第二个集合只用来做剔除,顺序超级重要!

Zset有序集合(zadd)
-
ZSet:Sorted Set,有序集合,五大类型最后一种数据结构(盒子)
-
zadd = zset add,往有序集合里添加成员的命令(操作工具)
格式:zadd key 分数 成员
核心特点:
1. 元素唯一
同一个值不能重复存,重复添加只会更新分数,不会新增重复元素
2. 权重排序
分数是数字(整数 / 小数都行)Redis 自动按分数从小到大排序
3. 灵活排序
普通 Set 无序;ZSet 永久按 Score 排序,可查前 N 名、分数区间、倒数几名
案例1,实现手机APP市场的软件排名
key : hotTop
id score name
1 2 qq
2 3 wechat
3 5 alipay
4 7 taobao
5 10 mi
6 8 jd
#添加数据
zadd hotTop 2 qq 3 wechat 5 alipay 7 taobao 10 mi 8 jd
#完整榜单
zrevrange hotTop 0 -1 withscores
#排序,从下标 0开始,取到下标 5为止
zrange hotTop 0 5
#排序,从大到小
zrevrange hotTop 0 5
#获取hottop键中的jd的的score值
zscore hotTop jd
-1 = 最后一位下标-2 = 倒数第二位下标
hash哈希
# 1. 插入完整用户数据
hset user:1 name 王维 sex 男 age 18 address "北京市昌平区"
# 2. 获取指定字段的值
hget user:1 name
# 3. 一次性查多个字段
hmget user:1 name age
# 4. 修改单个字段
hset user:1 age 20
hincrby 小咪 age 103
# 5. 删除某个字段
hdel user:1 address
# 6. 查看一个键的字段是否存在
hexists 小咪 age
# 7. 获取一共有多少个字段
hlen user:1
# 8.一次设置多个值对
hmset 小咪 name mi age 99 gender miao
# 9.查看全部字段+值
hgetall 小咪
# 10.获取键的所有字段/获取键的所有值
hkeys 小咪
hvals 小咪
Redis数据持久化操作
数据持久化(数据在服务或者软件重启之后不丢失)
如果数据只存在内存中,肯定会丢失,实现持久化,就需要把数据存储到磁盘中
Redis 四种持久化运行模式
1. RDB (Redis Database)
RDB 持久化,会按照设定的时间间隔,对全量数据做定点快照。
2,AOF (Append Only File)
AOF 持久化会记录服务器收到的每一条写操作命令;Redis 重启时,会重新逐条执行日志里所有命令,还原出完整原始数据
3. No persistence 不开启持久化
可以完全关闭持久化。一般只做纯缓存场景使用(不在乎断电丢数据,追求极致性能)
4. RDB + AOF 混合持久化
同一个 Redis 服务实例,可以同时开启 RDB+AOF 两套持久化。生产环境标准方案:RDB 做定时全量备份,AOF 弥补快照间隔丢失的数据,兼顾恢复速度与数据安全
1. 仅 RDB 模式
只开启快照持久化。
-
逻辑:每隔一段时间,给内存全部数据拍一张完整快照存磁盘。
-
特点:文件小、恢复快;两次快照之间新增的数据会丢失。
-
适用:定时备份、数据允许少量丢失的场景。
2. 仅 AOF 模式
只开启命令日志持久化。
-
逻辑:把每一条修改数据的命令追加写到日志文件。
-
特点:数据丢失极少(默认最多丢 1 秒);文件体积大,重启恢复慢。
-
适用:对数据安全要求高、不能丢大量数据的业务。
3. No persistence(无持久化)
完全关闭磁盘落地,数据只放内存。
-
逻辑:不生成任何 rdb /aof 文件。
-
特点:性能最好;一旦断电、重启,所有数据全部清空。
-
适用:纯临时缓存、丢数据不影响业务的场景。
4. RDB + AOF 混合模式(生产线上标准方案)
同时开启两套持久化。
-
逻辑:AOF 保证少丢数据,RDB 定时生成完整快照。
-
重启规则:Redis 启动时优先加载 AOF 文件(数据更完整)。
-
优势:兼顾数据安全、备份速度、恢复效率,企业项目首选。
RDB 持久化 - Snappshoting 快照
RDB(Redis DataBase)是 Redis 默认开启的持久化方式,通过 定时生成内存数据的二进制快照(snapshot) 将当前数据保存到磁盘文件(默认 dump.rdb),重启时直接加载快照恢复数据。
自动备份机制详解
触发条件:只有满足触发条件(指定时间内至少发生指定次数的增删改操作),才会自动备份!
讲的比较琐碎,想看更简略的步骤和命令可以看下一个目录:关于自动备份机制的总结
优化Redis配置文件
我的redis安装路径在/export/server下
#备份配置
mv /export/server/redis/conf/redis.conf /export/server/redis/conf/redis.conf.bak
#过滤注释、空行,生成纯净配置
grep -Ev "^$|#" /export/server/redis/conf/redis.conf.bak > /export/server/redis/conf/redis.conf
-Ev 开启正则表达,让|表示或者,v只输出不匹配的行
^ \^一行的开头 一行的结尾 连起来表示空行
#匹配任意一行里包含
#的行,也就是配置文件里所有注释行
会在/export/server/redis/conf下看到两个文件:
-
redis.conf.bak原始完整备份 -
redis.conf精简无注释版本cat /export/server/redis/conf/redis.conf
一、网络访问(远程连接必改)
bind 0.0.0.0 默认是 bind 127.0.0.1,只能本机连;改成 0.0.0.0 允许其他服务器 / 本地工具远程连接
protected-mode yes保护模式开启规则:没设密码 + bind 本地回环 → 拒绝外部连接。如果开了远程访问,必须配置密码,否则外部连不上。
port 6379:默认端口,不需要改,特殊环境才换端口。
二、后台运行daemonize yes 代表 Redis 后台守护进程运行,执行启动命令后不会占住终端,推荐保持 yes
三,持久化相关

stop-writes-on-bgsave-error yes后台生成 RDB 快照失败时,停止接收写入,防止数据丢失rdbcompression yes开启 RDB 文件压缩,节省磁盘空间rdbchecksum yesRDB 文件加入校验和,重启加载时检查文件是否损坏dbfilename dump.rdbRDB 快照文件名字dir ./重点:RDB、AOF 文件存放目录./代表当前启动命令所在目录 ,容易乱,建议改成绝对路径:dir /export/server/redis,现在改也行,后面有步骤会带着一起改
AOF 日志,

- no:只开 RDB,断电会丢失两次快照间数据;
- 线上生产改为
appendonly yes,同时开启 RDB+AOF 双重持久化,数据更安全
四,安全访问密码
添加一行,我这里没加,生产环境加上
requirepass 123456
配置文件/export/server/redis/conf/redis.conf save配置说明
save 900 1 # 15分钟内(900秒)至少有1个key被修改 → 触发快照
save 300 100 # 5分钟内(300秒)至少有100个key被修改 → 触发快照
save 60 10000 # 1分钟内(60秒)至少有10000个key被修改 → 触发快照
三个条件满足任意一个即可触发快照("或"关系),例如若 1 分钟内修改了 10001 个 key,则立即生成快照。
触发后,计时会重置。
下面的
save 3600 1 300 100 60 10000
等价的分行写法
save 3600 1
save 300 100
save 60 10000
自定义配置
# 语法:save 秒数 redis事务型操作(增删改)次数
save 10 2 # 10秒内至少2个写操作触发快照(原配置可注释或删除)
echo 'save 10 2' >> /export/server/redis/conf/redis.conf
看一下快照文件在不在
grep dump.rdb /export/server/redis/conf/redis.conf
实时查看当前 Redis 正在使用的持久化文件存放目录
config get dir

返回 /,代表现在 rdb、aof 文件会生成在服务器根目录,非常乱,不安全直接更改配置518行,数据存放路径,放到redis安装目录下(原文件的这个在518行,我们前面不是把原文件挪走,重新弄了一个吗 vim进去改)
vim /export/server/redis/conf/redis.conf
# 在vim里输入 /dir 回车,直接定位到dir ./ 这一行,改成绝对路径
dir /export/server/redis

重启验证
/export/server/redis/bin/redis-cli shutdown
/export/server/redis/bin/redis-server /export/server/redis/conf/redis.conf
redis-cli
config get dir

关于自动备份机制的总结
1,原配置挪走改名,生成纯净配置用于修改
mv /export/server/redis/conf/redis.conf /export/server/redis/conf/redis.conf.bak
grep -Ev "^$|#" /export/server/redis/conf/redis.conf.bak > /export/server/redis/conf/redis.conf
2,检查一下纯净配置的东西都对不对,
3,修改持久化文件存放目录,放到绝对路径,也就是在这个纯净配置里修改,
改成dir /export/server/redis 改到redis的安装路径下

4,在这个纯净配置的最下方添加一行 ,也就是 10s内有2个操作,自动拍一个快照,方便我们验证这个自动备份的机制
save 10 2
5,重启redis,验证刚刚两个修改生效没有,先看看绝对路径改好没有
redis-cli
config get dir
6,看一下测试备份频率,具体的我放在下面了
备份会覆盖,想要保留旧备份,只能再给旧备份做备份
# 先复制当前rdb做备份(改名时带时间戳)
cp /usr/local/redis/dump.rdb ./dump_$(date +%Y%m%d_%H%M%S).rdb
# 再操作增加key之类的 再生成新快照
redis-cli
bgsave
测试备份频率
在10s内,进行两个key的改变,查看备份效果:
我又开了一个窗口 准备观察cd /export/server/redis/ ll
原窗口操作
redis-cli
set a 1
set b 2
新窗口可以看到一个dump.rdb的文件

手动快照备份
save,别用
save
- 同步阻塞执行
- Redis 主线程暂停所有读写,全程卡住,直到完整生成
dump.rdb才释放 - 数据量大时会造成服务卡顿、业务请求超时,生产禁止用
- 返回
OK代表快照文件已落地磁盘
bgsave
bgsave
- 后台异步快照(生产推荐)
- Redis fork 一个子进程单独做持久化,主线程正常处理客户端命令,不阻塞业务
- 返回
Background saving started仅代表后台任务已发起,不代表文件写完 - 自动 RDB 定时触发底层就是
bgsave
查看快照状态命令
# 查看上一次持久化时间、是否正在后台存RDB
lastsave
时间戳转时间
date -d @时间戳
数据还原
Redis 启动时,会自动读取 dir 目录下的 dbfilename dump.rdb 文件,把快照里所有键值加载进内存;我们手动替换旧 rdb 文件,就能恢复删除前的数据
systemctl stop redis
cp dump.rdb /exprot/server/redis/
# 覆盖询问输入y
systemctl start redis
redis-cli
举例说明,方便理解,
在上面自动备份机制的讲解时,我们的 /export/server/redis/下已经有了第一个dump.rdb文件
我们把这个复制放到根下cp /usr/local/redis/dump.rdb ./dump20260704.rdb
我们现在生成第二个快照:我们前面设置的自动备份机制时10秒2个操作就触发,del a del b
删除两个key,/export/server/redis/下现在的dump.rdb就覆盖了之前的有a b键的快照
现在我们想要还原有a b键的快照该怎么做?
有效快照是被我们复制根下的dump20260704.rdb,先给redis停了,cp dump20260704.rdb /export/server/redis/dump.rdb 然后再启redis 进去看看那两个键在不在
RDB高级配置
关键配置参数
stop-writes-on-bgsave-error yes快照失败拒绝写入,防止宕机丢数据,生产必开。rdbcompression yesLZF 压缩 rdb,省磁盘,轻微耗 CPU,默认不用改。rdbchecksum yesCRC64 校验 rdb 文件完整性,防止损坏数据加载,建议开启。rdb-del-sync-files no主从同步临时 rdb 是否自动删除,仅无持久化环境生效。
stop-writes-on-bgsave-error yes
后台 RDB 快照失败时,是否禁止 Redis 写入新数据
-
默认
yes(推荐生产)磁盘满、权限不足、磁盘故障导致 bgsave 快照失败 → Redis 直接拒绝所有写命令(set/hset/sadd 等),只允许读。目的:不让内存新数据丢失,提醒运维磁盘出问题,避免数据只存在内存、落盘失败。 -
改成
no快照失败也照常接收写入,服务不中断;但一旦机器宕机,这部分新数据完全丢了,风险高。
rdbcompression yes
RDB 文件是否开启 LZF 压缩
-
默认
yes生成 dump.rdb 时用 LZF 算法压缩字符串,文件体积大幅变小,节省磁盘、拷贝备份更快;代价:fork 子进程时会消耗一点点 CPU,几乎感知不到。 -
改成
no不压缩,CPU 占用更低,但 rdb 文件会大很多;只有 CPU 极度紧张的机器才关,日常保持 yes。
rdbchecksum yes
- 默认
yes- 存快照:结尾写入校验和;
- 启动加载 rdb:校验文件是否损坏(断电、磁盘坏道导致文件残缺);校验失败直接拒绝加载,防止读到错乱脏数据。
- 改成
no跳过校验,节省一点点 CPU;但 rdb 损坏时 Redis 会加载错误数据,不建议关闭。
rdb-del-sync-files no
从同步时的临时 RDB
Redis 主从复制初次同步,主节点会生成一份临时 RDB 发给从节点,用来一次性同步全量数据。
-
如果你机器没开启 RDB、AOF 持久化(只做主从,不落地磁盘);
-
这份同步用的 rdb 文件默认会留在磁盘,不会自动删除
-
rdb-del-sync-files no(默认)主从同步完成后,保留同步用的临时 rdb 文件
-
db-del-sync-files yes 主从同步完毕,立刻删掉这份用于同步的临时 rdb
RDB快照触发场景
除配置的 save 规则外,以下操作也会触发 RDB 快照:
-
手动执行 save/bgsave 命令;
-
执行 flushall 清空数据库并持久化(生成空 RDB 文件,无实际意义);
-
主从复制时,主节点自动触发快照发送给从节点;
-
执行 shutdown 且未开启 AOF 持久化时,自动保存最后一次快照。
禁用RDB快照
若需完全关闭 RDB,可通过以下方式
# 方式1:命令行临时禁用(重启后恢复原配置)
127.0.0.1:6379> config set save ""
# 方式2:修改配置文件永久禁用
vim /usr/local/redis/conf/redis.conf
# 添加或修改:save "" # 空引号表示禁用所有自动快照规则
AOF持久化(日志追加)
AOF(Append Only File) 是 Redis 的另一种持久化方式。
它不记录数据快照 ,而是记录所有写操作命令(读操作不记录),就像数据库的"操作日志"。
Redis 重启时,通过重新执行这些命令恢复数据。
核心特点:
-
只追加不修改:AOF 文件仅允许在末尾追加新命令(增删改内容),不允许直接编辑或删除已有内容。
-
数据恢复可靠:通过重放日志,能精确还原到崩溃前的状态(丢失数据量可控)。
-
文件可读性强:命令以 Redis 协议格式存储,人类可读(如 SET key value)。
|-----------|---------------------------------------------------------------|
| 步骤 | 详解 |
| ①命令接收 | 客户端发送的写操作(如 SET name "redis")先到达 Redis 服务端。 |
| ②缓存写入 | 命令不会直接写入磁盘,而是先存入 AOF 缓存区(内存区域),避免频繁磁盘 I/O。 |
| ③策略同步 | AOF缓存会根据AOF缓存区 同步文件的三种写回策略 将命令写入磁盘上的AOF文件。 |
| ④文件优化 | 随着 AOF 文件变大,Redis 会根据规则进行命令的合并( 又称AOF重写 ),从而起到AOF文件压缩的目的。 |
| ⑤重启恢复 | Redis 重启时,读取 AOF 文件 并 按顺序执行所有写命令,重建原始数据集。 |
核心配置
开启 AOF
默认情况下,Redis 未开启 AOF,需手动修改配置文件(redis.conf):
vim /export/server/redis/conf/redis.conf
appendonly yes # 开启 AOF(原默认值为 no)
只改这一行就生效,最好再改一下参数,刷盘策略决定丢数据多少
三种 写回策略(刷盘策略)
修改位置:打开 redis.conf 配置文件,搜索 appendfsync 这一行,就在 AOF 配置区域(大概 1400 多行附近)
这是 AOF 持久化里核心配置,控制日志什么时候真正写到硬盘,平衡性能 和丢数据风险。
- appendfsync always
-
规则:每一条写命令执行完,立刻强制刷磁盘
-
优点:机器断电、宕机不会丢任何数据,可靠性拉满
-
缺点:每一次写入都等磁盘 IO,速度极慢,并发高直接卡死,生产几乎不用
- appendfsync everysec(官方推荐默认)
-
规则:Redis 后台线程,每 1 秒统一刷一次磁盘
-
优点:性能损失很小,绝大多数业务够用
-
缺点:极端宕机最多丢失最近 1 秒内的写入数据
-
适用:绝大多数线上业务,兼顾性能与数据安全
- appendfsync no
-
规则:Redis 不主动刷盘,全部交给操作系统内核缓冲区自行刷新
-
优点:性能最高,Redis 完全不等待磁盘操作
-
缺点:操作系统可能攒几秒甚至更久才落盘;一旦宕机,会丢失大量未刷盘数据,风险极高
| 策略 | 刷盘时机 | 优点 | 缺点 |
|---|---|---|---|
| always | 每条写命令立刻落盘 | 零丢失 | 性能极差 |
| everysec | 每秒统一刷一次 | 性能折中,平衡安全 | 最多丢 1 秒数据 |
| no | 交给操作系统自动刷 | 性能最好 | 可能丢大量数据 |
文件位置与命名
Redis 6或之前版本: AOF 保存文件的位置和 RDB 保存文件的位置一样,都是通过 redis.conf 配置文件中的 dir 配置的。
Redis 7: redis.conf 配置文件中除了要设置dir以外,还新增加了一个属性appenddirname 。
AOF保存文件的位置:dir + appenddirname
vim /export/server/redis/conf/redis.conf
:set nu
518 dir /export/server/redis
1427 appendfilename "appendonly.aof"
1433 appenddirname "appendonlydir"
dir /usr/local/redis
全局持久化根目录,RDB、AOF 相关文件都基于这个路径存放。
appendfilename "appendonly.aof"
老版本单文件 AOF 的文件名;现在 Redis 默认开启多部分混合 AOF(base rdb + 增量 incr.aof),这个参数不再作为单一日志文件名,只是前缀标识。
appenddirname "appendonlydir"
全新独立 AOF 文件夹配置 Redis 不再把 AOF 日志散落在根目录,统一全部放进 /usr/local/redis/appendonlydir/ 文件夹内,里面会生成三类文件:
-
appendonly.aof.数字.base.rdb:基础快照(RDB 格式,全量数据) -
appendonly.aof.数字.incr.aof:增量操作日志(新增写命令) -
appendonly.aof.manifest:清单文件,记录当前生效的 base、incr 文件
好处:RDB 快照和 AOF 增量日志分开管理,重写、恢复时更清晰,不会和 dump.rdb 混在一起。
# 记得重启redis
[root@redis ~]# systemctl restart redis
[root@redis ~]# systemctl status redis
# 查看目录
[root@redis ~]# ll /export/server/redis/appendonlydir
验证测试
进到redis安装目录下的redis里面,/export/server/redis/,找appendonlydir,有这个的话就说明AOF持久化已经打开了
ll /export/server/redis/appendonlydir/
添加一些数据
redis-cli
127.0.0.1:6379 > set name devops
127.0.0.1:6379 > set age 25
127.0.0.1:6379 > set address canton
查看AOF文件
-rw-r--r--. 1 root root 88 Nov 29 16:52 appendonly.aof.1.base.rdb
-rw-r--r--. 1 root root 0 Nov 29 16:52 appendonly.aof.1.incr.aof
-rw-r--r--. 1 root root 88 Nov 29 16:52 appendonly.aof.manifest
incr.aof:增量日志,记录后面set name/age/address三条写入命令
灾难与数据恢复
模拟灾难前,我们把上面我标红的这个再备份一下
cp /export/server/redis/appendonlydir/appendonly.aof.1.incr.aof ./
重启进去看一下,数据正常存在,删三个数据模拟灾难,退出停服务
[root@redis ~]# systemctl restart redis
[root@redis ~]# redis-cli
127.0.0.1:6379> get name
"devops"
127.0.0.1:6379> get age
"25"
127.0.0.1:6379> get address
"canton"
127.0.0.1:6379> del name age address
(integer) 3
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> get age
(nil)
127.0.0.1:6379> get address
(nil)
127.0.0.1:6379> exit
[root@redis ~]# systemctl stop redis
重启 Redis 的话,会先加载 base 快照,再依次执行 set、del,最后数据是空的。
想要三个数据回来,cp 根目录备份的旧incr.aof 覆盖回去
cp appendonly.aof.1.incr.aof /export/server/redis/appendonlydir/
然后重启,再进去验证一下,就会发现数据全部回来了
RDB + AOF 混合模式
**RDB + AOF 混合模式,**指在同一 Redis 实例中既开启 RDB 快照,又开启 AOF 日志,结合两者优势:
-
RDB:提供 快速恢复(单文件加载效率高)和 全量数据备份(时间点快照)。
-
AOF:提供 数据高可靠性(记录所有写操作,丢失数据量更小)。
通过混合模式,既保留 RDB 的快速加载能力 ,又弥补 AOF 文件体积大、恢复慢的缺点,实现 "又快又安全" 的数据持久化。
|-----------|----------------------|----------------------------|
| 特性 | RDB(快照) | AOF(Append Only File) |
| 数据形式 | 内存数据的二进制快照(时间点备份) | 记录所有写操作命令(日志) |
| 恢复方式 | 直接加载快照文件 | 重启时重放命令 |
| 数据安全性 | 可能丢失最后一次快照后的所有数据 | 可控丢失(默认每秒同步,最多丢 1 秒数据) |
| 文件大小 | 更紧凑(仅存数据状态) | 通常比 RDB 大(记录详细命令) |
| 性能影响 | 全量同步可能阻塞主线程(fork 压力) | 写操作需同步到磁盘(依赖策略,默认每秒同步性能较好) |
| 适用场景 | 对恢复速度要求高(如大数据量备份) | 对数据安全性要求高(如金融业务) |
AOF 和 RDB 持久化可以同时启用,不会有问题。
如果启动时启用了 AOF,Redis 将加载 AOF ,即文件具有更好的持久性保证。则 AOF 的优先级高于 RDB。
数据恢复顺序及加载流程
同时开启 RDB 和 AOF 时
Redis 启动先判断有没有 AOF 文件
- 有 AOF → 只加载整套 AOF(base.rdb+incr.aof),完全忽略 dump.rdb
- 没有 AOF 文件 → 才会去加载 dump.rdb
如果 AOF 文件不存在,会刷新一个空的 AOF 配置,而忽略 RDB 文件。
redis主从复制
Redis 主从复制原理
1)第一次同步时,从库会把主库的数据整体拉取一份(全量同步)
主库会生成一个数据快照,并把这份快照发给从库,从库加载后与主库保持一致。
2)全量同步完成后,主库会把这期间发生的写操作再补发给从库
这样从库不会漏掉主库在同步过程中产生的新数据。
3)进入正常工作后,主库的每一条写操作都会实时推送给从库(增量同步)
从库按顺序执行这些写命令,从而与主库持续保持数据一致。
4)如果从库短暂断线,重新连接后会尽量只同步缺失的部分命令(部分同步)
避免每次都全量同步,提高效率。
5)主从之间维持长连接,通过心跳与偏移量确保同步状态正常
这样主库可以知道从库是否在线、是否落后。
注意:
主从复制使用的是"快照 + 写命令流 "的方式,与 RDB/AOF 是否开启持久化无关,即使持久化关闭也不影响主从复制正常工作。
主从环境
|---------|----------------|-------------|--------|
| 主机名 | IP | Redis版本 | 角色 |
| redis03 | 192.168.88.103 | redis-7.4.0 | master |
| redis02 | 192.168.88.102 | redis-7.4.0 | slave |
优化Linux环境
sed -i -r 's/SELINUX=[ep].*/SELINUX=disabled/g' /etc/selinux/config
setenforce 0
systemctl stop firewalld &> /dev/null
systemctl disable firewalld &> /dev/null
iptables -F
修改主机名
hostnamectl set-hostname redis03 && bash
hostnamectl set-hostname redis02 && bash
没有redis的服务器安装一下
找到对应的安装包资源,使用wget命令下载,这里安装的7.4.0版本。 安装包资源地址:https://download.redis.io/releases/
上传或者下载Redis到Linux系统中 wget https://download.redis.io/releases/redis-7.4.0.tar.gz
本文中最上面有redis的安装方法 我是两个redis安装包都上传到/export/software 解压安装在/export/server 最好俩个服务的redis统一路径
两个服务器都清理一下数据
做主从配置要求两个服务器的原始文件一致,进行这一步之前可以先拍快照存一下(RDB持久化快照)
redis-cli
flushall
配置 master
192.168.88.103
vim /export/server/redis/conf/redis.conf
开启监听,在网络中与其他服务器进行网络交互的网卡
bind的ip指的是其他的主机需要和这个主机通讯的ip
bind 0.0.0.0 # 允许所有IP连接
protected-mode no # 关闭redis安全保护机制,允许主从、哨兵、集群中各节点之间的相互访问,没有安全限制
logfile "/export/server/redis/redis.log" #日志文件存放路径
replica-read-only no
删除注释行和空行(可选)
[root@web01 conf]# vim redis.conf
:g/#/d 删除注释行
:g/^$/d 删除空行
配置 slave
192.168.88.102
vim /export/server/redis/conf/redis.conf
bind 0.0.0.0
protected-mode no
logfile "/export/server/redis/redis.log"
节点专属配置,告诉当前 Redis 实例:我是从库,要去跟着 IP 为192.168.88.103、端口 6379 的主库做数据同步,搭建一主一从
echo 'replicaof 192.168.88.103 6379' >> /export/server/redis/conf/redis.conf
systemctl restart redis
systemctl status redis --no-pager
#看完从服务器,再去看一下主服务器
redis-cli
info replication


测试Redis主从,主写从读
注:slave不允许写操作,因为从服务器Redis配置文件里进行了replica-read-only设置,也是符合业务的使用需求 replica-read-only yes
也就是在主服务器进行一些操作比如 redis-cli set aaa 111 从服务器 redis-cli get aaa 得到111
如果没有得到的话 看一下从服务器info replication 看状态那里起来没有

如果是down的话 两边配置再检查一下,再两边重启一下,再看一下防火墙,端口放行没有
从库
# 停止redis
systemctl stop redis
# 删除本地快照
rm -rf /export/server/redis/dump.rdb
rm -rf /export/server/redis/appendonlydir
# 启动redis
systemctl start redis
从库再跟主库重建
replicaof 192.168.88.103 6379
info replication

ok 主从复制搭建成功
安全限制
目前redis已经完成相关配置,缺少安全配置,如IP限制,密码限制。
==IP限制登录==
① master打开配置IP限制
注意如果有防火墙,先关闭防火墙或者开启端口放行。
bind IP 限制(IP 白名单,只允许指定机器访问)
# vim /export/server/redis/conf/redis.conf
bind 127.0.0.1 本机的网卡IP地址
bind 192.168.88.101 具体某个IP
bind 0.0.0.0 适合公网环境,没有限制,任意主机均可访问
bind 127.0.0.1只允许本机
127.0.0.1登录,外部机器(从库、其他服务器)完全连不上,适合单机本地使用。
bind 127.0.0.1 192.168.88.103(我的主库 redis03)只允许本机 + 本机内网 IP 访问;如果你要让从库 redis02 连主库,只写这个不够,会拦截从库。
bind 0.0.0.0 -::1(现在用的配置)监听服务器所有网卡,内网所有机器、公网都能访问,无 IP 限制,不安全,仅实验临时使用
重启Redis服务
② slave服务器进行远程连接测试
# redis-cli -h 远程IP地址
如果设置了密码也可以通过-a指定密码,密码会保留终端
③ master进行密码限制
vim /export/server/redis/conf/redis.conf
# 1051行 requirepass 密码
requirepass 123456
systemctl restart redis
systemctl status redis
在slave服务器测试密码是否可用
redis-cli -h 192.168.88.103
auth 123456
或者这样
redis-cli -h 192.168.88.103 -a 123456
info
如果开启了安全限制,从服务器的配置里 要写上主服务器的密码
# vim /export/server/redis/conf/redis.conf
547行:masterauth 密码
systemctl restart redis
systemctl status redis
PHP redis
PHP 程序本身原生不认识 Redis,装上这个扩展,PHP 代码才能连接、读写 Redis 缓存。
- Redis 是独立缓存服务(前面搭的一主一从 Redis 数据库);
- PHP 是后端编程语言,二者是两套独立软件;
redis-5.3.3.tgz是 PHP 的驱动扩展,相当于「翻译官」,打通 PHP ↔ Redis。
php redis的安装配置
# 1. 创建存放目录
mkdir -p /export/software /export/server
cd /export/software
# 2. 下载redis扩展源码包
wget https://pecl.php.net/get/redis-5.3.3.tgz
# 查看文件
ls -hl
# 3. 解压
tar xf redis-5.3.3.tgz
# 新增:把解压目录移动到 /export/server 并重命名 php-redis
mv redis-5.3.3 /export/server/php-redis
# 进入新目录编译
cd /export/server/php-redis
# 4. 安装编译依赖
yum install -y gcc make autoconf php-devel php-pear php-fpm
# 生成编译配置文件
which phpize
#看一眼
/usr/bin/phpize
# 编译配置(删掉无用--enable-fpm)
./configure --with-php-config=/usr/bin/php-config
# 编译
make
# 安装扩展模块到php扩展目录
make install
# 查看PHP版本
php -v
# 启动php-fpm服务
systemctl start php-fpm
systemctl status php-fpm
# 验证扩展(未改php.ini前无输出redis)
php -m|grep redis
配置php.ini
#查php.in路径
php --ini
vim /etc/php.ini
#在最后一行添加如下内容
extension = redis.so
tail -n 1 /etc/php.ini
优化php.ini配置文件
cp /etc/php.ini /etc/php.ini.bak
sed -i '/^\s*;/d; /^$/d' /etc/php.ini
sed -i
sed:流式文本编辑工具-i:in-place 原地修改,直接修改原文件,不输出到屏幕;若写-i.bak会自动备份原文件为php.ini.bak,更安全:sed -i.bak '/^\s*;/d; /^$/d' /etc/php.ini
两个删除规则用分号 ; 分隔,同时执行
/^\s*;/d 删除所有注释行
^:行开头位置\s*:0 个或多个空白字符(空格、Tab 制表符),兼容「行首带空格再写;」的注释;:php.ini 的注释标识符d:delete 删除当前匹配行
/^$/d 删除纯空行
^行首,$行尾,^$代表一行里没有任何字符(纯空白行)d删除空行
回滚命令:cp /etc/php.ini.bak /etc/php.ini
测试php redis是否安装成功
systemctl restart php-fpm
php -m
systemctl restart php-fpm
php -m|grep redis
企业应用
缓存配置文件 cache.php
这份是 CRMEB 商城项目的缓存配置文件 cache.php,用来控制项目缓存存在哪里:原来默认存本地文件 File,现在改成存 Redis 缓存服务器,实现两大核心作用:
-
业务数据缓存:商品、分类、首页热门数据放 Redis,减少频繁查询 MySQL,降低数据库压力;
-
Session 会话共享:用户登录信息存入 Redis,多站点、多台 Web 服务器共用登录状态,实现单点登录 SSO。
两个存储方式对比
1)File 文件缓存(旧方案)
session、缓存文件存在服务器本地 /runtime/cache 文件夹:
- 缺点:① 多台 Web 服务器部署时,每台机器独立存 session,用户切换服务器会掉线;② 大量并发读写文件 IO 慢,性能差;③ 服务器重启、清理文件会丢失全部缓存 / 登录状态。
2)Redis 缓存(修改后的方案)
把缓存、session 全部存独立 Redis 服务:
- 优点:① 内存读写,速度远快于文件;② 多 Web 服务器共用同一 Redis,所有站点共享 session,实现单点登录;③ 可设置过期时间、持久化,数据不易丢失;④ 分担 MySQL 查询压力,高频数据不用反复查库。
① 更改缓存保存方式
vim /data/CRMEB/crmeb/config/cache.php
return [
// 默认缓存驱动
'default' => Env::get('cache.driver', 'redis'), //修改这里
// 缓存连接方式配置
'stores' => [
'file' => [
// 驱动方式
'type' => 'File',
// 缓存保存目录
'path' => app()->getRuntimePath() . 'cache' . DIRECTORY_SEPARATOR,
// 缓存前缀
'prefix' => '',
// 缓存有效期 0表示永久缓存
'expire' => 0,
// 缓存标签前缀
'tag_prefix' => 'tag:',
// 序列化机制 例如 ['serialize', 'unserialize']
'serialize' => [],
],
// 更多的缓存连接
// redis缓存
'redis' => [
// 驱动方式
'type' => 'redis',
// 服务器地址
'host' => Env::get('redis.redis_hostname', '192.168.88.132'), //缓存服务器地址
// 端口
'port' => Env::get('redis.port', '6379'), //缓存服务器地址端口
// 密码
'password' => Env::get('redis.redis_password', '123456'), //缓存服务器密码
// 缓存有效期 0表示永久缓存
'expire' => 0 ,
// 缓存前缀
'prefix' => Env::get('cache.cache_prefix', 'c:'),
// 缓存标签前缀
'tag_prefix' => Env::get('cache.cache_tag_prefix', 'CRMEB:'),
// 数据库 0号数据库
'select' => intval(Env::get('redis.select', 0)),
// 序列化机制 例如 ['serialize', 'unserialize']
'serialize' => [],
// 服务端主动关闭
'timeout' => 0
],
],
];
② 更改Session存储位置
https://doc.thinkphp.cn/v6_1/Session.html
vim /data/CRMEB/crmeb/config/session.php
web01 session文件
web02 session文件
web01/web02 => session => redis
<?php
// +----------------------------------------------------------------------
// | 会话设置
// +----------------------------------------------------------------------
return [
// session name
'name' => 'PHPSESSID',
// SESSION_ID的提交变量,解决flash上传跨域
'var_session_id' => '',
// 驱动方式 支持file cache
'type' => 'cache', // 修改这里
// 存储连接标识 当type使用cache的时候有效
// 过期时间
'expire' => 1440*1000,
// 前缀
'prefix' => 'think', // 修改这里
];
Nginx+Lua+Redis 实现访问攻击黑名单 WAF
WAF:(Web Application Firewalld),根据nginx访问流量判断是否为恶意攻击,如果1s内超过200个请求,就认为这是恶意攻击。
① redis01,安装openresty 和之前安装一致
#!/bin/bash
dnf -y install pcre-devel zlib-devel openssl-devel
tar -zxf openresty-1.25.3.2.tar.gz
cd openresty-1.25.3.2
./configure && make && make install
② 编写lua脚本
# mkdir -p /usr/local/lua
# vim /usr/local/lua/access_limit.lua
-- access_by_lua_file '/usr/local/lua/access_limit.lua'
local function close_redis(red)
if not red then
return
end
--释放连接(连接池实现)
local pool_max_idle_time = 10000 --毫秒
local pool_size = 100 --连接池大小
local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
if not ok then
ngx_log(ngx_ERR, "set redis keepalive error : ", err)
end
end
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000)
local ip = "192.168.88.132" -- redis ip
local port = 6379 -- redis port
local ok, err = red:connect(ip,port)
red:auth("123456") -- redis auth password
if not ok then
return close_redis(red)
end
local clientIP = ngx.req.get_headers()["X-Real-IP"]
if clientIP == nil then
clientIP = ngx.req.get_headers()["x_forwarded_for"]
end
if clientIP == nil then
clientIP = ngx.var.remote_addr
end
local incrKey = "user:"..clientIP..":freq"
local blockKey = "user:"..clientIP..":block"
local is_block,err = red:get(blockKey) -- check if ip is blocked
if tonumber(is_block) == 1 then
ngx.exit(ngx.HTTP_FORBIDDEN) -- return 403
return close_redis(red)
end
res, err = red:incr(incrKey)
if res == 1 then
res, err = red:expire(incrKey,1)
end
if res > 5 then -- block 频率,测试环境可以考虑设置为3或5
res, err = red:set(blockKey,1)
res, err = red:expire(blockKey,600)
end
close_redis(red)
在Nginx中添加Lua脚本:
# vim /usr/local/openresty/nginx/conf/nginx.conf
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
root html;
location / {
index index.html index.htm;
access_by_lua_file /usr/local/lua/access_limit.lua;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
# 启动openresty
cd /usr/local/openresty
bin/openresty
启动openresty,在lb01上面可以先停旧的nginx(如何存在了nginx),然后再启动,也可以通过修改/usr/local/openresty/nginx/conf里面nginx.conf监听端口号
停掉旧版本nginx再启动
[root@node1 conf]# pkill nginx
[root@node1 conf]# cd /usr/local/openresty
[root@node1 conf]# bin/openresty
或者修改/usr/local/openresty/nginx/conf/nginx.conf端口
[root@node1 conf] vim /usr/local/openresty/nginx/conf/nginx.conf
server {
listen 8080; //修改端口号
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
access_by_lua_file /usr/local/lua/access_limit.lua;
}
}
启动服务
[root@node1 conf]# /usr/local/openresty/bin/openresty
通过浏览器进行访问
③ 测试验证黑名单效果
实现商城前端恶意攻击IP加入黑名单
停掉旧版nginx 服务器并禁止开机自启动
复制配置文件
[root@lb01 conf]# systemc disable nginx --now
[root@lb01 conf]# cp -a /etc/nginx/conf.d /usr/local/openresty/nginx/conf/
修改配置文件,
[root@lb01 conf]# cd /usr/local/openresty/nginx/conf/conf.d
[root@lb01 conf]# vim my_web_service.conf
upstream my_web_service {
#ip_hash;
server 192.168.88.130:80;
server 192.168.88.131:80;
}
server {
listen 80;
#################### 配置开始 ######################
server_name www.itheimashop.com; # 替换为实际域名或 IP
location / {
proxy_pass http://my_web_service; # 将请求转发到负载均衡组
proxy_set_header HOST $host;
proxy_set_header X-Real-IP $remote_addr; # 添加这两行
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
access_by_lua_file /usr/local/lua/access_limit.lua;
}
#################### 配置结束 ######################
}
修改lua脚本访问频率
[root@lb01 lua]# vim /usr/local/lua/access_limit.lua
-- access_by_lua_file '/usr/local/lua/access_limit.lua'
local function close_redis(red)
if not red then
return
end
--释放连接(连接池实现)
local pool_max_idle_time = 10000 --毫秒
local pool_size = 100 --连接池大小
local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
if not ok then
ngx_log(ngx_ERR, "set redis keepalive error : ", err)
end
end
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000)
local ip = "192.168.88.132" -- redis ip
local port = 6379 -- redis port
local ok, err = red:connect(ip,port)
red:auth("123456") -- redis auth password
if not ok then
return close_redis(red)
end
local clientIP = ngx.req.get_headers()["X-Real-IP"]
if clientIP == nil then
clientIP = ngx.req.get_headers()["x_forwarded_for"]
end
if clientIP == nil then
clientIP = ngx.var.remote_addr
end
local incrKey = "user:"..clientIP..":freq"
local blockKey = "user:"..clientIP..":block"
local is_block,err = red:get(blockKey) -- check if ip is blocked
if tonumber(is_block) == 1 then
ngx.exit(ngx.HTTP_FORBIDDEN) -- return 403
return close_redis(red)
end
res, err = red:incr(incrKey)
if res == 1 then
res, err = red:expire(incrKey,1)
end
if res > 100 then -- block 频率,网站前端改成100
res, err = red:set(blockKey,1)
res, err = red:expire(blockKey,600)
end
close_redis(red)
启动服务
[root@lb01 lua]# /usr/local/openresty/bin/openresty
浏览器http://www.itheimashop.com访问
高可用架构对比
|----------|------|------------|--------|
| 特性 | 主从复制 | 哨兵模式 | 集群模式 |
| 架构 | 1主多从 | 主从+哨兵监控 | 多主多从分片 |
| 故障转移 | 手动 | 自动(10-30秒) | 自动(秒级) |
| 扩展性 | 垂直扩展 | 读写分离 | 水平扩展 |
| 数据量 | 单机容量 | 单机容量 | 海量数据 |
| 适用场景 | 简单备份 | 中小规模高可用 | 大规模高并发 |
redis哨兵
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
-
通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
-
当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。
故障切换流程
1)主观下线(Subjective Down, SDOWN)
哨兵 1 定期检测主节点(如心跳超时、无响应),发现主节点不可用,但 仅代表哨兵 1 的主观判断(可能因网络问题误判),此时进入 主观下线状态。
2)客观下线(Objective Down, ODOWN)
其他哨兵(如哨兵 2、哨兵 3)也检测到主节点不可用,且 达到预设的投票阈值(如多数哨兵同意) 时,哨兵集群通过内部投票确认主节点 确实故障(客观下线)。
3)选举 Leader 哨兵
多个哨兵通过投票机制选出一个 Leader 哨兵(负责执行故障切换操作),其他哨兵仅参与决策,不直接操作。
4)执行 Failover(故障切换)
Leader 哨兵主导以下操作:
-
从所有从节点中 选择一个最优从节点(如数据最完整、延迟最低的节点)提升为 新主节点。
-
将其他从节点重新配置为 同步新主节点(通过 SLAVEOF命令)。
-
若原主节点恢复,将其降级为 新主节点的从节点。
5)通知客户端(透明化)
- 哨兵集群通过 发布订阅模式(Pub/Sub) 广播主节点变更信息,所有连接的客户端(如应用程序)收到通知后,自动将请求路由到 新主节点 ,对业务代码无感知(故障切换对客户端透明)。
部署实战-环境准备
配置3个哨兵和1主2从的Redis服务器来演示这个过程
特别注意:使用Redis哨兵模式,最少需要3个节点(一主多从结构),这样至少能部署3 个哨兵进程,从而保证共同投票选出新的主节点。
Redis 哨兵模式的核心功能是监控主从节点状态,并在主节点故障时自动完成**故障切换(Failover),**这一过程依赖多个哨兵节点的协同投票。
给一个 Linux 换国内阿里云下载源
安装软件(比如 redis、gcc、net-tools)速度变快,不会国外源超时报错,执行后会生成文件 /etc/yum.repos.d/aliyun.repo
如果用下面的这一串代码,安装成功后,Redis 的可执行文件将被安装到 /usr/local/redis
我这没用这个,用的是压缩包,三个redis统一上传安装包路径/export/software 解压安装路径/export/server 之后命令的路径按照我自己的来,后面步骤记得修改路径
cat >/etc/yum.repos.d/aliyun.repo<<EOF
[baseos]
name=CentOS Stream \$releasever - BaseOS
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/BaseOS/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1
[baseos-debug]
name=CentOS Stream \$releasever - BaseOS - Debug
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/BaseOS/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
[baseos-source]
name=CentOS Stream \$releasever - BaseOS - Source
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/BaseOS/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
[appstream]
name=CentOS Stream \$releasever - AppStream
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/AppStream/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1
[appstream-debug]
name=CentOS Stream \$releasever - AppStream - Debug
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/AppStream/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
[appstream-source]
name=CentOS Stream \$releasever - AppStream - Source
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/AppStream/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
[crb]
name=CentOS Stream \$releasever - CRB
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/CRB/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0
[crb-debug]
name=CentOS Stream \$releasever - CRB - Debug
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/CRB/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
[crb-source]
name=CentOS Stream \$releasever - CRB - Source
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/CRB/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
[highavailability]
name=CentOS Stream \$releasever - HighAvailability
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/HighAvailability/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0
[highavailability-debug]
name=CentOS Stream \$releasever - HighAvailability - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/HighAvailability/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
[highavailability-source]
name=CentOS Stream \$releasever - HighAvailability - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/HighAvailability/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
[nfv]
name=CentOS Stream \$releasever - NFV
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/NFV/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0
[nfv-debug]
name=CentOS Stream \$releasever - NFV - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/NFV/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
[nfv-source]
name=CentOS Stream \$releasever - NFV - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/NFV/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
[rt]
name=CentOS Stream \$releasever - RT
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/RT/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0
[rt-debug]
name=CentOS Stream \$releasever - RT - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/RT/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
[rt-source]
name=CentOS Stream \$releasever - RT - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/RT/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
[resilientstorage]
name=CentOS Stream \$releasever - ResilientStorage
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/ResilientStorage/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0
[resilientstorage-debug]
name=CentOS Stream \$releasever - ResilientStorage - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/ResilientStorage/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
[resilientstorage-source]
name=CentOS Stream \$releasever - ResilientStorage - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/ResilientStorage/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
[extras-common]
name=CentOS Stream \$releasever - Extras packages
baseurl=http://mirrors.aliyun.com/centos-stream/SIGs/\$stream/extras/\$basearch/extras-common/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-Extras-SHA512
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1
[extras-common-source]
name=CentOS Stream \$releasever - Extras packages - Source
baseurl=http://mirrors.aliyun.com/centos-stream/SIGs/\$stream/extras/source/extras-common/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-Extras-SHA512
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
EOF
三台服务器全部安装redis
统一解压安装在/exprot/server
统一redis版本7.4.0
- 192.168.88.102 从
- 192.168.88.103 主服务器
- 192.168.88.104 从
在上文 已经配置好了102和103 一主一从 103是主服务器

没有redis的服务器安装一下,给一串代码作为参考,我自己是用的安装包
第一步:找到对应的安装包资源,使用wget命令下载
安装包资源地址:https://download.redis.io/releases/
第二步:上传或者下载Redis到Linux系统中
wget https://download.redis.io/releases/redis-7.4.0.tar.gz
第三步:配置=>编译=>安装
tar -zxvf redis-7.4.0.tar.gz
cd redis-7.4.0
yum install epel-release -y
yum install make gcc jemalloc jemalloc-devel -y
make
make PREFIX=/usr/local/redis install
第四步:配置环境变量
echo 'PATH=$PATH:/usr/local/redis/bin/' >> /etc/profile && source /etc/profile
查看版本
redis-cli -v
优化系统
过量使用内存设置为1
vm.overcommit_memory 的 3 种模式
0(默认)内核根据启发式算法决定是否允许超分配"看情况给,不一定准"
1允许内存超分配,不做任何限制想分就分,不管你有没有这么多内存
2严格限制,不允许超分配非常安全,但可能导致程序申请内存失败
echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf
#配置好后执行以下命令让配置生效
sysctl -p
服务器环境初始化脚本,三台 Redis 哨兵机器全部要执行,作用:关闭安全限制、放开网络互通,保证 Redis 主从 + 哨兵互相通信不被拦截
sed -i -r 's/SELINUX=[ep].*/SELINUX=disabled/g' /etc/selinux/config
# sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config &> /dev/null
setenforce 0
systemctl stop firewalld &> /dev/null
systemctl disable firewalld &> /dev/null
iptables -F
iptables -t nat -F
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
modprobe bridge
echo 1 > /proc/sys/net/ipv4/ip_forward
echo net.ipv4.ip_forward = 1 >> /etc/sysctl.conf
sysctl -p
一主两从配置
三台服务器都要执行,写过的可以略过
mkdir -p /exprot/server/redis/conf
cp /root/redis-7.4.0/redis.conf /export/server/redis/conf/
#修改配置
vim /export/server/redis/conf/redis.conf
#88: bind 127.0.0.1 -::1 --> bind 0.0.0.0
#310: daemonize no --> daemonize yes
#过量使用内存设置为1
echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf
#配置好后执行以下命令让配置生效
sysctl -p
vm.overcommit_memory 的 3 种模式
0(默认)内核根据启发式算法决定是否允许超分配"看情况给,不一定准"
1允许内存超分配,不做任何限制,想分就分,不管你有没有这么多内存
2严格限制,不允许超分配非常安全,但可能导致程序申请内存失败
启动
redis-server /usr/local/redis/conf/redis.conf
查看服务进程
ps aux | grep redis
ps -ef | grep redis
netstat -pantul|grep 6379
配置服务
cat >/etc/systemd/system/redis.service<<EOF
[Unit]
Description=redis-server
After=network.target
[Service]
Type=forking
ExecStart=/export/server/redis/bin/redis-server /export/server/redis/conf/redis.conf
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
pkill redis-server
systemctl daemon-reload
systemctl start redis
systemctl enable redis
systemctl status redis --no-pager
配置master
#创建conf目录,复制redis.conf配置文件
mkdir -p /export/server/redis/conf
cp redis.conf /export/server/redis/conf/
修改redis.conf配置文件
cp /export/server/redis/conf/redis.conf /export/server/redis/conf/redis.conf.bak
sed -i '/^\s*#/d; /^$/d' /export/server/redis/conf/redis.conf
创建日志目录
mkdir -p /export/server/redis/logs/
vim /export/server/redis/conf/redis.conf
关键参数如下:
bind 0.0.0.0 # 允许所有IP连接
protected-mode no # 关闭redis安全保护机制,允许主从、哨兵、集群中各节点之间的相互访问,没有安全限制
port 6379 # 服务端口
daemonize yes # 允许后台运行
requirepass 123456 # 从服务器连接需要使用密码
logfile "/export/server/redis/logs/redis.log" # 日志存储目录与名称
replica-read-only no # 可写
注意:别的都是手动改几个字母加几个字母,密码这是原来的注释行,纯净配置已经给删了,需要手动添加到尾行,在配置salve时密码需要与主服务器一致,因为宕机后还要切换主服务器,三个服务器密码必须一致
systemctl restart redis
systemctl status redis --no-pager
配置slave
本文里在【目录:主从复制-配置slave】里说过 这里简述一下
vim /export/server/redis/conf/redis.conf
#修改,行号为原配置行号,不是纯净配置行号
88行 bind 0.0.0.0
112行 protected-mode no
356行 logfile "/export/server/redis/redis.log"
#添加
# 本机登录密码
requirepass 123456
# 连接主库的认证密码(和主库requirepass保持一致)
masterauth 123456
# 指定主节点地址
replicaof 192.168.88.103 6379
replicaof固定关键字:声明当前 Redis 实例作为从节点,跟随指定主节点;192.168.88.103:主服务器 IP 地址;6379:Redis 默认端口。
主服务器添加了密码 从服务器必须添加一致密码
注意:默认 replica-read-only yes,从库保持 yes 不要改成 no

systemctl restart redis
systemctl status redis --no-pager
验证测试
兜底检查,确保当前运行环境网络完全放行,方便后面验证主从、哨兵。
sed -i -r 's/SELINUX=[ep].*/SELINUX=disabled/g' /etc/selinux/config
# sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config &> /dev/null
setenforce 0
systemctl stop firewalld &> /dev/null
systemctl disable firewalld &> /dev/null
iptables -F
iptables -t nat -F
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
三个库都这样上去看看
redis-cli
auth 123456
info replication

修改数据

配置 Sentinel 哨兵
三台机器各自跑 1 个哨兵,凑成总共 3 个哨兵集群
我的架构:103(主库)、102(从库)、104(从库),每台都配置、启动一份 sentinel,一共 3 个哨兵进程,互相通信、一起监控主库。
主观下线 vs 客观下线(阈值 2 的作用)
sentinel monitor mymaster 192.168.88.103 6379 2 末尾的2就是阈值
① 主观下线(SDOWN)
单独某一台哨兵,ping 不通主库、收不到响应,它自己单方面认为:主库坏了。只是这一个哨兵自己的判断,不作数,不会切换主从。
② 客观下线(ODOWN)
整个哨兵集群里,至少 2 台哨兵同时判定主库失联,达成投票阈值,才认定主库真故障。为什么要设 2?防止单台哨兵网络卡顿、断网误判,避免无故切换主库。
③ 触发故障转移
一旦达到客观下线,哨兵集群自动执行 failover:
-
在两个从库(102、104)里选一台新主库;
-
把剩下另一台从库指向新主同步;
-
自动更新所有哨兵配置,记录新 master 地址。
什么是故障转移 failover
简单场景:主库 103 关机 / 宕机 → 2 个哨兵确认故障 → 自动把 102 或者 104 提拔成新主库,业务不用手动改 IP,还能正常读写 Redis。
什么是高可用 HA核心:某一台机器挂了,整套 Redis 服务还能正常使用。
只有单台 Redis:机器一坏,服务直接瘫痪;
一主两从 + 三哨兵:主库宕机,自动切换从库上位,业务持续可用,这就是高可用。高可用靠两件事实现:哨兵持续健康检查 + 故障自动转移。
结合当前配置对应一下
-
三台机器全部配 sentinel.conf,启动后形成 3 哨兵集群;
-
阈值写 2:必须≥2 个哨兵同时失联主库,才切换;
-
哨兵 24 小时持续检测主库、从库状态;
-
主库故障时全自动切换,不需要你手动改 replicaof、重启服务。
举个直观例子
三台哨兵:S1 (103)、S2 (102)、S3 (104)
-
只有 S1 连不上主库,S2、S3 正常 → 仅主观下线,不切换;
-
S1、S2 同时连不上主库 → 达到阈值 2,客观下线,启动故障转移;
-
切换完成后,所有哨兵自动记录新主库 IP,后续业务连新主。
配置 Sentinel
在 sentinel.conf 配置文件中,可以找到 port,这里是用来设置 sentinel 的端口;一般情况下,至少需要三个哨兵对 redis 进行监控。
这个红框里的就是 哨兵专属配置文件

三个服务器都要操作这个
-
复制redis下的配置到redis下的conf下 这样的话哪怕同名但是路径不同 方便我们修改
-
创建一个存放日志的路径
-
清理前面复制到conf路径下的配置,清掉注释行空行
cp /export/server/redis/sentinel.conf /export/server/redis/conf/
mkdir -p /export/server/redis/logs
sed -i '/^\s*#/d; /^\s*$/d' /export/server/redis/conf/sentinel.conf
vim /export/server/redis/conf/sentinel.conf
修改这两处
daemonize yes
logfile "/export/server/redis/logs/sentinel.log"

三台Linux的redis.conf都要改成replica-read-only no
sed -i 's/replica-read-only yes/replica-read-only no/' /export/server/redis/conf/redis.conf
grep replica-read-only /export/server/redis/conf/redis.conf
systemctl restart redis
systemctl status redis --no-pager

配置master的端口
Redis 官方英文手册里哨兵(Sentinel)完整官方教程。
https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/
跟着我做也ok 一共两步
哨兵只需要写监控 Master,不用手动写 Slave 配置
1,先把我们之前改过的配置文件再次备份一下
cp /export/server/redis/conf/sentinel.conf /export/server/redis/conf/sentinel.conf.bak
vim /export/server/redis/conf/sentinel.conf
2,新增两行,并且删掉 原来的 sentinel monitor mymaster 127.0.0.1 6379 2
sentinel monitor mymaster 192.168.88.103 6379 2
sentinel auth-pass mymaster 123456

- sentinel monitor mymaster 192.168.88.103 6379 2
- 注:2权值/阈值,代表至少需要2个哨兵确认才能客观下线。 原理:首先某个哨兵发现master主节点无法连接(无法响应),则会标记为主观下线,如果超过2台哨兵确认master节点故障,则标记为客观下线,并触发故障转移。
启动所有的 Sentinel
在三台机器上都执行
# 启动Sentinel
/export/server/redis/bin/redis-sentinel /export/server/redis/conf/sentinel.conf
# 查看进程
ps -ef | grep redis
或者
ps -aux | grep redis
ps aux|grep sentinel
# 杀掉进程
kill -9 PID
或者
pkill redis-sentinel

配置哨兵服务
先停止手动起来的哨兵服务,不然后面端口冲突
在所有Linux服务器上执行以下命令
/export/server/redis/bin/redis-cli -p 26379 shutdown
netstat -pantul|grep 26379

这个是优雅的停 需要等一下,然后多看几次
cat >/etc/systemd/system/redis-sentinel.service<<EOF
[Unit]
Description=Redis Sentinel
After=network.target
[Service]
Type=forking
ExecStart=/export/server/redis/bin/redis-sentinel /export/server/redis/conf/sentinel.conf
ExecStop=/export/server/redis/bin/redis-cli -p 26379 shutdown
ExecReload=/export/server/redis/bin/redis-cli -p 26379 shutdown && /export/server/redis/bin/redis-sentinel /export/server/redis/conf/sentinel.conf
PrivateTmp=true
Restart=always
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl start redis-sentinel
systemctl enable redis-sentinel
systemctl status redis-sentinel --no-pager

验证测试
在所有的机器上校验密码
# grep 全局检索 .conf 后缀文件
grep -n '123456' /export/server/redis/conf/*.conf

- redis 主从库密码是 123456,配置生效;
- 哨兵配置里 auth-pass 密码和 Redis 完全一致;如果密码不一样,哨兵会连不上主库,监控失败;
- 两台配置文件都正确写入密码,没有漏配、输错数字。
为主节点和从节点配置密码
三个服务器都得有
在/export/server/redis/conf/redis.conf配置添加两行
在/export/server/redis/conf/sentinel.conf配置添加一行
vim /export/server/redis/conf/redis.conf
masterauth 123456
requirepass 123456
vim /export/server/redis/conf/sentinel.conf
sentinel auth-pass mymaster 123456
systemctl restart redis
systemctl restart redis-sentinel
systemctl status redis --no-pager
systemctl status redis-sentinel --no-pager
requirepass 123456:客户端连接 Redis(主 / 从)必须输密码,防止外人随便读写、删数据;masterauth 123456:从库同步主库时,需要用这个密码认证;主库设了密码,从库不写masterauth,会同步失败,主从断开;sentinel auth-pass mymaster 123456:哨兵连接主库、从库做监控、故障切换时要密码,没有这条哨兵连不上 Redis,高可用失效- 这些都是三个服务器都要改的!!
随便一个服务器 查看哨兵集群整体状态信息
redis-cli -p 26379
info sentinel
- 连接本机 26379 端口的哨兵服务(不是 6379 数据库)

这个地方应该是3,1的话不对 ,下面是解决办法
先互相ping 一下 能ping通说明是26379 端口连通性有问题 先三个服务器都下载一个工具
yum install telnet -y
主服务器
telnet 192.168.88.102 26379
telnet 192.168.88.104 26379
卡出出不来的话,按住CTRL 右中括号 退出

connected就是可以103可以连接上102,再看看能不能连上104,也能连上
node2 再去连一下node4
telnet 192.168.88.104 26379
三台全部重启,等待60秒,
systemctl restart redis-sentinel
sleep 60
校验哨兵集群完整度
redis-cli -p 26379
info sentinel
还是不行的话!!需要删掉脏数据 也就是主服务的这个地方/export/server/redis/conf/sentinel.conf
红框里的是她自动生成的 ,我们把上面的东西先复制下来到记事本随便什么地方,

三个都操作一下
systemctl stop redis-sentinel
rm -f /export/server/redis/conf/sentinel.conf
cat > /export/server/redis/conf/sentinel.conf <<EOF
protected-mode no
port 26379
daemonize yes
pidfile "/var/run/redis-sentinel.pid"
loglevel notice
logfile "/export/server/redis/logs/sentinel.log"
dir "/tmp"
acllog-max-len 128
sentinel deny-scripts-reconfig yes
sentinel resolve-hostnames no
sentinel announce-hostnames no
sentinel monitor mymaster 192.168.88.103 6379 2
sentinel auth-pass mymaster 123456
EOF
systemctl daemon-reload
systemctl start redis-sentinel
systemctl status redis-sentinel
sleep 90

故障模拟
在 master 节点上,手动关闭 Master Redis
redis-cli -p 6379
auth 123456
shutdown
systemctl stop redis
systemctl status redis
等待 30 秒~180 秒,sleep 180 ,哨兵内部投票,从两台从库中选举一台升级为新主节点。
查看哨兵集群状态,确认切换结果
redis-cli -p 26379
info sentinel
重点看 address=xxx 这一段,IP 会变成新主库(原 102 或 104)。
redis-cli -p 6379
auth 123456
set test 999
get test
恢复旧主节点(node3)
systemctl start redis
旧主重启后,会自动识别当前集群新主,降级为从库,同步新主的数据
再次查看哨兵状态,验证拓扑
旧主变成 replica,新主保持 master

redis集群
可以查看Redis官网查看集群搭建方式,连接如下
https://redis.io/topics/cluster-tutorial
如果跟着我前面做了主从和哨兵的搭建,需要恢复到刚刚下载到redis后的状态的快照,再进行redis集群搭建,服务器之间,不能有任何主从绑定
redis集群和哨兵集群有什么区别?
哨兵 Sentinel(主从高可用方案)
底层基础是一主多从复制,哨兵只做两件事:
- 监控主、从节点健康状态;
- 主库宕机时自动故障转移,把从库升为新主,修改其余从库指向新主。
- 数据不分片:所有数据完整存放在主库,从库只是完整副本;
- 容量瓶颈:单主库内存上限决定整体数据上限,数据量大单机器扛不住。
Redis Cluster 集群(分片分布式方案)
解决海量数据分片存储,同时自带基础高可用:
- 把数据分成 16384 个槽(slot),分散到多个主节点;
- 每个主节点配套若干从节点,主宕机自动切从;
- 数据分片:不同 key 存在不同主节点,单台机器只存一部分数据;
- 支持横向扩容,数据量超大场景使用。
数据分片 = 把一整份大数据,拆成多份小块,分散存到多台服务器。
| 对比项 | 哨兵 Sentinel | Redis Cluster 集群 |
|---|---|---|
| 数据存储 | 全量复制,不分片,所有节点存完整数据 | 分片存储,16384 槽分散,单节点只存部分数据 |
| 扩容方式 | 只能垂直扩容(升级单机器配置),无法水平分数据 | 水平扩容,新增主节点分摊槽位,存更多数据 |
| 槽位机制 | 无槽位概念 | 固定 16384 哈希槽,key 通过哈希分配节点 |
| 高可用能力 | 单独组件哨兵集群负责故障转移 | 集群内置故障转移,不需要额外哨兵 |
| 客户端连接 | 固定主节点地址,哨兵提供统一发现地址 | 客户端需要支持集群协议,自动跳转槽位对应节点 |
| 适用场景 | 数据量不大(几十 G 内),追求简单、运维轻便 | 百 G/TB 级海量数据,需要分布式分片存储 |
| 节点数量 | 最少 1 主 2 从 + 3 哨兵 | 最少 3 主 3 从(生产标准) |
不可以同时混用,二者互斥
- Redis Cluster 自身内置了完整的故障检测、自动主从切换逻辑,不需要哨兵;如果同时启动哨兵监控集群节点,会出现配置冲突、双机制抢着切换主库,引发脑裂、数据错乱。
- 底层架构冲突:
- 哨兵依赖传统
replicaof静态主从; - Cluster 靠槽位分配、集群握手管理主从,自动修改复制关系;两套机制同时运行会互相覆盖配置,集群直接瘫痪。
- 哨兵依赖传统
两种架构选型建议
- 业务数据量小、并发一般(缓存几十 GB)→ 哨兵 + 主从,部署简单、排错容易;
- 海量缓存、数据超百 GB、需要无限水平扩容 → Redis Cluster;
- 不存在 "Cluster + 哨兵" 组合方案,官方不支持、生产严禁混用
Redis 集群原理
Redis 集群采用去中心化架构,通过哈希槽分片将数据分散到多个节点。整个集群共有 16384 个槽,每个键根据 CRC16 哈希算法分配到特定槽中,不同节点负责不同范围的槽。客户端访问时,如果键不在当前节点,节点会返回重定向信息引导客户端访问正确节点。
集群中每个主节点都有对应的从节点,主节点负责读写,从节点复制数据并在主节点故障时自动接替,实现高可用。节点间通过 Gossip 协议相互通信,维护集群状态。这种设计使 Redis 集群能够水平扩展、自动故障转移,并保持高性能
Linux服务器环境概览
系统信息
-
操作系统: CentOS Stream release 9
-
主机用途: Redis集群环境
主机详细配置
|-------------------------------------------------------------------|----------------|
| 主机名称 | IP地址 |
| redis02.itcast.cn | 192.168.88.102 |
| redis03.itcast.cn | 192.168.88.103 |
| redis04.itcast.cn | 192.168.88.104 |
网络环境
- 网段:
192.168.88.0/24
主机名配置(每台主机分别执行)
hostnamectl set-hostname redis02.itcast.cn && bash
hostnamectl set-hostname redis03.itcast.cn && bash
hostnamectl set-hostname redis04.itcast.cn && bash
主机名解析配置
临时修改本机 /etc/hosts 主机映射文件
在每台主机的 /etc/hosts 文件中添加:
cat >/etc/hosts<<EOF
192.168.88.104 redis04 redis04.itcast.cn
192.168.88.102 redis02 redis02.itcast.cn
192.168.88.103 redis03 redis03.itcast.cn
EOF
redis的下载安装
在文章最开头就说过 后面又说了一次,不再赘述了,三个服务器都要装
- ✅ 版本:必须统一大版本,最优全版本完全一致;
- ✅ 安装路径:无强制统一要求,各机器自定义路径不影响集群;
- ⚠️ 配置文件内本地文件路径(日志、pid、持久化文件夹)只需要本机存在即可,不用和其他服务器同步。
Redis 集群配置(单节点,了解即可)
- 只跑单个 redis 集群节点:仅需 1 台服务器;
- 搭建完整多节点 Redis Cluster 集群 :
- 测试凑合用:1 台服务器(多端口多实例);
- 生产规范:3 台服务器分散部署节点。
我这里在node4上操作,弄完了再恢复快照,演示多节点
1,创建配置文件
redis-server 程序本身只有内置默认参数,默认配置不适合集群、生产环境,配置文件用来自定义所有运行规则
注:代码的路径:只是创建配置文件夹,和程序安装路径 /export/server 没有任何绑定关系
注:6个配置文件不能在同一个目录,此处我们定义如下:
mkdir -p /redis/conf/700{1..6}
/redis/conf/7001/redis.conf
/redis/conf/7002/redis.conf
/redis/conf/7003/redis.conf
/redis/conf/7004/redis.conf
/redis/conf/7005/redis.conf
/redis/conf/7006/redis.conf
2,配置文件内容
6个.conf都写下面这个 里面的数字改一下就行,比如
vim /redis/conf/7001/redis.conf 里面都写 prot 7001
vim /redis/conf/7002/redis.conf 里面都写 prot 7002 ...
port 7001 #端口
cluster-enabled yes #启用集群模式
cluster-config-file nodes_7001.conf
cluster-node-timeout 5000 #超时时间
appendonly yes
daemonize yes #后台运行
protected-mode no #非保护模式
pidfile /var/run/redis_7001.pid
我们不用一条条写进去 直接用下面的这个脚本
脚本都放在/redis/conf/下,和配置文件放一块方便管理
cd /redis/conf/
vim create_redis_cluster_conf.sh
#!/bin/bash
for i in $(seq 7001 7006)
do
cat > /redis/conf/$i/redis.conf <<EOF
port $i
cluster-enabled yes
cluster-config-file nodes_$i.conf
cluster-node-timeout 5000
appendonly yes
daemonize yes
protected-mode no
pidfile /var/run/redis_$i.pid
EOF
done
3,启动节点
vim start_all_redis.sh
#!/bin/bash
for i in $(seq 7001 7006)
do
/export/server/redis/bin/redis-server /redis/conf/$i/redis.conf
done
4,启动集群
/export/server/redis/bin/redis-cli --cluster create 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 --cluster-replicas 1
5,测试
/export/server/redis/bin/redis-cli -c -p 7001
注:一定要添加-c选项,否则redis-cli默认启动是不以集群方式启动
6,关闭
echo 7001 7002 7003 7004 7005 7006 | xargs -n1 -I{} /export/server/redis/bin/redis-cli -p {} shutdown
- -n :表示每次执行命令时传递的参数个数
- -n1:表示每次只传递 1 个参数 给后续命令
- -I :用于指定一个占位符(通常用 {}),后续命令中可以通过占位符引用输入参数
redis集群配置 (多节点
我这里使用三台服务器,node4 node3 node2
1,主机名解析配置
在每台主机的 /etc/hosts 文件中添加:
cat >/etc/hosts<<EOF
192.168.88.104 redis04 redis04.itcast.cn
192.168.88.102 redis02 redis02.itcast.cn
192.168.88.103 redis03 redis03.itcast.cn
EOF
2,Redis 集群实现
第一步:环境规划(3主3从,对外提供服务的一共是3个节点)
前提:Redis集群往往在搭建环境时必须要提前设计,而且Redis集群要求,在创建集群之前,不能有任何数据!
|----|-------------------|----------------|-----------|
| 编号 | 主机名称 | IP地址 | 角色 |
| 1 | redis02.itcast.cn | 192.168.88.102 | redis7001 |
| 2 | redis02.itcast.cn | 192.168.88.102 | redis7002 |
| 3 | redis03.itcast.cn | 192.168.88.103 | redis7003 |
| 4 | redis03.itcast.cn | 192.168.88.103 | redis7004 |
| 5 | redis04.itcast.cn | 192.168.88.104 | redis7005 |
| 6 | redis04.itcast.cn | 192.168.88.104 | redis7006 |
搭建集群前清空 = 把实例恢复到刚安装、从未运行过的纯净状态,消除旧集群信息、旧缓存数据带来的各种组网报错,只在测试环境搭建集群时使用,生产环境绝对不能执行
systemctl restart redis
systemctl status redis
redis-cli
flushall
改主机名
hostnamectl set-hostname redis02.itcast.cn && bash
hostnamectl set-hostname redis03.itcast.cn && bash
hostnamectl set-hostname redis04.itcast.cn && bash
创建配置文件
先不要跟着操作,先理解,实操跟着【目录:快速配置】实操
在node2的 /export/server/redis/conf目录中创建redis7001.conf,redis7002.conf
在node3的 /export/server/redis/conf目录中创建redis7003.conf,redis7004.conf
在node4的 /export/server/redis/conf目录中创建redis7005.conf,redis7006.conf
node2
cp /export/server/redis/conf/redis.conf /export/server/redis/conf/redis7001.conf
cp /export/server/redis/conf/redis.conf /export/server/redis/conf/redis7002.conf
sed -i '/^\s*#/d; /^\s*$/d' /export/server/redis/conf/redis7001.conf
sed -i '/^\s*#/d; /^\s*$/d' /export/server/redis/conf/redis7002.conf
cat /export/server/redis/conf/redis7001.conf
cat /export/server/redis/conf/redis7002.conf
node3
cp /export/server/redis/conf/redis.conf /export/server/redis/conf/redis7003.conf
cp /export/server/redis/conf/redis.conf /export/server/redis/conf/redis7004.conf
sed -i '/^\s*#/d; /^\s*$/d' /export/server/redis/conf/redis7003.conf
sed -i '/^\s*#/d; /^\s*$/d' /export/server/redis/conf/redis7004.conf
cat /export/server/redis/conf/redis7003.conf
cat /export/server/redis/conf/redis7004.conf
node4
cp /export/server/redis/conf/redis.conf /export/server/redis/conf/redis7005.conf
cp /export/server/redis/conf/redis.conf /export/server/redis/conf/redis7006.conf
sed -i '/^\s*#/d; /^\s*$/d' /export/server/redis/conf/redis7005.conf
sed -i '/^\s*#/d; /^\s*$/d' /export/server/redis/conf/redis7006.conf
cat /export/server/redis/conf/redis7005.conf
cat /export/server/redis/conf/redis7006.conf
所有服务器的所有Redis全部停止,删除或移除所有默认配置文件redis.conf
systemctl stop redis
systemctl disable redis
ps aux|grep redis
rm -rf /export/server/redis/conf/redis.conf
或者
mv /export/server/redis/conf/redis.conf /export/server/redis/conf/redis.conf.bak
节点配置
统一基础规则,只改差异化标识
vim /export/server/redis/conf/redis7001.conf
bind 0.0.0.0
protected-mode no
port 7001
cluster-enabled yes
cluster-config-file nodes_7001.conf
cluster-node-timeout 5000
daemonize yes
pidfile /var/run/redis_7001.pid
logfile "/export/server/redis/logs/redis.log"
appendonly yes
vim /export/server/redis/conf/redis7002.conf
bind 0.0.0.0
protected-mode no
port 7002
cluster-enabled yes
cluster-config-file nodes_7002.conf
cluster-node-timeout 5000
daemonize yes
pidfile /var/run/redis_7002.pid
logfile "/export/server/redis/logs/redis.log"
appendonly yes
7003~7006都差不多 改下里面的数字
快速配置
现在不是预计
node2 放redis7001.conf,redis7002.conf
node3 放redis7003.conf,redis7004.conf
node4 放redis7005.conf,redis7006.conf
思路:先停服务,备份 再把node2的7001.conf改成纯净配置写好,再复制到别的地方
node2操作
systemctl stop redis
systemctl disable redis
ps aux|grep redis
cp /export/server/redis/conf/redis.conf /export/server/redis/conf/redis.conf.bak
cp /export/server/redis/conf/redis.conf /export/server/redis/conf/redis7001.conf
sed -i '/^\s*#/d; /^\s*$/d' /export/server/redis/conf/redis7001.conf
cat /export/server/redis/conf/redis7001.conf
vim /export/server/redis/conf/redis7001.conf
以下行号为纯净配置后的行号
#修改
1 bind 0.0.0.0
2 protected-mode no
3 port 7001
7 daemonize yes
8 pidfile /var/run/redis_7001.pid
10 logfile "/export/server/redis/logs/redis.log"
40 appendonly yes
#尾行新加
cluster-enabled yes
cluster-config-file nodes_7001.conf
cluster-node-timeout 5000
把配置好的redis7001.conf复制到同路径下,重命名为7002
冒号进入 Vim 命令行模式,
-
%号代表文件全部行,
-
s/旧内容/新内容,把旧内容替换为新内容,
-
g为全局标记
cp /export/server/redis/conf/redis7001.conf /export/server/redis/conf/redis7002.conf
vim /export/server/redis/conf/redis7002.conf
:%s/7001/7002/g
现在node2的配置就弄完了,给node3,node4配置,先在node2操作,远程复制过去给他们,前面的域名解析在这里就用上了不是
scp /export/server/redis/conf/redis7001.conf root@redis03:/export/server/redis/conf/redis7003.conf
scp /export/server/redis/conf/redis7001.conf root@redis03:/export/server/redis/conf/redis7004.conf
scp /export/server/redis/conf/redis7001.conf root@redis04:/export/server/redis/conf/redis7005.conf
scp /export/server/redis/conf/redis7001.conf root@redis04:/export/server/redis/conf/redis7006.conf
node3操作,或者node2 ssh root@redis03 登录上去操作
vim /export/server/redis/conf/redis7003.conf
:%s/7001/7003/g
vim /export/server/redis/conf/redis7004.conf
:%s/7001/7004/g
node4操作
vim /export/server/redis/conf/redis7005.conf
:%s/7001/7005/g
vim /export/server/redis/conf/redis7006.conf
:%s/7001/7006/g
配置完成后,启动Reids6个节点
mkdir -p /export/server/redis/logs
redis-server /export/server/redis/conf/redis7001.conf
redis-server /export/server/redis/conf/redis7002.conf
redis-server /export/server/redis/conf/redis7003.conf
redis-server /export/server/redis/conf/redis7004.conf
redis-server /export/server/redis/conf/redis7005.conf
redis-server /export/server/redis/conf/redis7006.conf
ps aux|grep redis

启动与连接集群实例
node2操作
systemctl stop redis
rm -rf /export/server/redis/conf/redis.conf
redis-server /export/server/redis/conf/redis7001.conf
ps aux|grep redis
#测试链接,这个会连接失败
redis-cli
#集群连接方式
redis-cli -p 7001 -c
info
node4操作
redis-cli -h 192.168.88.102 -p 7001 -c
info
创建集群
(集群最少需要3个主节点)
最终集群架构:
- 主 1 (102:7001) ← 从 1 (102:7002)
- 主 2 (103:7003) ← 从 2 (103:7004)
- 主 3 (104:7005) ← 从 3 (104:7006)
建议在所有Linux服务器上执行优化系统的命令:
sed -i -r 's/SELINUX=[ep].*/SELINUX=disabled/g' /etc/selinux/config
setenforce 0
systemctl stop firewalld &> /dev/null
systemctl disable firewalld &> /dev/null
iptables -F
iptables -t nat -F
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
modprobe bridge
echo 1 > /proc/sys/net/ipv4/ip_forward
# echo net.ipv4.ip_forward = 1 >> /etc/sysctl.conf
sysctl -p
以下命令只需在其中一台Linux服务器上(如 redis02)执行:
redis-cli --cluster create ①主节点 ②主节点 ③主节点 ①从节点 ②从节点 ③从节点
redis-cli --cluster create \
192.168.88.102:7001 \
192.168.88.103:7003 \
192.168.88.104:7005 \
192.168.88.102:7002 \
192.168.88.103:7004 \
192.168.88.104:7006 \
--cluster-replicas 1
参数说明:
- cluster create 创建集群,后面跟Redis IP及端口号
- cluster-replicas 1 每个主节点默认都有1个从节点

常见问题:Redis集群要求Redis中不能有数据,包括appendonlydir、dump.rdb、nodes_700x.conf
解决方案:哪个节点报错,就清除哪个节点(以7001为例)
ps -ef | grep redis-server
找到7001对应的进程
kill -9 进程号
rm -rf appendonlydir
rm -rf dump.rdb
rm -rf node_7001.conf
清除完成后,重启Redis
redis-server /export/server/redis/conf/redis7001.conf
3,测试集群
格式:redis-cli -c -h 主节点IP地址 -p 7001
-c代表以集群的方式进行连接
查看集群的状态
cluster info


集群读写操作 测试
redis2操作(7001节点
创建key,能正常取值
redis-cli -c -p 7001
keys *
set redis_cluster 666
keys *
get redis_cluster

redis_cluster计算哈希槽 = 9626,该槽归属192.168.88.102:7003主节点;-c参数自动跳转客户端到目标节点,写入成功返回 OK。
redis03 操作(7003 主节点
redis-cli -c -p 7003
cluster info
keys *
get redis_cluster

redis04 操作(7004,7003 的从节点)
redis-cli -c -p 7004 -h 192.168.88.102

退出,远程连接7003主节点
redis-cli -c -p 7003 -h 192.168.88.103

redis04 操作(7006,7005 的从节点)
redis-cli -c -p 7006

从节点不会主动存储槽位数据,只读不写,直接查为空;读取时会自动跳转主节点。
7006 是从节点,执行keys *会自动跳转至对应主节点 7003,所以能查到 key。
7004也是-c上去的7006也是-c上去的 两个也都是从节点 为什么一个keys 能查到 一个查不到
- 7004 场景 客户端停留在 7004,
keys *无重定向响应,直接在空槽的从节点扫描 → 空数组。 - 7006 场景
keys *触发 MOVED 重定向,-c自动切换到 7003 主节点执行扫描 → 查到 key,执行完切回 7006 提示符。补充:为什么 7004 不会触发重定向?
Redis 集群规则:
- 单 key 命令(
get/set):能精准计算槽位,一定会返回 MOVED; - 全量扫描命令(
keys *):服务器不会主动推送全局重定向,仅当客户端主动遍历所有槽时才跳转;两次操作时集群内部路由状态微小差异,导致一次触发跳转、一次未触发,属于正常现象。
4,关闭集群
以下命令在任意一个节点执行都可以
redis-cli -c -h 192.168.88.102 -p 7001 shutdown # redis02
redis-cli -c -h 192.168.88.102 -p 7002 shutdown # redis02
redis-cli -c -h 192.168.88.103 -p 7003 shutdown # redis03
redis-cli -c -h 192.168.88.103 -p 7004 shutdown # redis03
redis-cli -c -h 192.168.88.104 -p 7005 shutdown # redis04
redis-cli -c -h 192.168.88.104 -p 7006 shutdown # redis04
ps aux|grep redis
5,启动集群
启动步骤非常简单,只需要把每台服务器的各个节点依次启动即可。
redis-server /export/server/redis/conf/redis7001.conf # redis02
redis-server /export/server/redis/conf/redis7002.conf # redis02
redis-server /export/server/redis/conf/redis7003.conf # redis03
redis-server /export/server/redis/conf/redis7004.conf # redis03
redis-server /export/server/redis/conf/redis7005.conf # redis04
redis-server /export/server/redis/conf/redis7006.conf # redis04
ps aux|grep redis
redis集群核心技术点
16384个哈希槽
哈希槽就是 Redis 集群的数据分区单位
简单理解:
-
Redis 集群把整个数据空间分成 16384 个固定的小格子(每个格子就是一个哈希槽)
-
每个键通过计算
CRC16(key) % 16384得到一个 0-16383 的数字,决定它进入哪个格子 -
集群中的每个主节点负责管理一部分格子(比如节点A管0-5000号格子,节点B管5001-10000号格子...)
类比理解:
想象一个大型图书馆:
-
16384 个书柜 = 哈希槽
-
每本书(键)根据书名计算放到哪个书柜
-
每个图书管理员(节点)负责管理几个书柜
-
读者要找书时,先算书在哪个书柜,然后找负责那个书柜的管理员
为什么是16384?
-
足够多:确保数据能均匀分布到各个节点
-
不太多:节点间同步槽分配信息时占用合理带宽
-
用 14 位二进制就能表示(2^14=16384)
关键点:客户端访问时,节点会快速算出键在哪个槽,如果这个槽不由自己管理,就告诉客户端"去找XXX节点"。
Redis 集群采用去中心化设计,所有节点地位平等,相互连接并同步集群状态。客户端连接任一节点即可访问整个集群的数据。
集群使用 16384 个哈希槽 进行数据分片。每个键通过 CRC16(key) % 16384 计算所属槽位,集群将槽位分配给各主节点管理。
例如三主集群中:
-
节点A管理槽 0-5460
-
节点B管理槽 5461-10922
-
节点C管理槽 10923-16383
当客户端操作键时,节点会计算键的槽位:
-
若槽位归自己管理,直接执行命令
-
否则返回重定向信息,引导客户端访问正确节点
每个主节点可配置从节点,主节点负责写,数据同步到从节点。主节点故障时,对应从节点自动接替,保证高可用。
集群要求至少3 个主节点 **,**且存活主节点数必须超过半数,否则集群将停止服务。
例如:
-
3 主 3 从:正常
-
1 主 3 从:主节点故障后无法选举新主,集群失效
需要注意的是:必须要3个或以上的主节点,否则在创建集群时会失败,并且当存活的主节点数小于总节点数的一半时,整个集群就无法提供服务了。
3主 3从 = 正常
1主 3从 = 1 < (1+3)/2 = 2 = 集群失效
简单理解:Redis 集群就是一个大仓库,这个仓库中为了方便数据存储,拆分为16384 slot哈希槽。
因为写只和 master 主节点相关,所以16384要被3个 master 拆分
这段不是实际代码,而是示意图,Redis 自动平均分配槽。
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
写入一条记录如name:小凑咪,写入到哪个槽中?
在Redis设计过程中,引入了一个crc16函数,用于针对key求解,结果返回一个数字 => crc16(name) = 5000
具体数据写入到哪里 => 哈希求余 => crc16(name) % 3 = 5000 % 3 = 1666(存放槽位置)
添加新节点
redis-cli --cluster add-node 192.168.88.114:7007 192.168.88.102:7001
- 功能:向已有 Redis 集群新增节点
- 第一个 IP 端口:新节点
192.168.88.114:7007 - 第二个 IP 端口:集群内任意一个已有节点(用来接入集群网络)
- 默认新增后是空主节点 ,没有分配任何哈希槽,需要手动执行
reshard迁移槽位才能存数据。
修复未完成的槽迁移(若存在)
# 修复 192.168.88.102:7003 的槽 5798
redis-cli -h 192.168.88.103 -p 7003 cluster setslot 5798 stable
# 修复 192.168.88.102:7004 的槽 741 和 3680
redis-cli -h 192.168.88.103 -p 7004 cluster setslot 741 stable
redis-cli -h 192.168.88.103 -p 7004 cluster setslot 3680 stable
cluster setslot X stable 作用:强制将处于importing/migrating 迁移中间态 的哈希槽,标记为稳定正常状态,清除open slots警告。你三条命令分别修复 3 个残留迁移槽:
- 192.168.88.103:7003 槽 5798
- 192.168.88.103:7004 槽 741
- 192.168.88.103:7004 槽 3680
底层原理
之前迁移中断,槽停留在导入 / 迁出中间状态,集群判定为open slots告警;stable 标识代表:该槽归属节点固定、迁移流程结束,集群恢复正常读写路由。
- 该命令是强制修复 ,仅适用于迁移中断、槽内无数据冲突 场景;如果同一槽在两个节点同时存在数据,直接执行会造成数据丢失,优先用
--cluster fix自动修复。 - 修复完成后建议写入测试 key,验证 3 个槽位(741、3680、5798)读写正常
迁移槽到新主节点(自动平衡)
redis-cli --cluster rebalance 192.168.88.102:7001 --cluster-threshold 1
--cluster rebalance:集群槽位自动均衡工具,自动统计所有主节点持有哈希槽数量,自动迁移槽位,让每台主节点槽数差距控制在阈值内,实现均分。
192.168.88.102:7001:集群内任意节点,用来连接整个集群--cluster-threshold 1:均衡阈值(单位 %),代表**主节点之间槽位数量差值不能超过总槽位的 1%**总槽 16384,1%≈164 个槽,任意两台主节点槽数相差≥164,就会自动触发迁移;阈值越小均衡越严格。
添加从节点 192.168.88.114:7008
# 获取新主节点 ID
NODE_ID_7007=$(redis-cli -h 192.168.88.114 -p 7007 cluster nodes | grep myself | awk '{print $1}')
# 添加从节点并绑定到主节点
redis-cli --cluster add-node 192.168.88.114:7008 192.168.88.102:7001 \
--cluster-slave \
--cluster-master-id $NODE_ID_7007
验证集群状态
redis-cli --cluster check 192.168.88.102:7001