Redis小册
安装 Redis#
判断是否安装 GCC 环境#
以下命令均可检查是否安装 GCC
gcc --version
# or
gcc -v
whereis gcc
# or
whereis g++
如需安装, 执行以下命令即可
yum -y install gcc-c++
安装#
下载 redis, 联网情况下可以 wget 命令直接下载, 此方式默认下载至当前用户的家目录, 也可以下载好安装包上传至 linux
wget https://download.redis.io/releases/redis-7.0.0.tar.gz
/opt 目录下解压 redis, 注意文件夹读写权限问题。tar 命令 – 压缩和解压缩文件 – Linux 命令大全(手册) (linuxcool.com)
sudo tar -zxvf redis-7.0.0.tar.gz -C /opt/
进入安装目录
cd /opt/redis-7.0.0/
执行 make 命令, 出现下图红框内容代表安装成功, 安装需要能够访问外网, 以及有足够的权限, 不知道为什么 sudo 的都不行, 最后直接使用的 root 用户进行的编译
make && make install
修改配置#
进入默认安装路径
cd /usr/local/bin
# 列出所有文件
ll
将默认的 redis.conf 拷贝到我们自己定义的路径下, 以防后续修改坏了的话还有备份。cp 命令 – 复制文件或目录 – Linux 命令大全(手册) (linuxcool.com)
# 创建文件夹
sudo mkdir -p /myredis/conf
# 拷贝文件到自定义文件夹下
sudo cp /opt/redis-7.0.0/redis.conf /myredis/conf/redis7.conf
配置 redis 的配置文件, 使用 Vim 打开 /myredis/conf/redis7.conf 设置以下参数, 配置文件修改后, 需要重启 redis 服务才会生效
# 以后台服务的模式启动
daemonize yes
# 关闭redis的保护模式
protected-mode no
# 注释掉bind参数
# bind 127.0.0.1 -::1
# 添加redis密码
requirepass 1234
参数说明
启动服务#
启动 redis 服务,
/usr/local/bin
目录下运行redis-server
,启用/myredis/conf/redis7.conf 文件, 服务启动是没有输出消息的
redis-server /myredis/conf/redis7.conf
ps -ef|grep redis|grep -v grep
链接服务#
链接
redis-cli
是 redis 的命令行工具, 有很多参数可以redis-cli --help
去了解, 链接成功如下图所示
redis-cli -a 1234 -p 6379
测试服务是否能正常使用
ping
退出客户端#
退出客户端
quit
停止服务#
redis-cli 内部执行
shutdown
或者redis-cli -a 1234 shutdown
。
# 未进入 redis-cli
redis-cli -a 1234 shutdown
# 进入 redis-cli内部后
shutdown
Redis 十大数据类型#
Key(键)#
Redis 键命令用于管理 redis 的键。
常用命令:
DEL key
该命令用于在 key 存在时删除 keyDUMP key
序列化给定 key ,并返回被序列化的值。使用DUMP
命令所获取的序列化数据可以用RESTORE
命令来恢复EXISTS key
判断键是否存在,存在返回 1,不存在返回 0EXPIRE key seconds
设置键的过期秒数EXPIREAT key timestamp
作用和EXPIRE
一样,不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳,可以指定到期时间PEXPIRE key seconds
设置键的过期毫秒数PEXPIREAT key timestamp
作用和EXPIRE
一样,不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳,可以指定到期时间KEYS partten
查找所有符合给定模式 ( pattern) 的 key 全部就是 key 正式环境禁用,使用 scan 命令代替。MOVE key db
移动 key 到指定的库PERSIST key
移除 key 的过期时间,能让数据一致保持TTL key
看 key 剩余时间还有多少秒PTTL key
看 key 剩余时间还有多少毫秒RANDOMKEY
随机返回库中的一个 keyRENAME key newkey
修改 key 的名称RENAMENX key newkey
修改 key 的名称,当新 key 不存在时才生效TYPE key
返回 key 的类型SCAN cursor [MATCH pattern] [COUNT count]
迭代数据库中的数据库键。返回值有下一次用于迭代的游标,COUNT count
,是一种提示 (hint),如Count 10
表示期望返回 10 个 key,大多数情况是有效的,但不一定 100% 返回 10 个 key。
String(字符串)#
String 是 Redis 中最简单同时也是最常用的数据类型。它是一个二进制安全的字符串,可以存储任何类型的数据,如字符串、整数、浮点数等。
SET key value
设置指定key
的值。GET key
获取指定key
的值。GETRANGE key start end
返回key
中字符串值的子字符串。GETSET key value
将给定key
的值设为value
,并返回key
的旧值。GETBIT key offset
对key
所储存的字符串值,获取指定偏移量上的位(bit)。MGET key1 [key2..]
获取所有(一个或多个)给定key
的值。SETBIT key offset value
对key
所储存的字符串值,设置或清除指定偏移量上的位(bit)。SETEX key seconds value
将值value
关联到key
,并将key
的过期时间设为seconds
(以秒为单位)。SETNX key value
只有在key
不存在时设置key
的值。SETRANGE key offset value
用value
参数覆写给定key
所储存的字符串值,从偏移量offset
开始。STRLEN key
返回key
所储存的字符串值的长度。MSET key value [key value ...]
同时设置一个或多个key-value
对。MSETNX key value [key value ...]
同时设置一个或多个key-value
对,当且仅当所有给定key
都不存在。PSETEX key milliseconds value
同SETEX
命令相似,但它以毫秒为单位设置key
的生存时间。INCR key
将key
中储存的数字值增一。INCRBY key increment
将key
所储存的值加上给定的增量值(increment)。INCRBYFLOAT key increment
将key
所储存的值加上给定的浮点增量值(increment)。DECR key
将key
中储存的数字值减一。DECRBY key decrement
将key
所储存的值减去给定的减量值(decrement)。APPEND key value
如果key
已经存在并且是一个字符串,APPEND
命令将指定的value
追加到该key
原来值的末尾。
常用命令:SET key value
:设置 key 的值。GET key
:获取 key 的值。INCR key
:将 key 中的数字值增 1。DECR key
:将 key 中的数字值减 1。STRLEN key
:获取 key 的值的长度。EXISTS key
:判断 key 是否存在。DEL key
:删除 key 及其值。EXPIRE key seconds
:为 key 设置过期时间(秒)。TTL key
:查看 key 的剩余生存时间(秒)。
List(列表)#
Redis 中的 List 是一个双向链表,支持快速的插入和删除操作。它可以作为队列或栈来使用。
常用命令:
LPUSH key value
:在列表的左端插入一个或多个值。RPUSH key value
:在列表的右端插入一个或多个值。LPOP key
:移除并返回列表的左端元素。RPOP key
:移除并返回列表的右端元素。LRANGE key start stop
:获取列表指定范围内的元素。LLEN key
:获取列表的长度。
Set(集合)#
Redis 中的 Set 是一个无序的字符串集合,它不允许有重复的元素。Set 提供了集合操作,如添加、删除、合并等。
常用命令:
SADD key member
:向集合添加一个或多个成员。SREM key member
:从集合移除一个或多个成员。SMEMBERS key
:返回集合中的所有成员。SISMEMBER key member
:判断成员是否在集合中。SCARD key
:获取集合的成员数量。
Hash(哈希)#
Redis 中的 Hash 是一个键值对的集合,类似于一个对象。Hash 中的每个字段(Field)和值(Value)都是字符串。
常用命令:
HSET key field value
:为哈希表 key 中的字段 field 设置值 value。HGET key field
:获取哈希表 key 中字段 field 的值。HMSET key field1 value1 field2 value2
:同时设置哈希表 key 的多个字段的值。HMGET key field1 field2
:同时获取哈希表 key 的多个字段的值。HGETALL key
:获取哈希表 key 中的所有字段和值。HDEL key field
:删除哈希表 key 中的字段 field。
Redis 持久化#
总体介绍#
redis 持久化 (也叫备份和恢复) 是指将数据写入持久存储,例如固态磁盘 (SSD)。Redis 提供了一系列持久性选项。这些包括:
- RDB(Redis 数据库):RDB 持久性按指定的时间间隔执行数据集的时间点快照。
- AOF(仅追加文件):AOF 持久性记录服务器接收的每个写入操作。然后,可以在服务器启动时再次重播这些操作,从而重建原始数据集。使用与 Redis 协议本身相同的格式记录命令。
- RDB + AOF:您还可以在同一实例中组合 AOF 和 RDB。
- 无持久性:您可以完全禁用持久性。这有时在缓存时使用。
持久化的两种方式#
下图形象的描述了持久化的两种方式, 我们可以对其有个大概印象, 后续再回头看这张图就会有更多的理解
RDB#
是什么, 能干嘛
在指定的时间间隔内将内存中的数据集快照写入磁盘, 也就是行话讲的 Snapshot 内存快照,它恢复时再将硬盘快照文件直接读回到内存里
怎么用
配置文件新旧版本说明
讲在前头, redis6 和 7 的 RDB 配置的主要在于同步频次上, 以及在 redis7 中如果不进行显示配置, 则会按照默认的同步快照的批量进行.
关于同步快照的设置格式为 :
save 距离最近一次同步的秒数 键改变的数量
redis6.0.16 之前的配置文件
redis2.0 到 redis7 的配置
自动触发快照保存
修改同步频次本文档使用的是 redis7.0.0 进行操作, 因此为了更好的观察现象, 我们修改同步的配置为 save 20 2
,也就是距离上传同步大于 20 秒, 并且有至少两次键值改变才触发同步
修改快照保存路径 快照保存路径默认为当前 redis 所工作目录下, 我们可以修改配置文件中的 dir 参数自定义快照保存路径, 需要注意的是, 文件夹需要提前存在
修改保存文件的名称 文件默认保存的文件名为 dump.rdb
单机情况下肯定是无须修改, 但如果是集群环境下, 还是最好对文件名做一定的调整, 方便区分是那台服务的备份文件, 我们本次修改为 dump6379.rdb
踩坑记录
问题现象: 当触发了自动备份时, 会爆出下图中的错误
排查过程:
-
修改配置文件, 自定义日志的输出路径
logfile "/myredis/logs/redis.log"
-
按照 GPT 的回答, 是日志权限不足, 一查还真是, 除了当前用户,其他人没有写的权限
-
修改日志的权限
sudo chmod 666 /myredis/logs/redis.log
-
服务启动, 再次进行触发备份的操作, 步骤 1 的问题复现
总结 ① 与 redis 服务交互的文件需要给予足够的权限 ② 触发自动备份的时机除了上述满足条件外, 还有主动 shutdown
服务时也会进行一次备份
手动触发快照保存
Redis 提供了两个命令来生成 RDB 文件分别是
save
和bgsave
save
在主程序中执行会阻塞当前 redis 服务器,直到持久化工作完成执行 save 命令期间,Redis 不能处理其他命令,线上禁止使用basve
Redis 会在后台异步进行快照操作,不阻塞快照同时还可以响应客户端请求,该触发方式会 fork 一个子进程由子进程复制持久化过程。讲点大白话就是,执行bsave
之后, 会fork
复刻一个当前的进程, 专门用来进行数据集的保存。原来的进程继续用来接受处理 redis 的线上请求
Lastsave
查看最后一次快照的保存时间
lastsave
优势及注意点
优势
- RDB 是 Redis 数据的一个非常紧凑的单文件时间点表示。RDB 文件非常适合备份。例如,您可能希望在最近的 24 小时内每小时归档一次 RDB 文件,并在 30 天内每天保存一个 RDB 快照。这使您可以在发生灾难时轻松恢复不同版本的数据集。
- RDB 非常适合灾难恢复,它是一个可以传输到远程数据中心或 AmazonS3(可能已加密)的压缩文件。
- RDB 最大限度地提高了 Redis 的性能,因为 Redis 父进程为了持久化而需要做的唯一工作就是派生一个将完成所有其余工作的子进程。父进程永远不会执行磁盘 I/O 或类似操作。
- 与 AOF 相比,RDB 允许使用大数据集更快地重启。
- 在副本上,RDB 支持重启和故障转移后的部分重新同步。
注意点
- 适合大规模的数据恢复;
- 按照业务定时备份;
- 对数据完整性和一至 - 致性要求不高;
- RDB 文件在内存中的加载速度要比 AOF 快得多
劣势及注意点
劣势
-
如果您需要在 Redis 停止工作时(例如断电后)将数据丢失的可能性降到最低,那么 RDB 并不好。您可以配置生成 RDB 的不同保存点(例如,在对数据集至少 5 分钟和 100 次写入之后,您可以有多个保存点)。但是,您通常会每五分钟或更长时间创建一次 RDB 快照,因此,如果 Redis 由于任何原因在没有正确关闭的情况下停止工作,您应该准备好丢失最新分钟的数据。
-
RDB 需要经常 fork() 以便使用子进程在磁盘上持久化。如果数据集很大,fork() 可能会很耗时,并且如果数据集很大并且 CPU 性能不是很好,可能会导致 Redis 停止为客户端服务几毫秒甚至一秒钟。AOF 也需要 fork() 但频率较低,您可以调整要重写日志的频率,而不需要对持久性进行任何权衡。
注意点
-
在一定间隔时间做一次备份,所以如果 redis 意外 down 掉的话,就会丢失从当前至最近一次快照期间的数据,快照之间的数据会丢失;
-
内存数据的全量同步,如果数据量太大会导致 I/O 严重影响服务器性能;
-
RDB 依赖于主进程的 fork,在更大的数据集中,这可能会导致服务请求的瞬间延迟。fork 的时候内存中的数据被克隆了一份,大致 2 倍的膨胀性,需要考虑
模拟数据丢失
录入数据, 正常触发保存. 保存后, 设置 a5 的值, 并且此次数据录入并未满足自动保存条件
模拟意外的发生
# 查询进程号
ps aux |grep {redis}
# 杀死进程
kill -9 {进程号}
重启服务, 并查询 a3, a4, a5 的值, 可以发现 最后一次保存时间点前的数据 a3 和 a4 能正常获取, 而 a5 的数据发生了丢失
检查修复 Dump 文件
以下指令用来修复备份文件, 能修复就修复了, 修复不了基本上就寄了
redis-check-rdb /myredis/dumpfiles/dump6379.rdb
触发快照保存的情况总结
- 符合配置文件中快照配置条件
- 手动
save
/bsave
命令执行 - 执行
flushall
/flushdb
命令也会触发备份, 不过会清空备份文件, 没意义 - 执行
shutdown
且没开启 AOF 持久化 - 主从复制, 主节点自动触发 (当前还未验证,后面验证)
禁用快照 (自动保存)
使用命令动态临时的关闭快照
redis-cli config set save ''
或者直接修改配置文件中的
save
参数为''
RDB 配置项详解
也就是配置文件的件 SNAPSHOTTING 模块
stop-writes-on-bgsave-error
默认 yes,如果配置成 no,表示你不在乎数据不一致或者有其他的手段发现和控制这种不一致,那么在快照写入失败时,也能确保 redis 继续接受新的写请求|rdbcompression
默认 yes, 对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis 会采用 LZF 算法进行压缩。如果你不想消耗 CPU 来进行压缩的话,可以设置为关闭此功能rdbchecksum
默认 yes 在存储快照后,还可以让 redis 使用 CRC64 算法来进行数据校验,但是这样做会增加大约 10% 的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能rdb-del-sync-files
在没有持久性的情况下删除复制中使用的 RDB 文件启用。默认情况下 no,此选项是禁用的save 20 2
dbfilename dump6379.rdb
dir /myredis/dumpfiles
一图总结
AOF#
是什么, 能干嘛
以日志的形式来记录每个
增删改
操作,将 Redis 执行过的所有写指令记录下来 (读操作不记录),只许追加文件但不可以改写文件,redis 启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
AOF 持久化的工作流程图
- Client 作为命令的来源,会有多个源头以及源源不断的请求命令。
- 在这些命令到达 Redis Server 以后并不是直接写入 AOF 文件,会将其这些命令先放入 AOF 缓存中进行保存。这里的 AOF 缓冲区实际上是内存中的一片区域,存在的目的是当这些命令达到一定量以后再写入磁盘,避免频繁的磁盘 IO 操作。
- AOF 缓冲会根据 AOF 缓冲区同步文件的三种写回策略将命令写入磁盘上的 AOF 文件。
- 随着写入 AOF 内容的增加为避免文件膨胀,会根据规则进行命令的合并 (又称AOF 重写),从而起到 AOF 文件压缩的目的。
- 当 Redis Server 服务器重启的时候会从 AOF 文件载入数据。
AOF 三种同步的写回策略
三种写回策略
always
可以理解为, 客户端执行写一个指令, 离开就往日志文件中追加一个everysec
每秒写回,每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔 1 秒把缓冲区中的内容写入磁盘no
操作系统控制的写回,每个写命令执行完,只是先把日志写到 AOF 文件的内存缓中区,由操作系统决定何时将缓中区内容写回磁盘
各种策略优缺点
配置项 | 写回时机 | 优点 | 缺点 |
---|---|---|---|
Always | 同步写回 | 可靠性高,数据基本不丢失 | 每个写命令都要落盘,性能影响较大 |
Everysec | 每秒写回 | 性能适中 | 岩机时丢失 1 秒内的数据 |
No | 操作系统控制的写回 | 性能好 | 岩机时丢失数据较多 |
怎么用
配置文件说明
开启 AOF
AOF 默认是关闭的, 开起需要将配置文件中的
appendonly
参数设置为yes
设置回写策略
使用默认的写回策略, 默认写回即可
备份存放路径
redis6 中的 AOF 保存文件的位置和 RDB 保存文件的位置一样,都是通过 redis.conf 配置文件的 dir 配置, 但是在 redis 中, 增加了
appenddirname
参数, 用来指定 AOF 的日志存放文件夹。因此当前 redis7 中的 AOF 备份的实际存放路径为dir
+appenddirname
。本次修改 AOF 保存文件的路径为/myredis/backupfiles/appendonlydir
备份文件名
Redis6 版本中的备份文件名由
appendfilename
参数控制, 是单独一个文件。但是由于 AOF 是一个一个指令集的叠加, 导致文件会逐渐膨胀, 过大的文件会导致影响写入指令的 IO 性能。 因此在 Redis7 中, 使用了一组文件来作为 AOF 的备份文件, 分别是基础文件base
,增量文件incr
,清单文件manifest
。而appendfilename
参数所配置的为该组文件的文件名后缀, 此次我们使用默认的不做变更。
生成 AOF 备份文件
按需求配置完参数, 重启 reds 服务, nice, 启动失败
额, 这波属实是不长记性了, 处理一下小意外, 无伤大雅
# 创建备份文件的路径
sudo mkdir /myredis/backupfiles
# 给备份文件夹修改写入权限
sudo chmod 777 /myredis/backupfiles
正常写入数据
查看生成的备份文件
重启 redis, 为避免是从由于 RDB 文件中加载的数据, 我们将 RDB 备份文件重命名
sudo mv dump6379.rdb dump6379.rdb.bak
重启服务, 获取数据, 该说不说, 兄弟们好使
作死执行一手
flushall
, 查看一下备份文件的情况
重启 Redis, 查询数据发现, 没有值
命令不是逐条写入, 写的这么清楚, 我是不是能改一下呢, 删掉
flushall
来恢复数据呢? 说干就干, 不过干之前备份文件夹。本来还想记录踩坑经过呢, 没想到一把梭哈,我真是张飞吃豆芽, 粗中有细啊.
异常恢复 AOF 备份文件
AOF 备份文件可能会由于网络断链, 服务进程被杀掉等情况导致命令还未完全写入备份文件导致的文件损坏, 这里我们乱写一通模拟坏掉的文件
备份文件损坏后, 我们发现连服务都起不来
修复备份文件, 记住检查指令中一定要加上
--fix
参数, 是否继续选y
就行了, 如下图操作所示, 好使。
redis-check-aof --fix /myredis/backupfiles/appendonlydir/appendonly.aof.1.incr.aof
优劣势
- 优点: 更好的保护数据不丢失、性能高、可做紧急恢复
- 缺点:① 相同数据集的数据而言 aof 文件要远大于 rdb 文件,恢复速度慢于 rdb; ②aof 运行效率要慢于 rdb,每秒同步策略效率较好,不同步效率和 rdb 相同
AOF 重写机制
是什么
由于 AOF 持久化是 Redis 不断将写命令记录到 AOF 文件中,随着 Redis 不断的进行,AOF 的文件会越来越大,文件越大,占用服务器内存越大以及 AOF 恢复要求时间越长。为了解决这个问题,Redis 新增了重写机制,当 AOF 文件的大小超过所设定的峰值时,Redis 就会自动启动 AOF 文件的内容压缩,只保留可以恢复数据的最小指令集或者可以手动使用命令
bgrewriteaof
来重写。启动 AOF 文件的内容压缩,只保留可以恢复数据的最小指令集
触发机制
触发机制分为两种情况, 分别是自动触发和手动触发
-
自动触发: 需要同时满足下图中的两个条件,即
auto-aof-rewrite-percentage
本次 AOF 文件大小达到上次 AOF 文件的百分之多少;auto-aof-rewrite-min-size
重写时需要满足的文件大小至少要满足多少。举个 : redis7 中默认参数分别是auto-aof-rewrite-percentage 100
,auto-aof-rewrite-min-size 64mb
也就意味着, 当本次的 AOF 的大小是之前 AOF 的百分之百并且本次文件大小不小于 64Mb, 才会触发自动重写机制 -
手动触发: 客户端向服务器发送
bgrewriteaof
命令
案例演示 (仅演示自动触发)
前期配置文件修改
- 开启 AOF :
appendonly yes
默认是 no, 设置为 yes 即可开启 - 重写峰值:
auto-aof-rewrite-min-size 1kb
默认的重写峰值是 64mb 太大了, 不好测试, 我们修改为 1kb - 关闭混合持久化:
aof-use-rdb-preamble no
默认是 yes, 我们为了单纯的测 AOF, 所以关掉与 AOF 的混合持久化 - 删除原来的 aof 与 rdb 文件, 避免干扰项
启动服务, 执行写入数据的指令, 当我执行了 16 次
set k1 1111111111111111111111111111111
之后, 增量 AOF 文件的大小来到了 967kb
此时我们查看一下
cat appendonly.aof.1.incr.aof
文件的内容, 不出以外, 清一色的如下指令, 如下图所示
趁此间隙, 解释一下这些指令集怎么看吧
# *{该条指令包含几个参数}
,# ${该参数的长度}
, 也就是说下面这个指令集的意思是包含三个参数, 第一个参数的长度是 3, 值是 set , 第二个参数的长度是 2, 值是 k1, 后面的规则相同.......
*3
$3
set
$2
k1
$31
1111111111111111111111111111111
继续写入数据, 触发重写
查看 apf 备份文件夹下的文件情况, 我们发现 incr 文件没有继续增加大小, 反而是 base 文件变大了, 并且文件的后缀均变成了 2
我们先看一下 base 文件里新增的内容, 根据刚刚学习的指令集, 我们发现 base 文件中只存了我们当前最新的数据, 并没有保存以前输入的一大堆 set k1 111111
再来看一下 incre 文件, 第一次查看时, 没有内容, 第二次查看时又有内容了。为什么呢?因为第一次查看查看时,我发现没有数据, 所以我又执行了一条指令。由此我们可以断定,
set k1 ababab......
触发重写后, 会将当前内存里的数据写入 base 文件, 后续的指令写入到 incr 文件中
结论: 也就是说 AOF 文件重写并不是对原文件进行重新整理,而是直接读取服务器现有的键值对,然后用一条命令去代替之前记个键值对的多条命令,生成一个新的文件后去替换原来的 AOF 文件。AOF 文件重写触发机制:通过 redis.conf 配置文件中的
auto-aof-rewrite-percentage
:默认值为 100,以及auto-aof-rewrite-min-size
:64mb 配置,也就是说默认 Redis 会记录上次重写时的 AOF 大小,默认配置是当 AOF 文件大小是上次 rewrite 后大小的一倍且文件大于 64M 时触发
重写原理
AOF 的重写原理可以参考以下链接 Redis7.0 以后 AOF 底层原理变更图解-腾讯云开发者社区-腾讯云 (tencent.com)。
- 在重写开始前,redis 会创建一个“重写子进程”,这个子进程会读取现有的 AOF 文件,并将其包含的指令进行分析压缩并写入到一个临时文件中。
- 与此同时,主进程会将新接收到的写指令一边累积到内存缓冲区中,一边继续写入到原有的 AOF 文件中,这样做是保证原有的 AOF 文件的可用性,避免在重写过程中出现意外。
- 当“重写子进程 " 完成重写工作后,它会给父进程发一个信号,父进程收到信号后就会将内存中缓存的写指令追加到新 AOF 文件中
- 当追加结束后,redis 就会用新 AOF 文件来代替 I 旧 AOF 文件,之后再有新的写指令,就都会追加到新的 AOF 文件中
- 重写 aof 文件的操作,并没有读取旧的 aof 文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的 aof 文件,这点和快照有点类似
AOF 配置项详解
也就是配置文件中的 APPEND_ONLY_MODLE 模块
appendonly
是否开启 AOF, 默认 noappendfilename
AOF 系列文件的前缀名appendfsync
三种同步策略 everysec/always/nono-appendfsync-on-rewrite
aof 重写期间是否同步, 默认值 noauto-aof-rewrite-percentage
重写触发百分比auto-aof-rewrite-min-size
重写触发文件大小的最小值
一图总结
RDB+AOF 混合持久化#
官网建议#
关于 RDB 和 AOF 这两种持久化模式, 我们该如何选择, 官网是这样说的 Redis persistence | Redis, 建议我们两者均开启
RDB
Vs AOF
#
能否共存?
不用看了, 两者肯定能共存, 前面测试 AOF 的时候, rdb 备份文件也生成了
两者同时存在时的优先级谁高?
优先级是 AOF 的高, 主要原因在于 AOF 备份的实时性太搞了, 在开启
always
的写回策略时, 能保证数据的百分百一直, 所以数据恢复以及加载时, 肯定会以 AOF 为主
数据恢复的顺序和加载流程
在同时开启 rdb 和 aof 持久化时,重启时只会加载 aof 文件,不会加载 rdb 文件
如何选择#
两种持久化方式的特点
- RDB 持久化方式能够在指定的时间间隔能对你的数据进行快照存储
- AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF 命令以 redis 协议追加保存每次写的操作到文件末尾
推荐同时开启两种持久化方式, 虽然两者同时开启是, AOF 的优先级总是高于 RDB, 但是, 由于 AOF 文件总是在一直变化, 所以不易备份, 应该留存 RDB 文件用作备份, 分机备份
RDB
+ AOF
混合模式#
先说好处, 这种方式结合了 RDB 和 AOF 的优点,既能快速加载又能避免丢失过多的数据。强烈推荐
开启混合模式
之前测试 AOF 重写的时候关闭了, 这次再打开
写入数据, 触发重写
我们可以看到, 在开启混合模式之后, redis 在压缩指令集的时候, 并没有将内存快照处理成 base.aof 文件, 而是处理成了 RDB 文件
小结
使用 RDB 进行快照存储,然后使用 AOF 持久化记录所有的写操作,当重写策略满足或手动触发重写的时候,将最新的数据存储为新的 RDB 记录。这样的话,重启服务的时候会从 RDB 和 AOF 两部分恢复数据,既保证了数据完整性,又提高了恢复数据的性能
纯缓存模式#
关机就关机, 停服务就停服务, 主打一个对数据不关心, 强烈不推荐, 多大的并发啊, Redis 都支持不了你, 处理不了就加设备.....
至于如何开启, 把 AOF 和 RDB 都关了就算开启
Redis 事务#
是什么#
关于 Redis 的事务, 官网上是这样介绍的 Transactions | Redis, Redis Transactions allow the execution of a group of commands in a single step, they are centered around the commands
MULTI
,EXEC
,DISCARD
andWATCH
。讲人话就是可以一次执行多个命令,事务的本质是一组命令的集合。一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞
能干嘛#
开启 一个队列中,一次性、顺序性、排他性的执行一系列命令, 不允许其他指令加塞, 大致原理参照下图
Redis 事务 VS 数据库事务#
数据库事务及特性#
数据库事务是一组数据库操作,它们在数据库中被视为一个单一的工作单元,要么全部执行,要么全部不执行。事务是确保数据库数据一致性和完整性的一种机制。在事务的执行过程中,如果发生错误,所有已执行的操作将被撤销,数据库回滚到事务开始之前的状态。只有当所有操作都成功完成时,事务才会被提交,数据库保存更新后的状态。
这里多提一嘴, 数据库事务具有以下四个主要特性,通常被称为 ACID 特性:
-
原子性(Atomicity): 事务是一个原子操作,要么全部执行成功,要么全部失败回滚,不存在部分执行的情况。如果事务中的任何一个操作失败,整个事务将被撤销,数据库回到事务开始之前的状态。
-
一致性(Consistency): 在事务开始之前和事务结束后,数据库的完整性约束应该保持一致。这意味着事务执行后,数据库应该处于一个合法的状态,不违反任何完整性规则。
-
隔离性(Isolation): 事务的执行应该是相互隔离的,即一个事务的执行不应该影响其他事务的执行。隔离性通常通过并发控制机制来实现,以防止多个事务之间的干扰。
-
持久性(Durability): 一旦事务提交,其修改将永久保存在数据库中,即使系统发生故障或重启,事务的结果也不会丢失。
Redis 的特性#
特性 | 解释 |
---|---|
单独的隔离操作 | Redis 的事务仅仅是保证事务里的操作会被连续独占的执行,redis 命令执行是单线程架构,在执行完事务内所有指令前是不可能再去同时执行其他客户端的请求的 |
没有隔离级别的概念 | 因为事务提交前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”这种问题了 |
不保证原子性 | Redis 的事务不保证原子性,也就是不保证所有指令同时成功或同时失败,只有决定是否开始执行全部指令的能力,没有执行到一半进行回滚的能力 |
排它性 | Redis 会保证一个事务内的命令依次执行,而不会被其它命令插入 (这个感觉和第一个特性的区别不大) |
怎么用#
常用指令#
命令 | 描述 |
---|---|
MULTI | 开启一个事务 |
EXEC | 执行所有事务内的命令 |
DISCARD | 取消事务,放弃执行事务块内的所有命令 |
WATCH key [key ...] | 监视一个或多个键,如果在事务执行之前这些键被修改,则事务将被打断 |
UNWATCH | 取消所有对所有键的监视 |
正常执行#
multi
指令输入后, 将需要执行的指令依次放入队列, 但并不执行, 返回QUEUED
代表放入成功, 当输入 exec 时, 一次性执行完前面的指令, 并返回对应的结果。
# 开启事务
MULTI
# ......输入语句
# 执行事务
EXEC
正常执行大体流程示意图
放弃事务#
multi
指令输入后, 将需要执行的指令依次放入队列, 但并不执行, 返回QUEUED
代表放入成功, 当输入discard
时,取消执行前面的指令, 并结束事务
# 开启事务
MULTI
# ......输入语句
# 放弃执行
DISCARD
事务放弃执行的大体流程图如下
全体连坐#
该种情况是指在开启事务后部分语法编译阶段就未通过, 会导致事务内的所有指令均不执行.
下图中, 首先设置 k1
的值为 zhanglei
, 然后开启事务, 修改 k1
的值为 zhangsan
, 然后将 输入了一条错误的指令 set k2
,由于该指令语法错误, 并且都编译未通过, 因此在后续执行 exec
提交时, set k1 zhangsan
指令并未生效. 获取 k1
的值仍然为 zhangslei
冤头债主#
该情况是指,
redis
开启事务之后, 存在错误的指令, 但是在编译阶段并未发现, 而当提交执行后, 冤有头债有主, 正取的指令执行, 错误的指令停止
如下图所示, 首先设置 k1
的值为 zhanglei
, 设置 count
的值为 abc
, 开启事务, 修改 k1
的值为 zhangsan
并执行 incr count指令
. 我们要知道的是, incr
这条自增指令并未在此时报错, 但是它存在语法错误, 字符串无法执行自增指令, 后续获取 k1
和 count
的值, 我们可以发现, 与编译未通过不同的是, set k1 zhangsan
这条指令仍然生效了. 仅有 incr count
该指令未执行成功
注意事项 与传统数据库事务不同,Redis 事务中的指令并不是一起成功一起失败的, 并且 Redis 未提供数据回滚的能力, 开发者需要再事务执行失败后, 自行恢复数据状态
Watch 监控#
Redis 使用
watch
来实现乐观锁定, 类似于 CAS(check and set) ,Redis Watch 命令用于监视一个 (或多个) key ,如果在事务执行之前这个 (或这些) key 被其他命令所改动,那么事务将被打断
如下图所示, 有两台 redis 客户端, 客户端 1 先执行左侧红框内 ① 中的指令, 设置 k1, k2 和 money 的值, 并监视 (watch
) money 的值, 然后客户端 ② 执行右侧红框 ② 中的指令, 并修改了 money 的值为 200, 客户端 1 执行左侧蓝框 ③ 中的指令, 并开启事务, 修改 money 以及 k1 的值, 我们可以发现, 修改指令并未生效, 原因就是 watch
的 money 值发生改变, 导致事务中端
取消监视的几种情况
- 手动执行
unwatch
指令 - 执行
exec
提交事务后, 会取消所有的监视 - 客户端断开链接, 也会取消所有的监视
总结#
讲白了, Redis 的事务就三步: ① 开启事务; ② 命令入队, ③ 执行命令
相关问题#
高频 Redis 面试题解析:Redis 事务是否具备原子性? - 知乎 (zhihu.com)
Redis 管道#
写在前面#
面试题#
如何优化命令往返导致的性能瓶颈?
面试题由来#
Redis 是一种基于客户端 - 服务端模型以及请求/响应协议的 TCP 服务。一个请求会遵循以下步骤:
- 客户端向服务端发送命令分四步 (发送命令 → 命令排队 → 命令执行 → 返回结果),并监听 Socket 返回,通常以阻塞模式等待服务端响应。
- 服务端处理命令,并将结果返回给客户端。
上述两步称为:Round Trip Time(简称 RTT,数据包往返于两端的时间),如果同时需要执行大量的命令,那么就要等待上一条命令应答后再执行,这中间不仅仅多了 RTT(Round Time Trip),而且还频繁调用系统 IO,发送网络请求,同时需要 redis 调用多次 read() 和 write() 系统方法,系统方法会将数据从用户态转移到内核态,这样就会对进程上下文有比较大的影响了,性能不太好,o(╥﹏╥)o
答案
管道 (pipeline) 可以一次性发送多条命令给服务端,服务端依次处理完完毕后,通过一条响应一次性将结果返回,通过减少客户端与 redis 的通信次数来实现降低往返延时时间。pipeline 实现的原理是队列,先进先出特性就保证数据的顺序性。
是什么#
Pipeline 是为了解决 RTT 往返回时,仅仅是将命令打包一次性发送,对整个 Redis 的执行不造成其它任何影响. 批处理命令变种, 类似于 redis 原生命令中的
mest
和mget
怎么用#
cat cmd.txt | redis-cli -a 1234 --pipe
先将需要执行的指令写入一个文档, 文档中的指令格式可以是通常的指令, 也可以是 aof 的增量文件的指令格式. 安装上方的指令即可将文档中的指令打包扔给 redis server 执行
总结#
Pipeline 与原生批量命令对比#
- 原生批量命令是原子性 (例如:mset,mget),pipeline 是非原子性
- 原生批量命令一次只能执行一种命令,pipeline 支持批量执行不同命令
- 原生批命令是服务端实现,而 pipeline 需要服务端与客户端共同完成
Pipeline 与事务对比#
- 事务具有原子性,管道不具有原子性
- 管道一次性将多条命令发送到服务器,事务是一条一条的发,事务只有在接收到 exec 命令后才会执行,管道不会
- 执行事务时会阻塞其他命令的执行,而执行管道中的命令时不会
注意事项#
- pipeline 缓冲的指令只是会依次执行,不保证原子性,如果执行中指令发生异常,将会继续执行后续的指令
- 使用 pipeline 组装的命令个数不能太多,不然数据量过大客户端阻塞的时间可能过久,同时服务端此时也被迫回复一个队列答复,占用很多内存
Redis 发布与订阅#
学习定位#
该部分内容了解即可
是什么#
Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。
能干嘛#
-
消息订阅: Redis 客户端可以订阅任意数量的频道,类似我们微信关注多个公众号, 下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
-
消息发布: 当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
怎么用#
常用指令#
命令 | 描述 |
---|---|
PSUBSCRIBE pattern [pattern ...] | ① 订阅一个或多个符合给定模式的频道。② 推荐先订阅再发布, 否则在订阅之前发布的消息接收不到。 |
PUBSUB subcommand [argument [argument ...]] | 查看订阅与发布系统状态。 |
PUBLISH channel message | 将信息发送到指定的频道。 |
PUNSUBSCRIBE [pattern [pattern ...]] | 退订所有给定模式的频道。 |
SUBSCRIBE channel [channel ...] | 订阅给定的一个或多个频道的信息。 |
UNSUBSCRIBE [channel [channel ...]] | 指退订给定的频道。 |
案例演示#
订阅/退订/发布
涉及指令
SUBSCRIBE
andUNSUBSCRIBE
andPUBLISH
- 客户端 A 订阅消息, 客户端 C 发布消息 如下图 ①②③
- 客户端 B 也订阅消息, 客户端 C 发布消息 如下图 ④⑤⑥ 此时客户端 B 并未收到订阅之前的消息
- 客户端 A 退订消息, 客户端 C 发布消息 如下图 ⑦⑧⑨ 此时仅有客户端 B 收到了消息
批量订阅与发布#
涉及指令:
PUBSUB NUMPAT
andPUNSUBSCRIBE
- 查看当前订阅模式的数量 当前为 0 如下图 ①
- 订阅
a* b* c*
三种模式 如下图 ② - 再次查看订阅模式的数量 如下图 ③
- 发布消息, 客户端通过订阅模式的方式接收 如图 ④⑤⑥⑦
- 退订
PUNSUBSCRIBE a* b* c*
这个参数在不同的客户端下有不同的表现, 此处不做展示了就
查看订阅与发布系统状态#
# 查看订阅模式的数量 (订阅模式的数量而不是客户端的数量)。
PUBSUB NUMPAT
# 查看给定频道的订阅者数量, 订阅模式的客户端不计算在内
PUBSUB NUMSUB [channel-1 ... channel-N]
# 活跃频道指的是那些至少有一个订阅者的频道, 订阅模式的客户端不计算在内。有pattern参数, 则只会返回与pattern参数相匹配的
PUBSUB channels [pattern]
总结#
Redis 可以实现消息中间件 MQ 的功能,通过发布订阅实现消息的引导和分流。仅代表我个人,不推荐使用该功能,专业的事情交给专业的中间件处理,redis 就做好分布式缓存功能
缺点
- 发布的消息在 Redis 系统中不能持久化,因此,必须先执行订阅,再等待消息发布。如果先发布了消息,那么该消息由于没有订阅者,消息将被直接丢弃
- 消息只管发送对于发布者而言消息是即发即失的,不管接收,也没有 ACK 机制,无法保证消息的消费成功。
- 以上的缺点导致 Redis 的 Pub/Sub 模式就像个小玩具,在生产环境中几乎无用武之地,为此 Redis5.0 版本新增了 Stream 数据结构,不但支持多播,还支持数据持久化,相比 Pub/Sub 更加的强大
Redis 复制#
是什么#
一句话来讲就是 主从复制 , master 以写为主, slave 以读为主, 并且当 master 数据变化的时候,自动将新的数据异步同步到其它 slave 数据库。 下图是来自于官网的解释 Redis replication | Redis
能干嘛#
- 读写分离
- 容灾恢复
- 数据备份
- 水平扩容,可支持高并发
怎么用#
- 配从不配主 只需要修改从库的配置文件即可, 无须修改主库的配置文件
- 权限问题 如果主库配置了密码, 则要将从库配置文件中
masterauth
参数配置为主库的密码, 否则主库会拒绝从库的请求 - 基本的命令
命令 | 描述 |
---|---|
info replication |
可以查看复制节点的主从关系和配置信息 |
replicaof 主库IP 主库端口 |
告诉当前的 Redis 服务器从哪个主库复制数据 |
replicaof no one |
当前服务器将停止复制来自其他服务器的数据,并成为独立的主服务器。 |
SLAVEOF 192.168.1.100 6379 |
命令等同于 replicaof , 是旧版本的命令, 为兼容而保留的 |
SLAVEOF no one |
命令等同于 replicaof no one , 是旧版本的命令, 为兼容而保留的 |
案例演示#
架构说明#
如果使用三台虚拟机来启动三台 redis 服务, 电脑有点吃不消, 我们使用三个不同的端口号来模拟三台服务即可, 反正原理上大差不差
文件配置#
- 开启 daemaonize yes
- 注释 bind 127.0.0.1
- 关闭 protected-mode no
- 指定端口
- 指定工作目录 备份会存放在该目录下
dir /myredis/redis6381/backupfiles
- 修改 pid 文件名称,pidfile
/myredis/redis6381/run/redis_6381.pid
- 修改 log 文件名字,logfile
/myredis/redis6381/logs/redis_6381.log
- 设置 requirepass 密码
- 设置 dump.rdb 名字
- 开启 aof 备份
appendonly yes
- 设置 aof 文件的备份文件夹,
appenddirname "appendonlydir"
, 这样配置的花 AOF 备份文件就在/myredis/redis6381/backupfiles/appendonlydir 文件夹下 - 设置 aof 文件名前缀,
appendfilename "appendonly.aof"
- 如果是从机,则需要配置主机的通行证 masterauth,必须的
拷贝三份配置文件
[su@localhost conf]$ sudo cp redis7.conf redis6379.conf
[su@localhost conf]$ sudo cp redis7.conf redis6380.conf
[su@localhost conf]$ sudo cp redis7.conf redis6381.conf
操作演示#
修改完三份配置文件后, 启动三个端口的服务, 如下动图我们可以发现, 在主服务 6379 上设置的 k1 和 k2, 均能在从服务上读取数据, 但是从服务拒绝写入数据
原理和流程#
缺点#
复制延时#
由于所有的写操作都是先在 Master 上操作,然后同步更新到 Slave 上,所以从 Master 同步到 Slave 机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave 机器数量的增加也会使这个问题更加严重
主机停止服务#
默认情况下,master 服务挂掉之后,不会在 slave 节点中自动重选一个 master,redis 无法在对外提供写入服务,如果想要重启服务,需要人工干预。无人值守成为刚需。
Redis 哨兵#
是什么#
吹哨人巡查监控后台 master 主机是否故障,如果故障了根据投票数自动将某一个从库转换为新主库,继续对外服务
作用:
能干嘛#
- 主从监控:监控主从 Redis 运行是否正常
- 消息通知:可以将故障转移的结果通知给客户端
- 故障转移:如果主机异常,则进行主从切换,将其中一个 salve 作为新的 master
- 配置中心:客户端可以通过哨兵来获取当前 redis 服务的主节点地址
怎么用#
架构说明#
由于每一台虚拟机进启动一台 redis 的话,下图架构至少需要 6 台虚拟机,这样的话 16G 的内存有点吃不消,因此,如下图中的架构图所示,仅以端口号来对服务进行区分
案例演示#
创建相关文件夹#
sudo mkdir /myredis/sentinel26379
配置 sentinel.conf 文件#
bind 0.0.0.0
daemonize yes
protected-mode no
port 26379
logfile "/myredis/sentinel26379/logs/sentinel26379.log"
pidfile /myredis/sentinel26379/run/sentinel26379.pid
# 修改工作的目录
dir /myredis/sentinel26379
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel auth-pass mymaster 123456
启动主从复制#
由于测试机器上安装了 docker,并且自动启动了 docker 容器,因此需要先关闭一下,避免冲突。
redis-server /myredis/redis6379/conf/redis7_6379.conf
redis-server /myredis/redis6380/conf/redis7_6380.conf
redis-server /myredis/redis6381/conf/redis7_6381.conf
测试主从复制正常
启动哨兵#
redis-sentinel /myredis/sentinel26379/conf/sentinel_26379.conf --sentinel
redis-sentinel /myredis/sentinel26380/conf/sentinel_26380.conf --sentinel
redis-sentinel /myredis/sentinel26381/conf/sentinel_26381.conf --sentinel
测试哨兵自动切换的流程#
首先,正常启动主机服务,从机服务,查看同步是否正常,如下图 ①-⑤,此时我们停止主机 6379 的服务,如下图步骤 ⑥,为了验证从机是否自动切换成主机,我们进行了步骤 ⑦ 和 ⑧,发现从机并没有切换为主机,当进行操作 ⑨ 的时候,我们可以发现 6380 显示的身份为 master,此时再进行写入操作,发现成功,并且数据同步到了 6381 机器上,如下图步骤 10-11(说明主机下线时,从机上位需要一定的时间),当 6379 服务重新启动的时候,我们可以发现此时 6379 的身份已经变成了从机,至此哨兵切换主从机的步骤完成。
对比配置文件#
senter
运行流程和选举原理#
当一个主从配置中的 master 失效之后,sentinel 可以选举出一个新的 master 用于自动接替原 master 的工作,主从配置中的其他 redis 服务器自动指向新的 master 同步数据。般建议 sentinel 采取奇数台,防止某一台 sentinel 无法连接到 master 导致误切换
哨兵是禁军,哨兵 leader 是进军统帅,而选举则是发动政变,推举新的 master
使用建议#
- 哨兵节点的数量应该为多个,哨兵本身应该是集群,保证高可用
- 哨兵节点的数量应该为多个
- 各个哨兵节点的配置应一致
- 如果哨兵节点部署在 Docker 等容器里,尤其注意端口的正确映射
- 哨兵集群 + 主从复制,并不能保证数据零丢失
Redis 集群#
案例演示#
为节省服务器资源,将会在同一台服务器上,不同端口下起六台服务。均位于
/myredis/cluster
文件夹下
3 主 3 从的集群配置#
^8208ce
配置相关文件及文件夹
创建此次配置的根路径
mkdir /myredis/cluster
创建所属的文件夹
mkdir /myredis/cluster/redisCluster6381
创建配置文件,配置文件的内容参考下面的conf代码块
vim /myredis/cluster/redisCluster6381/redisCluster6382/cluster.conf
cluster.conf 文件的内容,不同的端口文件只需要更换 ip 端口就好了
bind 0.0.0.0
daemonize yes
protected-mode no
port 6386
logfile "/myredis/cluster/redisCluster6386/cluster.log"
pidfile /myredis/cluster/redisCluster6386/cluster.pid
dir /myredis/cluster/redisCluster6386
dbfilename dump.rdb
appendonly yes
appendfilename "appendonly.aof"
requirepass 111111
masterauth 111111
cluster-enabled yes
cluster-config-file nodes-6386.conf
cluster-node-timeout 5000
启动 6 台 redis 实例
redis-server /myredis/cluster/redisCluster6381/cluster.conf
......
redis-server /myredis/cluster/redisCluster6386/cluster.conf
构建集群关系
构建集群关系的指令格式如下,其中 --cluster-replicas 1
表示为每个 master 创建一个 slave 节点
redis-cli -a 111111 --cluster create --cluster-replicas 1 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 127.0.0.1:6385 127.0.0.1:6386
执行构建集群的命令后,我们可以发现 6381
,6382
,6383
为主节点,从节点依次为 6384
,6385
,6386
。
查验集群状态
随便进入一台 redis 实例中去
查看集群节点从属关系
cluster nodes
查看集群状态
cluster info
集群读写#
问题:为什么 set k1 v1 在 6383 端口是历下就能正常写入,而在 6381 就会报错
由于 redis 使用了槽位分区,k1
经过 crc16 算法会落在 12706 的位置上,而该位置由实例 6383 负责。那么问题又来了,我们该如何解决这个问题呢?总不能每次都自己算一遍吧
解决方案:进入实例时,新加
-c
参数优化路由
当我们想知道一个键会落在那个槽位置时,可以选择使用 cluster keyslot 键名称
主从容错迁移#
如果此时一台 6381 的主机停止服务,可能是发生宕机,也可能是发生其他因素导致需要停止该服务,那么会发生什么现象?
我们在模拟宕机之前,6381 为主机,其从机为 6385,当我们手动停止 6381 的服务,再次查看集群状态,我们可以发现 6381 的状态标识已经变换成了 fail,而 6385 的角色从 slave 转换成了 master
我们模拟宕机问题处理完毕,重启 6381 服务 redis-server /myredis/cluster/redisCluster6381/cluster.conf
查看集群状态,我们可以发现 6381 服务并未恢复到主机的地位,而是变成了 6385 的从机
设想我们 6381 服务器的性能较好,希望让其担任主机的角色,6385 成为其从机,我们应该如何调整节点的从属呢?
在从节点上执行 CLUSTER FAILOVER
命令,将启动手动故障转移过程
注意 由于主机宕机,切换成从机上位需要一定的时间,如果此时刚好有其主机负责的槽位接收到了客户端请求,会响应给客户端请求响应失败,如果主机接收到了数据,未来得及同步到从机中去,立刻发生了宕机,则会导致数据的丢失。因此主从集群也无法保证数据的强一致性
集群扩容#
随着业务量的增加,对集群需要进行扩容,在原有集群的基础上再增加两台 redis 实例,端口号分别为 6387 和 6388。分别是一主一从
先启动两个 redis 实例,与之前 6 个服务启动 redis 实例的方式相同。内容参考 三主三从集群配置 我们可以发现,6387 和 6388 服务启动后并未直接加入集群中去
将新增的 6387 节点作为 master 加原集群
redis-cli -a 密码 --cluster add-node 新增节点ip:端口 引路机器ip:端口
此时我们检查集群状况 redis-cli -a 密码 --cluster check 真实ip地址:端口
可以发现,新增加进来的 6387 节点虽然被分配为了主节点,但是并没有负责的槽位
使用 redis-cli -a 密码 --cluster reshard IP地址:端口号
重新分派槽位号。在执行重新分派的指令之后,程序会询问你要重新分派多少槽位,以及这些槽位要重新分配给谁,以及从哪些节点迁移槽位 (all 代表所有节点)。
为新增的主节点添加从节点 6388
-- redis-cli -a 密码 --cluster add-node 新slaveip:端口 引路节点ip:端口 --cluster-slave --cluster-master-id 新主机节点ID
redis-cli -a 111111 --clusetr add-node 127.0.0.1:6388 127.0.0.1:6384 --cluster-slave --cluster-master-id db03f008a93e4e878fd9d071090ecb3f72ad9cbe
确定从节点加入完成后,我们检查一些当前集群状态
redis-cli -a 111111 --cluster check 127.0.0.1:6381
根据下图我们可以看到,6388 节点已经成功加入集群,并且成为了 6377 的从节点
集群缩容#
集群既然能扩容,也相应的能缩容,我们现在将 6388 个 6387 两个节点从集群中移除
移除集群节点前,先要获取节点的 ID
直接执行删除从节点的命令
// redis-cli -a 密码 --cluster del-node 从机ip:端口 从机节点ID
redis-cli -a 111111 --cluster del-node 127.0.0.1:6388 475638d51e2be82d3db552325e89b9ca5da5e83d
我们使用同样的方式删除主节点,可以发现系统会提示我们 6387 下仍有数据,让我们重新分派槽位号之后再试
按照提示,我们手动地在 Redis 集群中重新分配哈希槽,执行下面的指令
redis-cli -a 111111 --cluster reshard 127.0.0.1:6387
重新分配哈希槽的过程无非是,分配多少个哈希槽,由谁分出来,给谁的一个过程
此时我们再次执行移除节点 6387 的指令,显示移除成功
再次查看集群状况,我们可以发现集群状态再次变成了三主三从,不同点是,6381 节点负责了 8192 个槽位,这是因为我们将 6387 所有的槽位均分配给了 6381 的原因。至此我们实现了集群的缩容
注意点#
不在同一个 slot 槽下的多键操作支持不好,需要使用通用占位符
使用 mset 直接操作 三个 key,提示这些 key 的哈希不在同一个槽位下
同样,我们使用 mget 的时候,也会遇到这种问题,也是使用哈希标签来解决问题
现在集群架构是 3 主 3 从的 集群 r 由 3 个 master 平分 16384 个 slot,每个 master 的小集群负责 1/3 的 slot,对应一部分数据。cluster-require-full-coverage: 默认值 yes , 即需要集群完整性,方可对外提供服务 通常情况,如果这 3 个小集群中,任何一个(1 主 1 从)挂了,你这个集群对外可提供的数据只有 2/3 了, 整个集群是不完整的, redis 默认在这种情况下,是不会对外提供服务的。如果你的诉求是,集群不完整的话也需要对外提供服务,需要将该参数设置为 no,不是很建议这样做
附录#
作者:ZhangBlog
出处:https://www.cnblogs.com/aaalei/p/17968865
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!