[TOC]
前置知识学习补充 Redis数据库基础入门介绍与安装 - https://blog.weiyigeek.top/2019/4-17-49.html
Redis数据库基础数据类型介绍与使用 - https://blog.weiyigeek.top/2020/5-17-50.html
Redis基础运维之原理介绍和主从配置 - https://blog.weiyigeek.top/2019/4-17-97.html
Redis基础运维之哨兵和集群安装配置 - https://blog.weiyigeek.top/2019/4-17-576.html
Redis基础运维之在K8S中的安装与配置 - https://blog.weiyigeek.top/2019/4-17-524.html
Redis数据库性能测试及优化配置 - https://blog.weiyigeek.top/2019/4-17-527.html
Redis数据库客户端操作实践及入坑出坑 - https://blog.weiyigeek.top/2019/4-17-577.html
0x00 数据持久化 描述: Redis 是将数据存储在内存之中所以其读写效率非常高,但是往往事物都不是那么美好,当由于某些不可抗力导致机器宕机、redis服务停止此时您在内存中的数据将完全丢失;
所以为了使 Redis 在异常重启后仍能保证数据不丢失, 我们就需要对其进行设置持久化存储,使其将内存的数据通过某种方式存入磁盘中,当Redis服务端重启后便会从该磁盘中进行读取数据进而恢复Redis中的数据, 所以Redis数据持久化是容灾恢复必备条件
;
Redis支持两种持久化方式:
(1) RDB 持久化(默认支持): 该机制是指在指定的时间间隔内将内存中数据集写入到磁盘;
(2) AOF 持久化: 该机制将以日志的形式记录服务器所处理的每一个写操作,同时在Redis服务器启动之初会读取该文件来重新构建数据库,以保证启动后数据库中的数据完整;
(3) 无持久化: 将 Redis 作为一个临时缓存,并将数据存放到memcached
之中;
Tips: 为保证数据安全性,我们可以设置 Redis 同时使用RDB和AOF持久化方式,来保证重启后Redis服务器中的数据完整;
1.RDB 方式 描述: Redis 将某一时刻的快照(备份的数据库数据)保存成一种称为RDB格式的文件中,这种格式是经过压缩的二进制文件,数据库的保存和恢复文件如下图所示。
weiyigeek.top-rdb数据
保存RDB数据的两种方式:
1、save命令:save命令会阻塞redis服务器的进程,直到RDB文件创建完,在该期间redis不能处理任何的命令请求,这就是save命令最大的缺陷。
2、bgsave命令:与save命令不同的是 bgsave在生成RDB文件时,会派生出一个子进程,子进程负责创建RDB文件,在此期间,主进程和子进程是同时存在的,因此不会阻塞redis服务器进程。 (推荐方式)
说明:可用lastsave命令
查看生成RDB文件是否成功
优势
1) 采用子线程创建RDB文件(dump.rdb
),不会对redis服务器性能造成大的影响( 性能最大化);
2) 快照生成的RDB文件是一种压缩的二进制文件,可以方便的在网络中传输和保存。通过RDB文件可以方便的将redis数据恢复到某一历史时刻,可以提高数据安全性,避免宕机等意外对数据的影响。
3) 适合大规模的数据恢复, RDB的启动恢复效率高。
4) 如果业务对数据完整性和一致性要求不高,RDB是很好的选择。
劣势
1) 在redis文件在时间点A生成,之后产生了新数据,还未到达另一次生成RDB文件的条件,redis服务器崩溃了,那么在时间点A之后的数据会丢失掉,数据一致性不是完美的好, 如果可以接受这部分丢失的数据,可以用生成RDB的方式;
2) 快照持久化方法通过调用fork()方法创建子线程。当redis内存的数据量比较大时,创建子线程和生成RDB文件会占用大量的系统资源和处理时间,对 redis处理正常的客户端请求造成较大影响。
3) 数据的完整性和一致性不高,因为RDB可能在最后一次备份时宕机了。
4) 备份时占用内存,因为Redis 在备份时会独立创建一个子进程,将数据写入到一个临时文件(此时内存中的数据是原来的两倍哦),最后再将临时文件替换之前的备份文件。所以 Redis 的持久化和数据的恢复要选择在夜深人静的时候执行是比较合理的。
Q: 通过RDB文件恢复数据?
答: 将dump.rdb 文件拷贝到redis的安装目录的bin目录下,重启redis服务即可。在实际开发中,一般会考虑到物理机硬盘损坏情况,选择备份dump.rdb 。
配置说明:
[TOC]
前置知识学习补充 Redis数据库基础入门介绍与安装 - https://blog.weiyigeek.top/2019/4-17-49.html
Redis数据库基础数据类型介绍与使用 - https://blog.weiyigeek.top/2020/5-17-50.html
Redis基础运维之原理介绍和主从配置 - https://blog.weiyigeek.top/2019/4-17-97.html
Redis基础运维之哨兵和集群安装配置 - https://blog.weiyigeek.top/2019/4-17-576.html
Redis基础运维之在K8S中的安装与配置 - https://blog.weiyigeek.top/2019/4-17-524.html
Redis数据库性能测试及优化配置 - https://blog.weiyigeek.top/2019/4-17-527.html
Redis数据库客户端操作实践及入坑出坑 - https://blog.weiyigeek.top/2019/4-17-577.html
0x00 数据持久化 描述: Redis 是将数据存储在内存之中所以其读写效率非常高,但是往往事物都不是那么美好,当由于某些不可抗力导致机器宕机、redis服务停止此时您在内存中的数据将完全丢失;
所以为了使 Redis 在异常重启后仍能保证数据不丢失, 我们就需要对其进行设置持久化存储,使其将内存的数据通过某种方式存入磁盘中,当Redis服务端重启后便会从该磁盘中进行读取数据进而恢复Redis中的数据, 所以Redis数据持久化是容灾恢复必备条件
;
Redis支持两种持久化方式:
(1) RDB 持久化(默认支持): 该机制是指在指定的时间间隔内将内存中数据集写入到磁盘;
(2) AOF 持久化: 该机制将以日志的形式记录服务器所处理的每一个写操作,同时在Redis服务器启动之初会读取该文件来重新构建数据库,以保证启动后数据库中的数据完整;
(3) 无持久化: 将 Redis 作为一个临时缓存,并将数据存放到memcached
之中;
Tips: 为保证数据安全性,我们可以设置 Redis 同时使用RDB和AOF持久化方式,来保证重启后Redis服务器中的数据完整;
1.RDB 方式 描述: Redis 将某一时刻的快照(备份的数据库数据)保存成一种称为RDB格式的文件中,这种格式是经过压缩的二进制文件,数据库的保存和恢复文件如下图所示。
weiyigeek.top-rdb数据
保存RDB数据的两种方式:
1、save命令:save命令会阻塞redis服务器的进程,直到RDB文件创建完,在该期间redis不能处理任何的命令请求,这就是save命令最大的缺陷。
2、bgsave命令:与save命令不同的是 bgsave在生成RDB文件时,会派生出一个子进程,子进程负责创建RDB文件,在此期间,主进程和子进程是同时存在的,因此不会阻塞redis服务器进程。 (推荐方式)
说明:可用lastsave命令
查看生成RDB文件是否成功
优势
1) 采用子线程创建RDB文件(dump.rdb
),不会对redis服务器性能造成大的影响( 性能最大化);
2) 快照生成的RDB文件是一种压缩的二进制文件,可以方便的在网络中传输和保存。通过RDB文件可以方便的将redis数据恢复到某一历史时刻,可以提高数据安全性,避免宕机等意外对数据的影响。
3) 适合大规模的数据恢复, RDB的启动恢复效率高。
4) 如果业务对数据完整性和一致性要求不高,RDB是很好的选择。
劣势
1) 在redis文件在时间点A生成,之后产生了新数据,还未到达另一次生成RDB文件的条件,redis服务器崩溃了,那么在时间点A之后的数据会丢失掉,数据一致性不是完美的好, 如果可以接受这部分丢失的数据,可以用生成RDB的方式;
2) 快照持久化方法通过调用fork()方法创建子线程。当redis内存的数据量比较大时,创建子线程和生成RDB文件会占用大量的系统资源和处理时间,对 redis处理正常的客户端请求造成较大影响。
3) 数据的完整性和一致性不高,因为RDB可能在最后一次备份时宕机了。
4) 备份时占用内存,因为Redis 在备份时会独立创建一个子进程,将数据写入到一个临时文件(此时内存中的数据是原来的两倍哦),最后再将临时文件替换之前的备份文件。所以 Redis 的持久化和数据的恢复要选择在夜深人静的时候执行是比较合理的。
Q: 通过RDB文件恢复数据?
答: 将dump.rdb 文件拷贝到redis的安装目录的bin目录下,重启redis服务即可。在实际开发中,一般会考虑到物理机硬盘损坏情况,选择备份dump.rdb 。
配置说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 cat > redis.conf <<'EOF' requirepass WeiyiGeek.top dir /data 说明:save <指定时间间隔> <执行指定次数更新操作>,满足条件就将内存中的数据同步到硬盘中。官方出厂配置默认是 900秒内有1个更改,300秒内有10个更改以及60秒内有10000个更改, 则将内存中的数据快照写入磁盘。 save 900 1 save 300 10 save 60 10000 dbfilename dump.rdb rdbcompression yes rdbchecksum yes stop-writes-on-bgsave-error yes rdb-save-incremental-fsync yes EOF
实际案例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 127.0.0.1:6379> SAVE OK 127.0.0.1:6379> BGSAVE OK 127.0.0.1:6379> quit /usr/local /redis/bin/redis-check-aof --fix appendonly.aof root@dfbf8c0c0625:/data dump.rdb docker cp dfbf8c0c0625:/data/dump.rdb /var/lib/redis/dump.rdb
RDB文件恢复数据流程 1 2 3 4 1、先备份一份 dump.rdb 为 dump_bak.rdb(模拟线上) 2、flushall 清空数据(模拟数据丢失,需要注意 flushall 也会触发rbd持久化) 3、将 dump_bak.rdb 替换为 dump.rdb 4、重启redis服务,恢复数据
2.AOF 方式 描述: AOF是redis对将所有的写命令保存到一个aof文件中,根据这些写命令实现数据的持久化和数据恢复。
weiyigeek.top-AOF备份还原
AOF文件生成机制
答: 生成过程包括三个步骤,即命令追加、文件写入、文件同步
。 redis 打开AOF持久化功能之后,redis在执行完一个写命令后,把执行的命令首先追加到redis内部的aof_buf缓冲区膜末尾,此时缓冲区的记录还没有写到appendonly.aof文件中。然后,缓冲区的写命令会被写入到 AOF 文件,这一过程是文件写入过程。对于操作系统来说,调用write函数并不会立刻将数据写入到硬盘,为了将数据真正写入硬盘,还需要调用fsync函数,调用fsync函数即是文件同步的过程,只有经过了文件的同步过程,写命令才真正的被保存到了AOF文件中。选项 appendfsync 就是配置同步的频率的。
weiyigeek.top-AOF备份流程
AOF文件重写
1、redis不断的将写命令保存到AOF文件中,导致AOF文件越来越大,当AOF文件体积过大时,数据恢复的时间也是非常长的,因此,redis提供了重写或者说压缩AOF文件的功能。 比如对key1初始值是0,调用incr命,100次,key1的值变为100,那么其实直接一句set key1 100 就可以顶之前的100局调用
,AOF重写功能就是干这个事情的。重写时,可以调用BGREWRITEAOF命令重写AOF文件,与新建子线程bgsave命令的工作原理相似。也可以通过配置文件配置什么条件下对AOF文件重写。
2、重写的原理:Redis 会fork出一条新进程,读取内存中的数据,并重新写到一个临时文件中。并没有读取旧文件(太大了)。最后替换旧的aof文件
3、重写触发机制:当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发,这里的“一倍”和“64M” 可以通过配置文件修改
优势
1) 该机制可以带来更高的数据安全性,即数据持久化; 常规三种同步策略即每秒同步(异步完成效率高
)、每修改同步(同步插入修改删除操作效率最低
)和不同步;
2) 由于该机制对日志文件的写入操作采用的是append模式,即使过程中出现宕机也不会破坏日志文件中已经存在的内容,如果数据不完整在Redis下次启动之前, 通过redis-check-aof解决数据一致性问题;
3) 如果日志文件体积过大可以启动rewrite机制,即redis以append模式不断的将修改数据写到老的磁盘文件中,同时创建新文件记录期间有哪些修改命令执行,此项极大的保证数据的安全性;
4) AOF文件可读性强,其包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作(可通过此文件完成数据的重构
)
5) 数据的完整性和一致性更高
劣势
1) AOF文件比RDB文件较大, 对于相同数量的数据集而言;
2) redis负载较高时,RDB文件比AOF文件具有更好的性能;
3) RDB使用快照的方式持久化整个redis数据,而aof只是追加写命令,因此从理论上来说,RDB比AOF方式更加健壮,另外,官方文档也指出,在某些情况下,AOF的确也存在一些bug,比如使用阻塞命令时,这些bug的场景RDB是不存在的。
4) 因为AOF记录的内容多,文件会越来越大,数据恢复也会越来越慢。
5) 根据同步策略的不同,AOF在运行效率上往往会慢于RDB,总的来说每秒同步策略的效率还是比较高的
Q: 如何触发AOF快照?
答: 根据配置文件触发,可以是每次执行触发,可以是每秒触发,可以不同步。
Q: 如何根据AOF文件恢复数据?
答: 正常情况下,将appendonly.aof 文件拷贝到redis的安装目录的bin目录下,重启redis服务即可。但在实际开发中,可能因为某些原因导致 appendonly.aof
文件格式异常,从而导致数据还原失败,可以通过命令redis-check-aof --fix appendonly.aof
进行修复 。
配置说明: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 cat > redis.conf <<'EOF' dir "/data" appendonly yes appendfilename "appendonly.aof" appendfsync everysec no-appendfsync-on-rewrite no auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 256mb aof-rewrite-incremental-fsync yes aof-load-truncated yes aof-use-rdb-preamble yes EOF
Q: 如何通过AOF文件恢复数据流程? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 1、执行flushall,模拟数据丢失 2、重启 redis 服务,恢复数据 3、修改 appendonly.aof,模拟文件异常 4、重启 Redis 服务失败,这同时也说明了RDB和AOF可以同时存在,且优先加载AOF文件。 5、使用 redis-check-aof 校验 appendonly.aof 文件。 /usr/local /redis/bin/redis-check-aof --fix appendonly.aof 6、重启Redis 服务后正常。 redis-cli -h 17.20.0.2 -a password --pipe < applendonly.aof 注意:当你使用 flushall 清空数据的时候,重启redis服务发现数据没恢复,是因为 FLUSHALL 命令也被写入AOF文件中,会导致数据恢复失败,所只需要删除aof文件中的flushall就行了
Tips : 在数据库恢复时把 aof(append only file) 从中对redis数据库操作的命令,增删改操作的命令,执行了一遍即可。
实际案例: 描述: 用于异步执行一个 AOF(AppendOnly File)
文件重写操作, 重写会创建一个当前 AOF 文件的体积优化版本,因为AOF为记录每次的操作会导致实际记录冗杂、使得文件过大,所以需要做重写操作。
重写方式分为以下两种:1 2 3 4 5 6 7 8 9 auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 256mb 127.0.0.1:6379> BGREWRITEAOF Background append only file rewriting started
如何抉择 RDB OR AOF? 描述: 在实际生产环境中不要仅使用RDB(加载快、不保证数据完整性)或仅使用AOF(加载慢、数据完整性保证), 所以推荐综合使用AOF和RDB两种持久化机制进行数据备份。
AOF 机制保证数据不丢失,并且文件有一定的可读性即可以选择性恢复一部分数据,所以作为数据恢复的第一选择;
RDB 机制在AOF文件都丢失或损坏不可用的时候,可以利用其冷备文件来进行快速的数据恢复;
AOF和RDB同时工作特点:
当 rdb 进行 snapshotting 时, redis 便不会再执行 AOF rewrite, 反之则一样。
当 rdb 进行 snapshotting 时, 其它用户也在执行 BGREWRITEAOF 命令, 结果是只有等RDB快照生成之后,才会执行AOF rewrite;
当同时拥有rdb 快照文件和AOF日志文件时, Redis 重启时会优先使用AOF日志进行恢复,。
数据恢复完全依赖于磁盘持久化,如果rdb和aof上都没有数据,数据就无法恢复了。
Tips : 重点再记录、为保证数据容灾建议启用rdb与aof持久化机制,前者保证数据备份而后者保证数据的完整性。 Tips : 重点再记录、当在服务器中同时启用rdb与aof持久化机制时,在redis服务启动时优先加载AOF文件(其数据的完整性)。
合并两个不同实例的数据 描述: 我们可以利用如下方式进行集群多个主节点持久化数据的合并。
(1) AOF 备份合并: 我们说过它实际上是一些列Redis的命令文本。 例如,假设有两台 Redis(6379, 6479),它们的AOF文件名分别为(6379.aof, 6479.aof),现在要将6379的数据合并到 6479.aof1 2 3 4 5 6 7 8 cp 6379.aof 6379.aof.bak, cp 6479.aof 6479.aof.bak cat 6379.aof 6479.aof > new.aof /usr/local /redis/bin/redis-check-aof --fix appendonly.aof
(2) RDB 备份合并: 注意以下方法可能由于服务端版本不同而有些许差异。 RDB格式如下:头5个字节是字符REDIS,之后4个字节代表版本号,阿里的版本分别是 00 00 00 06,之后2个字节 FE 00,FE是标识 00是数据库,还好我们只有一个库, 最后的结尾9个字节 , FF 加上8个字节的CRC64校验码(实在没空弄,后来偷了一个懒)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 dd bs=1 if =src_1.rdb of=1.rdb count=566346494 dd bs=1 if =src_2.rdb of=2.rdb skip=11 count=570214500 ... dd bs=1 if =src_8.rdb of=8.rdb skip=11 count=569253042 cat 1.rdb > dump.rdb cat 2.rdb >> dump.rdb ... cat 8.rdb >> dump.rdb redis-check-rdb dump.rdb rdbchecksum no
Tips : 数据恢复到此结束,此方法只适合用于临时恢复和导出数据,数据完整性不敢保证。
参考地址: https://github.com/sripathikrishnan/redis-rdb-tools/wiki/Redis-RDB-Dump-File-Format
其它工具:
0x01 备份容灾 一、备份 1.手动备份redis数据库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #!/bin/bash echo "auth 123456\nping\nsave\n" | redis-cli -h 127.0.0.1 -p 6379echo "auth 123456\nping\nbgsave\n" | redis-cli -h 127.0.0.1 -p 6379redis-cli -h 127.0.0.1 -p 6379 --rdb /tmp/backup/app-6379.rdb current_date=$(date +%Y%m%d-%H%M%S) BACKUPDIR="/backup/redis" RDBFILE="/data/dump.rdb" AOFFILE="/data/appendonly.aof" if [ ! -d ${BACKUPDIR} ];then mkdir -vp ${BACKUPDIR} fi if [ ! -f ${RDBFILE} ];then cp -a ${RDBFILE} ${BACKUPDIR} /${current_date} -dump.rdb fi if [ ! -f ${AOFFILE} ];then cp -a ${AOFFILE} ${BACKUPDIR} /${current_date} -appendonly.aof fi find ${BACKUPDIR} -type f -mtime +7 >> delete.log find ${BACKUPDIR} -type f -mtime +7 -exec rm -rf {} \;
2.迁移Redis指定db-数据库 方式1.同主机db迁移到另外一个dbn中 1 2 3 4 $ redis-cli -h localhost -a weiyigeek.top -n 0 keys "*" | while read key do redis-cli -h localhost -a weiyigeek.top -n 0 --raw dump $key | perl -pe 'chomp if eof' | redis-cli -h localhost -a weiyigeek.top -n 12 -x restore $key 0 done
方式2.跨主机迁移db 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 '''1、建立两个redis连接 2、获取所有的keys() 3、获取keys的类型:string hash''' import redissrc_redis = redis.Redis(host='211.149.218.16' , password='123456' , port=6379 , db=2 ) target_redis = redis.Redis(host='211.149.218.16' , password='123456' , port=6379 , db=14 ) for key in src_redis.keys(): if src_redis.type(key) == b'string' : v = src_redis.get(key) target_redis.set(key,v) else : all_hash_data = src_redis.hgetall(key) for k,v in all_hash_data.items(): target_redis.hset(key,k,v)
3.Redis集群数据备份与迁移 描述: 当我们需要备份或迁移Redis集群时可以采用以下方案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 redis-cli -a weiyigeek --cluster backup 172.16.243.97:6379 . redis-cli -a weiyigeek --cluster import 192.168.75.187:6379 --cluster-from 192.168.1.187:6379 --cluster-from-askpass --cluster-copy
Tips: 第三方redis集群数据迁移工具项目参考(https://github.com/alibaba/RedisShake )
二、恢复 1.系统Redis用户被删除后配置数据恢复流程 描述:在系统删除了配置文件后以及用户账号后恢复方法流程,实际环境中建议利用rdb文件进行重新部署。
Step1.Redis账户数据恢复首先确定系统中是否还有redis用户。(如果拷贝过来的系统也安装了redis,那么肯定是会有redis账户)
Step2.如果发现有redis用户以下步骤可以跳过,否则进行手动添加。
1 2 3 4 echo "redis:x:996:994:Redis Database Server:/var/lib/redis:/sbin/nologin" >> /etc/passwdecho "redis:!!:17416::::::" >> /etc/shadowecho "redis:x:994:" >> /etc/groupecho "redis:!::" >> /etc/gshadow
Step3.Redis配置文件恢复, Redis的配置文件恢复相对简单一些,官方提供了CONFIG REWRITE
命令重写redis.conf配置文件。
1 2 3 redis-cli > CONFIG REWRITE OK
Step4.修改配置文件权限
1 2 touch /etc/redis.conf chown redis:redis /etc/redis.conf
2.Kubernetes中单实例异常数据迁移恢复实践 方案1.利用其他kubernetes集群进行恢复原k8s集群的redis数据。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 #!/bin/bash BACKUP_TYPE=$1 BACKUP_DIR=$2 DATA_DIR="$(pwd) /data" echo "开始时间: $(date +%s) " if [ ! -d ${BACKUP_DIR} ];then echo -e "[Error] - Not Found ${BACKUP_DIR} Dirctory!" ; return -1; fi if [ ! -d ${DATA_DIR} ];then mkdir -vp ${DATA_DIR} ;else rm -rf "${DATA_DIR} /*" ; fi tee redisrecovery.yaml <<'EOF' apiVersion: v1 kind: ConfigMap metadata: name: redis-recovery namespace: database data: redis.conf: |+ bind 0.0.0.0 port 6379 daemonize no supervised no protected-mode no requirepass "weiyigeek" dir "/data" pidfile "/var/run/redis.pid" logfile "/var/log/redis.log" loglevel verbose maxclients 10000 timeout 300 tcp-keepalive 60 maxmemory-policy volatile-lru slowlog-max-len 128 lua-time-limit 5000 save 300 10 save 60 10000 dbfilename "dump.rdb" rdbcompression yes rdb-save-incremental-fsync yes appendfilename "appendonly.aof" appendfsync everysec rename-command FLUSHDB WeiyiGeekFLUSHDB rename-command FLUSHALL WeiyiGeekFLUSHALL rename-command EVAL WeiyiGeekEVAL rename-command DEBUG WeiyiGeekDEBUG rename-command SHUTDOWN WeiyiGeekSHUTDOWN --- apiVersion: apps/v1 kind: StatefulSet metadata: name: redis-recovery namespace: database spec: serviceName: redisrecovery replicas: 1 selector: matchLabels: app: redis-recovery template: metadata: labels: app: redis-recovery spec: containers: - name: redis image: redis:6.2.5-alpine imagePullPolicy: IfNotPresent ports: - containerPort: 6379 name: server command : [ "redis-server" , "/conf/redis.conf" ] volumeMounts: - name: conf mountPath: /conf/redis.conf subPath: redis.conf - name: data mountPath: /data - name: timezone mountPath: /etc/localtime volumes: - name: conf configMap: name: redis-recovery defaultMode: 0755 - name: data hostPath: type : DirectoryOrCreate path: {PersistentDir} - name: timezone hostPath: path: /usr/share/zoneinfo/Asia/Shanghai --- apiVersion: v1 kind: Service metadata: name: redisrecovery namespace: database spec: type : ClusterIP ports: - port: 6379 targetPort: 6379 name: server selector: app: redis-recovery EOF kubectl delete -f redisrecovery.yaml if [ "${BACKUP_TYPE} " == "aof" ];then sed -i "s|# appendonly yes|appendonly yes|g" redisrecovery.yaml cp -a ${BACKUP_DIR} /appendonly.aof ${DATA_DIR} else cp -a ${BACKUP_DIR} /dump.rdb ${DATA_DIR} fi sed -i "s#{PersistentDir}#${DATA_DIR} #g" redisrecovery.yaml kubectl create --save-config -f redisrecovery.yaml flag=$(kubectl get pod -n database -o wide -l app=redis-recovery | grep -c "Running" ) echo -e "\e[31m[Error]: Pod Status is not Running! \e[0m" while [ ${flag} -ne 1 ];do sleep 0.5 flag=$(kubectl get pod -n database -o wide -l app=redis-recovery | grep -c "Running" ) done echo "[OK] redis-recovery Status is Running" echo -e "AUTH weiyigeek\nping\ninfo" | redis-cli -h redisrecovery.database.svc.cluster.local | grep -A 16 "# Keyspace" while [ $? -ne 0 ];do echo -e "AUTH weiyigeek\nping\ninfo" | redis-cli -h redisrecovery.database.svc.cluster.local | grep -A 16 "# Keyspace" done echo "数据恢复完成......" echo "完成时间: $(date +%s) "
命令执行示例:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 /nfsdisk-31/datastore/redis/demo2 /nfsdisk-31/datastore/redis/demo2/data root@WeiyiGeek-107:/nfsdisk-31/datastore/redis/demo2 /nfsdisk-31/datastore/redis/demo2
weiyigeek.top-rdb与aof恢复对比
Tips : 从上述恢复结果可以看出以aof方式恢复的数据比rdb恢复的数据完整,但所加载的时间会随着数据增大会使得AOF方式耗时比rdb耗时更多。
方案2.利用宿主机安装编译redis源码,进行恢复原k8s集群的redis数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 #!/bin/bash if [ $# -eq 0 ];then echo -e "\e[31m[*] $0 [aof|rdb] redis原持久化目录 \e[0m" exit fi BACKUP_TYPE=$1 BACKUP_DIR=$2 DATA_DIR="$(pwd) /data" REDIS_DIR="/usr/local/redis" echo "开始时间: $(date +%s) " if [ ! -d ${BACKUP_DIR} ];then echo -e "[Error] - Not Found ${BACKUP_DIR} Dirctory!" ; return -1; fi if [ ! -d ${DATA_DIR} ];then mkdir -vp ${DATA_DIR} ;else rm -rf "${DATA_DIR} /*" ; fi sudo sysctl -w vm.overcommit_memory=1 sudo sysctl -w vm.swapniess=1 sudo sysctl -w net.ipv4.tcp_max_syn_backlog=4096 echo never > /sys/kernel/mm/transparent_hugepage/enabledif [ ! -f /usr/local /redis/redis.conf ];then REDIS_VERSION="redis-6.2.5" REDIS_URL_TAR="https://download.redis.io/releases/${REDIS_VERSION} .tar.gz" REDIS_TAR="${REDIS_VERSION} .tar.gz" apt install -y gcc make gcc+ pkg-config wget ${REDIS_URL_TAR} -O /tmp/${REDIS_TAR} tar -zxf /tmp/${REDIS_TAR} -C /usr/local / mv /usr/local /${REDIS_VERSION} ${REDIS_DIR} cd ${REDIS_DIR} && make distcleanmake PREFIX=${REDIS_DIR} install cp -a ${REDIS_DIR} /redis.conf /etc/redis.conf for i in $(ls -F ${REDIS_DIR} /bin | grep "*" | sed 's#*##g' );do sudo chmod +700 ${REDIS_DIR} /bin/${i} sudo ln -s ${REDIS_DIR} /bin/${i} /usr/local /bin/${i} done fi tee ${REDIS_DIR} /redis.conf <<'EOF' bind 0.0.0.0port 6379 daemonize yes supervised no protected-mode no requirepass "weiyigeek" dir "/data" pidfile "/var/run/redis.pid" logfile "/var/log/redis.log" maxclients 10000 timeout 300 tcp-keepalive 60 maxmemory-policy volatile-lru slowlog-max-len 128 lua-time-limit 5000 save 900 1 save 300 100 save 60 10000 dbfilename "dump.rdb" rdbcompression yes rdb-save-incremental-fsync yes appendfilename "appendonly.aof" appendfsync everysec rename-command FLUSHDB WeiyiGeekFLUSHDB rename-command FLUSHALL WeiyiGeekFLUSHALL rename-command EVAL WeiyiGeekEVAL rename-command DEBUG WeiyiGeekDEBUG rename-command SHUTDOWN WeiyiGeekSHUTDOWN EOF if [ "${BACKUP_TYPE} " == "aof" ];then sed -i "s|# appendonly yes|appendonly yes|g" ${REDIS_DIR} /redis.conf cp -a ${BACKUP_DIR} /appendonly.aof ${DATA_DIR} else cp -a ${BACKUP_DIR} /dump.rdb ${DATA_DIR} fi sed -i "s#/data#${DATA_DIR} #g" ${REDIS_DIR} /redis.conf ${REDIS_DIR} /bin/redis-server ${REDIS_DIR} /redis.conf ps -aux | grep redis-server echo -e "AUTH weiyigeek\nping\ninfo" | redis-cli -h 127.0.0.1 | grep -A 16 "# Keyspace" while [ $? -ne 0 ];do echo -e "AUTH weiyigeek\nping\ninfo" | redis-cli -h 127.0.0.1 | grep -A 16 "# Keyspace" done echo "数据恢复完成......" echo "完成时间: $(date +%s) "
方案3.利用Kubernetes部署的Redis集群,进行恢复原k8s集群的redis数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 #!/bin/bash if [ $# -eq 0 ];then echo -e "\e[31m[*] $0 [single|cluster] PODMATCH K8SVOLUMNDIR \e[0m" echo -e "\e[31m[*] PODMATCH : redis-cluster {异常集群 statefulset 资源对象名称} \e[0m" echo -e "\e[31m[*] K8SVOLUMNDIR : /nfsdisk-31/data {K8S 持久化跟目录} \e[0m" exit fi RECTARGET="${1} " PODMATCH="${2} " K8SVOLUMNDIR="${3} " AUTH="weiyigeek" PWD=$(pwd ) AOFNAME="appendonly.aof" DATADIR="${PWD} /database" K8SSVCNAME="database.svc.cluster.local" grep "myself,master" ${K8SVOLUMNDIR} /*${PODMATCH} -[0-9]-*/nodes.conf | head -n 3 > /tmp/nodes.log cat /tmp/nodes.log node1=$(grep "0-5460" /tmp/nodes.log | cut -d ":" -f 1) aofpath1=${node1%/*} node2=$(grep "5461-10922" /tmp/nodes.log | cut -d ":" -f 1) aofpath2=${node2%/*} node3=$(grep "10923-16383" /tmp/nodes.log | cut -d ":" -f 1) aofpath3=${node3%/*} if [ ! -f ${aofpath1} /${AOFNAME} -o ! -f ${aofpath2} /${AOFNAME} -o ! -f ${aofpath3} /${AOFNAME} ];then echo -e "\e[31m[-] ${AOFNAME} file not found \e[0m" ;exit ;fi cp ${aofpath1} /${AOFNAME} ./1.${AOFNAME} cp ${aofpath2} /${AOFNAME} ./2.${AOFNAME} cp ${aofpath3} /${AOFNAME} ./3.${AOFNAME} if [ ! -d ${DATADIR} ];then mkdir -v ${DATADIR} ;fi cat *.aof > ${DATADIR} /${AOFNAME} echo "y" | /usr/local /redis/bin/redis-check-aof --fix ${DATADIR} /${AOFNAME} ls -alh ${DATADIR} echo -e "\e[32m[*] 请验证原Redis集群Nodes信息是否无误? 请输入[Y|N] \e[0m" read flagif [ "${flag} " == "N" -o "${flag} " == "n" ];then echo -e "\e[31m[-] FAILED: 需要人工进行干预处理. \e[0m" ;exit ;fi if [ "${RECTARGET} " == "single" ];then echo -e "\e[32m[*] 正在进行异常的K8S集群 -> 单实例数据恢复! \e[0m" ./K8SRedisRecovery.sh aof ${DATADIR} return 0 else echo -e "\e[32m[*] 正在进行异常的K8S集群 -> 集群数据恢复! \e[0m" fi tee Redis-cluster-6.2.5.yaml <<'EOF' apiVersion: v1 kind: ConfigMap metadata: name: redis-cluster-recovery namespace: database data: update-node.sh: | if [ ! -f /data/nodes.conf ];then touch /data/nodes.conf;fi REDIS_NODES="/data/nodes.conf" sed -i -e "/myself/ s/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/${POD_IP} /" ${REDIS_NODES} exec "$@ " redis.conf: |+ port 6379 protected-mode no masterauth {RedisAuthPass} requirepass {RedisAuthPass} dir /data dbfilename dump.rdb rdbcompression yes no-appendfsync-on-rewrite no appendonly yes appendfilename appendonly.aof appendfsync everysec auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 128mb cluster-enabled yes cluster-config-file /data/nodes.conf cluster-node-timeout 5000 slave-read-only yes cluster-require-full-coverage no cluster-migration-barrier 1 --- apiVersion: apps/v1 kind: StatefulSet metadata: name: redis-cluster-recovery namespace: database spec: serviceName: redisclusterrecovery replicas: 6 selector: matchLabels: app: redis-cluster-recovery template: metadata: labels: app: redis-cluster-recovery spec: containers: - name: redis image: redis:6.2.5-alpine imagePullPolicy: IfNotPresent ports: - containerPort: 6379 name: client - containerPort: 16379 name: gossip command : ["/conf/update-node.sh" , "redis-server" , "/conf/redis.conf" ] env: - name: POD_IP valueFrom: fieldRef: fieldPath: status.podIP volumeMounts: - name: conf mountPath: /conf readOnly: false - name: data mountPath: /data readOnly: false - name: timezone mountPath: /etc/localtime volumes: - name: conf configMap: name: redis-cluster-recovery defaultMode: 0755 - name: timezone hostPath: path: /usr/share/zoneinfo/Asia/Shanghai volumeClaimTemplates: - metadata: name: data spec: accessModes: [ "ReadWriteOnce" ] storageClassName: "managed-nfs-storage" resources: requests: storage: 1Gi --- apiVersion: v1 kind: Service metadata: name: redisclusterrecovery namespace: database spec: clusterIP: "None" ports: - port: 6379 targetPort: 6379 name: client - port: 16379 targetPort: 16379 name: gossip selector: app: redis-cluster-recovery EOF sed -i "s#{RedisAuthPass}#${AUTH} #g" Redis-cluster-6.2.5.yaml sed "s#replicas: 6#replicas: 0#g" Redis-cluster-6.2.5.yaml > Redis-cluster-6.2.5-empty.yaml sts=$(kubectl get sts -n database | grep -c "redis-cluster-recovery" ) if [ ${sts} -eq 1 ];then kubectl apply -f Redis-cluster-6.2.5-empty.yaml; podrun=$(kubectl get pod -n database | grep -c "redis-cluster-recovery" ) while [ ${run} -ne 0 ];do podrun=$(kubectl get pod -n database | grep -c "redis-cluster-recovery" );sleep 5;echo -n .; done find ${K8SVOLUMNDIR} /database-data-redis-cluster-recovery-* -type f -delete; kubectl apply -f Redis-cluster-6.2.5.yaml run=$(kubectl get pod -n database -l app=redis-cluster-recovery | grep -c "Running" ) while [ ${run} -ne 6 ];do run=$(kubectl get pod -n database -l app=redis-cluster-recovery | grep -c "Running" );sleep 2;echo -n .; done else kubectl create --save-config -f Redis-cluster-6.2.5.yaml run=$(kubectl get pod -n database -l app=redis-cluster-recovery | grep -c "Running" ) while [ ${run} -ne 6 ];do run=$(kubectl get pod -n database -l app=redis-cluster-recovery | grep -c "Running" );sleep 2;echo -n .; done fi echo -e "\e[32m[*] Happy,redis-cluster-recovery Pod is Running....\e[0m" kubectl get pod -n database -l app=redis-cluster-recovery echo -e "yes" | redis-cli -h redisclusterrecovery.${database.svc.cluster.local} -a weiyigeek --cluster create --cluster-replicas 1 $(kubectl get pods -n database -l app=redis-cluster-recovery -o jsonpath='{range.items[*]}{.status.podIP}:6379 ' | sed "s# :6379 ##g" )redis-cli -h redis-cluster-recovery-0.redisclusterrecovery.${database.svc.cluster.local} --no-auth-warning -a weiyigeek cluster nodes | grep "master" > /tmp/rnodes.log cat /tmp/rnodes.log m1=$(grep "0-5460" /tmp/rnodes.log | cut -d " " -f 1) m2=$(grep "5461-10922" /tmp/rnodes.log | cut -d " " -f 1) m3=$(grep "10923-16383" /tmp/rnodes.log | cut -d " " -f 1) redis-cli --no-auth-warning -a weiyigeek --cluster reshard --cluster-from ${m2} --cluster-to ${m1} --cluster-slots 5462 --cluster-yes redis-cluster-recovery-0.redisclusterrecovery.${database.svc.cluster.local} :6379 > /dev/null redis-cli --no-auth-warning -a weiyigeek --cluster reshard --cluster-from ${m3} --cluster-to ${m1} --cluster-slots 5461 --cluster-yes redis-cluster-recovery-0.redisclusterrecovery.${database.svc.cluster.local} :6379 > /dev/null redis-cli -h redis-cluster-recovery-0.redisclusterrecovery.database.svc.cluster.local --no-auth-warning -a weiyigeek cluster nodes echo -e "\e[32m[*] 请验证Nodes信息是否无误? 请输入[Y|N] \e[0m" read flagif [ "${flag} " == "N" -o "${flag} " == "n" ];then echo -e "\e[31m[-] FAILED: 需要人工进行干预处理. \e[0m" ;exit ;fi kubectl apply -f Redis-cluster-6.2.5-empty.yaml; podrun=$(kubectl get pod -n database | grep -c "redis-cluster-recovery" ) while [ ${run} -ne 0 ];do podrun=$(kubectl get pod -n database | grep -c "redis-cluster-recovery" );sleep 5;echo -n .; done master=$(find /nfsdisk-31/data/*redis-cluster-recovery-0* -type d) rm -rf ${master} /*.aof ${master} */.rdb cp ${DATADIR} /appendonly.aof {${master} } kubectl apply -f Redis-cluster-6.2.5.yaml run=$(kubectl get pod -n database -l app=redis-cluster-recovery | grep -c "Running" ) while [ ${run} -ne 6 ];do run=$(kubectl get pod -n database -l app=redis-cluster-recovery | grep -c "Running" );sleep 2;echo -n .; done echo -e "\e[32m[*] Happy,redis-cluster-recovery Pod is Running....\e[0m" kubectl get pod -n database -l app=redis-cluster-recovery redis-cli -h redis-cluster-recovery-0.redisclusterrecovery.database.svc.cluster.local --no-auth-warning -a weiyigeek cluster nodes | grep "fail" > /tmp/errnodes.log for err in $(cat /tmp/errnodes.log | cut -d " " -f 1);do redis-cli -h redis-cluster-recovery-0.redisclusterrecovery.database.svc.cluster.local -a weiyigeek cluster forget ${errid} done for ip in $(kubectl get pods -n database -l app=redis-cluster-recovery -o jsonpath='{range.items[*]}{.status.podIP} ' | sed "s# :6379 ##g" );do redis-cli -h -a weiyigeek cluster meet ${ip} 6379 done redis-cli -h redisclusterrecovery.database.svc.cluster.local -a weiyigeek --cluster check redis-cluster-recovery-0.redisclusterrecovery.database.svc.cluster.local:6379 redis-cli -h redis-cluster-recovery-0.redisclusterrecovery.database.svc.cluster.local -a weiyigeek --cluster rebalance --cluster-threshold 1 --cluster-use-empty-masters redis-cluster-recovery-1.redisclusterrecovery.database.svc.cluster.local:6379 redis-cli -h redisclusterrecovery.database.svc.cluster.local -a weiyigeek --cluster check redis-cluster-recovery-0.redisclusterrecovery.database.svc.cluster.local:6379 redis-cli -h redis-cluster-recovery-0.redisclusterrecovery.database.svc.cluster.local -c -a weiyigeek info keyspace redis-cli -h redisclusterrecovery.database.svc.cluster.local -c -a weiyigeek
3.当Redis集群中出现从节点slave,fail,noaddr问题进行处理恢复流程。
Step 1.利用cluster nodes查看你集群状态,发现其中一个从节点异常(是Fail状态)。1 f86464011d9f8ec605857255c0b67cff1e794c19 :0@0 slave,fail,noaddr 2cb35944b4492748a8c739fab63a0e90a56e414a
Step 2.在问题节点上查看节点状态,发现它已脱离集群,且其ID都已发生了变化.1 2 127.0.0.1:6379> cluster nodes 0cbf44ef3f9c3a8a473bcd303644388782e5ee78 192.168.109.132:6379@16379 myself,master - 0 0 0 connected 0-5461
Tips : 若id没发生变化,直接重启下该从节点就能解决。
Step 3.但如果ID与Node IP都发生变化时,此时我们需要将该从节点剔出集群。1 2 echo 'cluster forget f86464011d9f8ec605857255c0b67cff1e794c19' | /usr/local /bin/redis-cli -a "weiyigeek"
Step 4.我们重新将该节点加入集群,此时我们只需要 在集群内任意节点上执行cluster meet
命令加入新节点,握手状态会通过信息在集群内传播,这样其他节点会自动发现新节点并发起握手流程。
1 2 3 4 5 echo 'cluster meet 192.168.109.132 6379' | /usr/local /bin/redis-cli -p 6379 -a "密码" echo 'cluster replicate 2cb35944b4492748a8c739fab63a0e90a56e414a' | /usr/local /bin/redis-cli -p 6383 -a "密码"
Step 5.最后检测集群是否恢复正常,执行如下命令即可。
1 2 3 4 echo 'cluster nodes' | /usr/local /bin/redis-cli -p 6384 -a "密码" 38287a7e715c358b5537a369646e9698a7583459 192.168.109.132:6383@16383 slave 2cb35944b4492748a8c739fab63a0e90a56e414a 0 1615233239757 8 connected 2cb35944b4492748a8c739fab63a0e90a56e414a 192.168.109.133:6383@16383 master - 0 1615233239000 8 connected 0-5461 .......