Redis 拒绝服务漏洞(CVE-2023-28856)修复处理

一、漏洞描述

Redis Labs Redis是美国Redis Labs公司的一套开源的使用ANSI C编写、支持网络、可基于内存亦可持久化的日志型、键值(Key-Value)存储数据库,并提供多种语言的API。

在这里插入图片描述

Redis 7.0.0 到 7.0.10版本、6.2.0 到 6.2.11版本、6.0.0 到 6.0.18版本存在安全漏洞,该漏洞源于对用户提供的输入的验证不充分,攻击者利用该漏洞可以使用 HINCRBYFLOAT命令创建一个无效的哈希字段,这将使 Redis 在访问时崩溃,导致合法用户不能够访问正常服务;

影响版本:

7 <= Redis < 7.0.11
6.2 <= Redis < 6.2.12
Redis < 6.0.19

现场版本:6.2.7,需要升级到6.2.13

漏洞链接: https://access.redhat.com/security/cve/cve-2023-28856

官方补丁:https://github.com/redis/redis/,https://github.com/redis/redis/pull/11149,
https://github.com/redis/redis/commit/c924ac3fdf8fe544891dc66c88018e259ee4be87

修复建议:厂商已发布了漏洞修复程序,请使用此产品的用户尽快更新至安全版本:https://redis.io/,官网安装文档

二、处理过程

2.1、升级前准备

#版本检查
redis-server -v
#介质下载
wget https://download.redis.io/releases/redis-6.2.13.tar.gz
#或
wget https://download.redis.io/redis-stable.tar.gz
#或补丁修复
#src/t_hash.c
unsigned int vlen;

    if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;
    #增加以下4行
    if (isnan(incr) || isinf(incr)) {
    
    
        addReplyError(c,"value is NaN or Infinity");
        return;
    }
    #到这里
    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
    if (hashTypeGetValue(o,c->argv[2]->ptr,&vstr,&vlen,&ll) == C_OK) {
    
    
        if (vstr) {
    
    
#tests/unit/type/hash.tcl
  assert {
    
    [r hincrbyfloat myhash float -0.1] eq {
    
    1.9}}
        }
    }
    #增加如下4行

    test {
    
    HINCRBYFLOAT does not allow NaN or Infinity} {
    
    
        assert_error "*value is NaN or Infinity*" {
    
    r hincrbyfloat hfoo field +inf}
        assert_equal 0 [r exists hfoo]
    }

在这里插入图片描述
备份:使用redis自带工具备份和导出 Redis 数据参考如下:

SAVE: 阻塞所有客户端,直到 RDB 文件创建完毕。SAVE 命令将数据集持久化到硬盘上,阻塞 Redis 服务器进程直到快照文件创建完毕。如果正在使用 AOF 持久化,SAVE 命令将被忽略。

BGSAVE: 在后台异步保存 RDB 快照。Redis 会 fork 出一个新的子进程进行快照的生成工作,当前进程继续处理新的命令请求,不会被阻塞。在自动触发RDB持久时,Redis会选择bgsave而不是save来进行持久化;在主从复制场景下,如果从节点执行全量复制操作,则主节点会执行bgsave命令,并将rdb文件发送给从节点;执行shutdown命令时,自动执行rdb持久化;redis被kill则不会触发备份机制。

#rdb和aof开启验证
#rdb开启配置
save 900 1 
save 300 10 
save 60 10000
dbfilename dump.rdb
dir ./
rdbcompression yes  #Redis采用LZF压缩方式,默认启用


#AOF开启
appendonly yes
appendfilename "appendonly.aof"
aof-load-truncated yes   #是否忽略最后一条可能存在问题的指令


#或执行:
grep -Ev "^#|^$" redis.conf|grep -Ei "aof|rdb"    //输出如下,可知现场同时开启了rdb和aof
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
rdb-del-sync-files no
appendfilename "appendonly.aof"
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes		# RDB-AOF 混合持久化模式开启了
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes

ll ./data/   //输出如下
total 44536028
-rw-r--r-- 1 root root 34872627978 Jun 28 09:32 appendonly.aof
-rw-r--r-- 1 root root 10732234315 Jun 28 09:48 dump.rdb
-rw-r--r-- 1 root root         129 Dec 23  2022 nodes-7379.conf


#备份rdb和aof文件
redis> config get dir   //确认数据文件目录
redis-cli --rdb "dump.rdb"  #方式1
redis-cli save    #方式2
redis-cli bgsave    #方式3
redis-cli save >> /data1/redis-6.2.7/dump_`date +%Y%m%d`.rdb   #方式4
cp -pr ./data/ ./data_20230808

#现场为了方便,整体redis打包备份
cd /data1/
tar -czf  redis627.tar.gz /data1/redis-6.2.7

#备份redis二进制文件
#二进制文件检查
ll /usr/local/bin/redis*   //输出如下
-rwxr-xr-x 1 root root 4830144 Dec 23  2022 /usr/local/bin/redis-benchmark
lrwxrwxrwx 1 root root      12 Dec 23  2022 /usr/local/bin/redis-check-aof -> redis-server
lrwxrwxrwx 1 root root      12 Dec 23  2022 /usr/local/bin/redis-check-rdb -> redis-server
-rwxr-xr-x 1 root root 5004264 Dec 23  2022 /usr/local/bin/redis-cli
lrwxrwxrwx 1 root root      12 Dec 23  2022 /usr/local/bin/redis-sentinel -> redis-server
-rwxr-xr-x 1 root root 9536000 Dec 23  2022 /usr/local/bin/redis-server
-rwxr-xr-x 1 root root    3600 Dec 23  2022 /usr/local/bin/redis-trib.rb
mv redis-server redis-server_6.2.7
mkdir -p /data1/redis_backup
mv /usr/local/bin/redis* /data1/redis_backup

2.2、编译安装

#关停redis
redis-cli shutdown
#新的redis目录
cd /data1
tar -xzvf redis-6.2.13.tar.gz
cd redis-6.2.13
make
cp src/redis-benchmark  /usr/local/bin/
cp src/redis-check-aof  /usr/local/bin/    #注意软连接
cp src/redis-check-dump  /usr/local/bin/
cp src/redis-cli  /usr/local/bin/
cp src/redis-sentinel  /usr/local/bin/    #注意软连接
cp src/redis-server  /usr/local/bin/
cp src/redis-check-rdb /usr/local/bin/   #注意软连接
cp src/redis-trib.rb /usr/local/bin/
//二进制替换旧不用执行如下安装命令了
make install   

chmod 755 /usr/local/bin/redis*

#二进制文件替换

#重启服务
mv /data1/redis-6.2.7/ /data1/redis-6.2.13/
/usr/local/redis-server /data1/redis-6.2.13/redis.conf

注:启动新版本的 Redis 服务后,需要将原来的 Redis 数据导入到新版本的 Redis 中。可以使用 Redis 的数据同步命令,将原来的数据同步到新的 Redis 中。Redis 数据同步命令常用的有两个:

slaveof:将 Redis 服务设置为主从模式,将原来的 Redis 服务设置为主服务器,新版本的 Redis 服务设置为从服务器。

sync:将从服务器上的数据库与主服务器进行同步。 示例命令:

redis-cli -h oldredis_ip -p oldredis_port slaveof newredis_ip 6380
redis-cli sync //新版本中执行同步命令
redis-benchmark -h newredis_ip -p 6380 -n 10000 -c 5 //同步完成后,测试确保数据完整性和服务可用性

2.3、验证

#连接 redis 客户端:
./redis-cli -p 6379 -h ip
#查看版本命令
ip:port>info server  //输出版本
#或
./redis-cli -p 6379 -h ip info 
#或
redis-cli --version
redis-server--version

2.4、平滑升级

某些场景因Redis无法停止服务,为避免造成数据丢失,需要采用平滑升级办法用新版替代redis完成升级。整体步骤如下:

1)下载编译安装启动一个6.2.13版的redis。
2)配置文件修改,作为slave进行配置。
3)启动slave,并做好数据测试。
4)将原来程序的redis读写迁移到新的slave上。
5) 升级原来的版本到2.6.14

迁移切换步骤 切换场景描述
升级前 生产主机 , redis6.2.7 , 单redis实例a
升级中 生产主机 , redis 6.2.7 ,redis master实例a
生产主机, redis6.2.13 ,redis slave实例b
升级后 生产主机 , redis6.2.13, redis slave实例a
生产主机 , redis 6.2.13, redis master实例b
#第一部分:确认当前持久化方式,端口,数据量
//参考上文,本次涉及数据达亿级


#第二部分:在同主机上创建修复版本 Redis实例
mkdir -p /data1/redis-6.2.13/{
    
    bin,data,logs,etc}
//安装
wget https://download.redis.io/releases/redis-6.2.13.tar.gz
tar -xzvf redis-6.2.13.tar.gz
cd redis-6.2.13
make     //这里不安装,只生成二进制文件
cp src/redis-benchmark  /usr/local/bin/
cp src/redis-check-aof  /usr/local/bin/    #注意软连接
cp src/redis-check-dump  /usr/local/bin/
cp src/redis-cli  /usr/local/bin/
cp src/redis-sentinel  /usr/local/bin/    #注意软连接
cp src/redis-server  /usr/local/bin/
cp src/redis-check-rdb /usr/local/bin/   #注意软连接
cp src/redis-trib.rb /usr/local/bin/

#验证
 ls ./bin/
total 46932
drwxr-x--- 2 root root    4096 Aug  9 01:09 .
drwxr-xr-x 7 root root    4096 Aug  9 01:06 ..
-rwxr-x--- 1 root root 4830432 Aug  9 01:05 redis-benchmark
-rwxr-xr-x 1 root root 9547992 Aug  9 01:05 redis-check-aof
-rwxr-xr-x 1 root root 9547992 Aug  9 01:05 redis-check-rdb
-rwxr-x--- 1 root root 5004544 Aug  9 01:05 redis-cli
-rwxr-xr-x 1 root root 9547992 Aug  9 01:05 redis-sentinel
-rwxr-x--- 1 root root 9547992 Aug  9 01:05 redis-server
-rwxrwxr-x 1 root root    3600 Jul 10 19:37 redis-trib.rb

#配置文件
less /data1/redis/etc/redis.conf,内容如下:
daemonize yes
pidfile /data1/redis/redis.pid
port 6379
bind 192.168.1.229
timeout 300
loglevel warning
logfile /data1/redis/logs/redis.log
databases 16
save 900 1
save 300 10
save 60 10000
rdbcompression yes
dbfilename /data1/redis/data/redis.rdb
dir /data1/redis/data/
replicaof 172.18.1.151 7379  //注意这2行,之前为slaveof masterip port,指定同步的master
replica-serve-stale-data yes   //之前为slave-serve-stale-data
maxmemory 128mb
appendonly no
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
slowlog-log-slower-than 10000
slowlog-max-len 1024
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
activerehashing yes
masterauth 123456

#启动 slave,同步数据
/data1/redis/bin/redis-server /data1/redis/etc/redis.conf   //现场竟然启动了集群模式 cluster-enabled yes
*** FATAL CONFIG FILE ERROR (Redis 6.2.13) ***
Reading the configuration file, at line 480
>>> 'replicaof 172.18.1.151 7379'
replicaof directive not allowed in cluster mode


#本地客户端登录查看
./bin/redis-cli -h 172.18.1.146 -p 7379
172.18.1.146:7379> info
# Server
redis_version:6.2.13
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:b3aa13c3700e5b79
redis_mode:standalone
os:Linux 3.10.0-862.el7.x86_64 x86_64
arch_bits:64
monotonic_clock:POSIX clock_gettime
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:4.8.5
process_id:11745
process_supervised:no
run_id:13031ec1eec8b67db06ebfa2c1fbcc64a4335714
tcp_port:7379
server_time_usec:1691517037070105
uptime_in_seconds:167
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:13795436
executable:/data1/redis/bin/redis-server
config_file:/data1/redis/etc/redis.conf
io_threads_active:0

# Clients
connected_clients:1
cluster_connections:0
maxclients:10000
client_recent_max_input_buffer:24
client_recent_max_output_buffer:0
blocked_clients:0
tracking_clients:0
clients_in_timeout_table:0

# Memory
used_memory:875952
used_memory_human:855.42K
used_memory_rss:9945088
used_memory_rss_human:9.48M
used_memory_peak:875952
used_memory_peak_human:855.42K
used_memory_peak_perc:100.20%
used_memory_overhead:832632
used_memory_startup:812120
used_memory_dataset:43320
used_memory_dataset_perc:67.87%
allocator_allocated:904984
allocator_active:1187840
allocator_resident:4681728
total_system_memory:67386519552
total_system_memory_human:62.76G
used_memory_lua:30720
used_memory_lua_human:30.00K
used_memory_scripts:0
used_memory_scripts_human:0B
number_of_cached_scripts:0
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
allocator_frag_ratio:1.31
allocator_frag_bytes:282856
allocator_rss_ratio:3.94
allocator_rss_bytes:3493888
rss_overhead_ratio:2.12
rss_overhead_bytes:5263360
mem_fragmentation_ratio:11.94
mem_fragmentation_bytes:9111896
mem_not_counted_for_evict:4
mem_replication_backlog:0
mem_clients_slaves:0
mem_clients_normal:20504
mem_aof_buffer:8
mem_allocator:jemalloc-5.1.0
active_defrag_running:0
lazyfree_pending_objects:0
lazyfreed_objects:0

# Persistence
loading:0
current_cow_size:0
current_cow_size_age:0
current_fork_perc:0.00
current_save_keys_processed:0
current_save_keys_total:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1691516870
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
rdb_last_cow_size:0
aof_enabled:1
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_last_cow_size:0
module_fork_in_progress:0
module_fork_last_cow_size:0
aof_current_size:0
aof_base_size:0
aof_pending_rewrite:0
aof_buffer_length:0
aof_rewrite_buffer_length:0
aof_pending_bio_fsync:0
aof_delayed_fsync:0

# Stats
total_connections_received:2
total_commands_processed:1
instantaneous_ops_per_sec:0
total_net_input_bytes:81
total_net_output_bytes:73
instantaneous_input_kbps:0.00
instantaneous_output_kbps:0.00
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
expired_stale_perc:0.00
expired_time_cap_reached_count:0
expire_cycle_cpu_milliseconds:0
evicted_keys:0
keyspace_hits:0
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:0
total_forks:0
migrate_cached_sockets:0
slave_expires_tracked_keys:0
active_defrag_hits:0
active_defrag_misses:0
active_defrag_key_hits:0
active_defrag_key_misses:0
tracking_total_keys:0
tracking_total_items:0
tracking_total_prefixes:0
unexpected_error_replies:0
total_error_replies:2
dump_payload_sanitizations:0
total_reads_processed:5
total_writes_processed:3
io_threaded_reads_processed:0
io_threaded_writes_processed:0

# Replication
role:slave
master_host:172.18.1.151
master_port:7379
master_link_status:down			 //这里异常,应该是生产环境启动了集群模式,且配置了密码认证
master_last_io_seconds_ago:-1     //这里正常为正数
master_sync_in_progress:0
slave_read_repl_offset:1
slave_repl_offset:1
master_link_down_since_seconds:-1
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:34181e63a9534606ff4f2db893db1880bacc97b0
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

# CPU
used_cpu_sys:7.382855
used_cpu_user:2.753302
used_cpu_sys_children:0.000000
used_cpu_user_children:0.000000
used_cpu_sys_main_thread:7.026060
used_cpu_user_main_thread:2.675756

# Modules

# Errorstats
errorstat_NOAUTH:count=2

# Cluster
cluster_enabled:0

# Keyspace

#等待一会儿再执行,这时就会看到状态up
>172.18.1.146:7379> info replication
# Replication
role:slave
master_host:172.18.1.151
master_port:7379
master_link_status:up		//这时已经up了
master_last_io_seconds_ago:2  
master_sync_in_progress:0
slave_read_repl_offset:2800
slave_repl_offset:2800
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:dfd252edeaaf6d7c983e17abd77d6e9a5217a9ff
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:2800
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:519
repl_backlog_histlen:2282

#主库上查看
172.18.1.151:7379> info  replication
# Replication
role:master
connected_slaves:1
slave0:ip=172.18.1.146,port=7379,state=online,offset=3668,lag=1
master_failover_state:no-failover
master_replid:dfd252edeaaf6d7c983e17abd77d6e9a5217a9ff
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:3668
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:3668


redis-cli -h 172.18.1.146 -p 7002 -a admin info replication | grep 'repl_offset'  #保障主从节点偏移量一致,就是同步了

#配置slave读写,可视情况将业务临时切换到slave上
./bin/redis-cli -h 172.18.1.146 -p 7379 -a admin config set slave-read-only no  //或
172.18.1.146:7379> CONFIG set slave-read-only no
OK
172.18.1.146:7379> CONFIG get slave-read-only
1) "slave-read-only"
2) "no"

#主库上升级redis,只make
wget https://download.redis.io/releases/redis-6.2.13.tar.gz
tar -xzvf redis-6.2.13.tar.gz
cd redis-6.2.13
make     //这里不安装,只生成二进制文件

#备份AOF文件,rdb文件已同步到slave
cp -pr ./appendonly.aof ./appendonly.aof.20230809
172.18.1.51:7379> bgsave  #落盘
Background saving started
(0.71s)

#slave关停同步,避免主库在未完成版本升级前就重启,进而直接覆盖掉从库上的数据,导致所有的数据丢失。
172.18.1.146:7379> SLAVEOF NO ONE

#生产master执行shutdown, 默认采用shutdown save模式
172.18.1.151:7379> shutdown
(320.60s)

#redis二进制文件替代
mkdir /usr/local/bin/redsi-bak
mv /usr/local/bin/redis-* /usr/local/bin/redsi-bak/
cp -pr ./redis-benchmark /usr/local/bin/
cp -pr ./redis-check-aof /usr/local/bin/
cp -pr ./redis-check-rdb /usr/local/bin/
cp -pr ./redis-cli /usr/local/bin/
cp -pr ./redis-sentinel /usr/local/bin/
cp -pr ./redis-server /usr/local/bin/
cp -pr ./redis-trib.rb /usr/local/bin/

#重启生产库
/usr/local/bin/redis-server /data1/redis-6.2.7/redis.conf

#登录验证
usr/local/bin/redis-cli  -h 172.18.1.51 -p 7379 --raw
172.18.1.151:7379> info
# Server
redis_version:6.2.13
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:2d6fc318bc558dd0
redis_mode:cluster
os:Linux 3.10.0-862.el7.x86_64 x86_64
arch_bits:64
monotonic_clock:POSIX clock_gettime
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:4.8.5


另平滑升级可参考:平滑升级;cluster模式参考
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

当我们重启Redis时,如何保证数据的完整性呢?redis 在启动时会加载 dump.rdb 文件,可删除,redis启动就会读取AOF文件(只许追加文件但不可以改写文件)重新构建数据,即redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作,启动中我们常会遇到 ‘LOADING Redis is loading the dataset in memory’ 的提示。这是由于 Redis 在加载数据到内存时所发出的提示。AOF 持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。 Redis 还可以在后台对 AOF 文件进行重写(rewrite),使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小。Redis 还可以同时使用 AOF 持久化和 RDB 持久化。 在这种情况下, 当 Redis 重启时, 它会优先使用 AOF 文件来还原数据集, 因为 AOF 文件保存的数据集通常比 RDB 文件所保存的数据集更完整。

Redis提供了两个配置参数:appendonly和rdbchecksum。前者可以开启AOF持久化机制,让Redis在关闭后自动把操作日志转储到硬盘上;后者则会在快照数据中添加校验和,确保恢复的数据完整性。另外,我们还可以使用Redis提供的redis-check-aof和redis-check-dump工具,来进行AOF文件和RDB文件的检查和恢复。Redis关闭后有一定的数据丢失可能性,但是通过使用Redis的持久化机制,可以将数据存储到硬盘中,确保Redis关闭后数据不会丢失。此外,在Redis重启后,也要特别注意数据恢复的问题,使用Redis提供的工具进行文件检查和恢复,保证数据的完整性。

三、附录

3.1、Redis持久化

Redis将数据存储在内存中,宕机或重启都会使内存数据全部丢失, Redis的持久化机制用来保证数据不会因为故障而丢失。Redis提供两种持久化方式:

RDB(内存快照方式):生成rdb文件,rdb是某一时间点内存数据的全量备份,文件内容是存储结构非常紧凑的二进制序列化形式;主要用于:缓存和数据迁移备份场景
优点:
全量快照,数据与内存基本是一致的。
占用空间相对比较小。
快照是二进制文件,生成、恢复都比较快。
缺点:
丢失数据的概率相对有点大。注意:SHUTDOWN 和 FLUSHALL (清空)命令都会触发RDB快照
配置参考:
语法:save <seconds> <changes>

#save “”

save 900 1
save 300 10
save 60 10000
dbfilename dump.rdb
dir ./
rdbcompression yes #Redis采用LZF压缩方式

AOF(日志备份方式) :日志保存的是基于数据的连续增量备份,日志文件内容是服务端执行的每一条指令的记录文本。

解发机制:
always:内存更新一条,即往磁盘追加一条。
everysec:每1s往磁盘追加一次。
no:同步的操作交给操作系统来完成。
优点:
直接追加,同步开销相对较小,可根据需求自由选择安全和性能。
.缺点:
日志文件是普通文本文件,恢复相对较慢。此外,日志文件中会包含很多无用操作。

当 aof-use-rdb-preamble 设置为 yes 时,表示开启 RDB-AOF 混合持久化模式;在该模式下,AOF 重写产生的文件将同时包含 RDB 格式的内容和 AOF 格式的内容,该文件的前半段是 RDB 格式的全量数据,而后半段是 Redis 命令格式的增量数据;

Redis持久化策略选用参考:

如果数据不能丢失,RDB和AOF混用。---->常用
如果只作为缓存使用,可以接受部分数据的丢失,可以使用RDB。
如果只使用AOF,优先使用everysec的写回策略。

在这里插入图片描述

3.2、创建redis自启动服务

vi /etc/systemd/system/[email protected]  //参考如下
[Unit]
Description=The redis-server Process lanager
After=syslog. target
After"network. target
[Service]
Typeforking
ExecStart=/usr/1ocal/bin/redis-server /data1/redis-6.2.13/redis.conf
ExecStop=/usr/1ocal/bin/redis-cli -p%i shutdown
Restart=always
PrivateTmptrue
[Instal1]
WantedBy=multi-user.target

#配置
systemctl enable --now redis@{
    
    7000, 7001}.service
systemctl status [email protected]
systemctl start [email protected]

3.3、Redis升级脚本

#!/bin/bash

# 备份 Redis 数据
echo "Backup Redis data..."
redis-cli bgsave
cd /data1
redis-cli shutdown
mv /data1/redis-6.2.7 /data1/redis-back_`date +%Y%m%d`
# 下载 Redis 新版本
echo "Downloading Redis new version..."
wget -q https://download.redis.io/releases/redis-6.2.13.tar.gz

# 解压 Redis 安装包
echo "Extracting Redis package..."
tar xzf redis-6.2.13.tar.gz
cd redis-6.2.13

# 编译 Redis
echo "Compiling Redis..."
make

# 安装 Redis
echo "Installing Redis..."
make install

# 启动新版本 Redis
echo "Starting Redis new version..."
redis-server /path/to/new/redis.conf --port 6380 &amp;

# 数据同步
echo "Synchronizing Redis data..."
redis-cli -h old_redis_host -p old_redis_port slaveof new_redis_host 6380
redis-cli sync

# 测试 Redis
echo "Testing Redis..."
redis-benchmark -h new_redis_host -p 6380 -n 10000 -c 5

3.4、备份脚本

#!/bin/bash
                                                                    
###-------------版本变更记录-------------------
###  日期          描述              作者
###--------------------------------------------
###  2023-01-27    Redis备份         麦龙飞

###使用方法是:sh ./redis-backup.sh 60 15
###参数1:如果bgsave命令在60个时间单位内没完成,就是会备份失败。一个时间单位是5s
###参数2:备份文件最少保留15天

if [[ -z $1 ]]; then
    bgsave_time=60
else
    bgsave_time=$1
fi

if [[ -z $2 ]]; then
    backup_keepday=15
else
    backup_keepday=$2
fi

#获取当前时间-秒
backup_time=$(date "+%F %T")
#获取当前时间-天
backup_day=$(date +"%Y-%m-%d")

#redis安装目录
apps_dir=/usr/local
#redis数据备份的目录
redis_backup_dir=/data/redis-backup
#redis端口列表
redis_port="[7001, 7002]"
port_list=$(echo "${redis_port}" | grep -Eo "[0-9]{0,6}")
#机器IP地址
ip_address=10.1.1.11

#备份配置文件
redis_conf_backup() {
    
    
    cp ${redis_home_dir}/conf/*.conf ${redis_backup_dir_today}
}

#备份数据持久化文件
redis_data_backup() {
    
    
    #持久化数据
    ${redis_cli_command} bgsave > /dev/null 2>&1
    for ((i=1;i<=${bgsave_time};i++)); do
        #判断bgsave命令是否已经执行完成
        bgsave_status=$(${
     
     redis_cli_command} info 2> /dev/null | grep -o "rdb_bgsave_in_progress:0")
        if [[ -n ${bgsave_status} ]]; then
            cp ${redis_home_dir}/data/appendonly.aof ${redis_backup_dir_today}
            cp ${redis_home_dir}/data/dump.rdb ${redis_backup_dir_today}
            echo "${backup_time} ${redis_home_dir}实例使用${i}个时间单位备份成功" >> ${redis_backup_dir}/redis-backup.log
            break
        fi

        sleep 5

        #如果在指定时间单位内bgsave命令没有完成,就会备份失败
        if [[ ${i} -eq ${bgsave_time} ]]; then
            echo "${backup_time} ${redis_home_dir}实例在${i}个时间单位内没有运行完bgsave,备份失败" >> ${redis_backup_dir}/redis-backup.log
        fi
    done
}

#管理备份文件
management_backup() {
    
    
    cd ${redis_backup_dir}/${backup_day}
    #压缩当天的备份文件
    tar zfc ${1}.tar.gz ${1} --remove-files

    #清理备份文件(清理相应的端口文件,清理空目录)
    find ${redis_backup_dir}/* -mtime +${backup_keepday} \( \( -type f -iname "${1}*" \) -o \( -type d \) \) -delete
}

#遍历所有端口(reids实例)
for port in ${port_list}; do
    redis_home_dir=${apps_dir}/redis-${port}
    redis_passwd=$(grep "^\s*requirepass" ${
     
     redis_home_dir}/conf/redis-${
     
     port}.conf | awk '{print $2}')
    redis_cli_command="${redis_home_dir}/bin/redis-cli -h ${ip_address} -p ${port} -a ${redis_passwd}"

    #判断reids实例是否是master,如果是则进行备份
    node_status=$(${
     
     redis_cli_command} info 2> /dev/null | grep -o "role:master")
    if [[ -n ${node_status} ]]; then
        #创建备份目录
        redis_backup_dir_today="${redis_backup_dir}/${backup_day}/${port}"
        if [[ ! -d ${redis_backup_dir_today} ]]; then
            mkdir -p ${redis_backup_dir_today}
        fi

        #备份配置文件
        redis_conf_backup
        #备份数据持久化文件
        redis_data_backup
        #管理备份文件(如果备份持久化文件失败,则不清理相应的持久化文件)
        if [[ -f ${redis_backup_dir_today}/dump.rdb ]]; then
            management_backup ${port}
        fi
    fi
done

3.5、Redis几个重要的指标

【0】慢日志:①、通过命令[slowlog get]得到 Redis 执行的 slowlog 集合,理想情况下slowlog 集合应该为空即没有任何慢日志。②、若发现有慢命令,则应该逐个分析是否正常,是否需要优化。
【1】存活情况:是最重要的健康指标,通过命令 PING 的响应是否为 PONG 来判断。
【2】连接数:①、连接的客户端数量,[redis-cli info Clients|grep connected_clients] 得到,这个值与使用 Redis 服务的连接池配置关系比较大,这个值如果很大,需要排查问题原因。②、另外还有一个拒绝连接数(rejected_connections)也需要关注,这个值理想状态是 0 。如果大于 0,说明创建的连接数大于 maxclients,需要排查原因。是 Redis 连接池配合不合理还是连接这个Redis 的服务过多。
【3】阻塞客户端数量:blocked_clients 通常是执行了 list 数据类型的 BLPOP 或者 BRPOP 命令引起的,可以通过[redis-cli info Clients|grep blocked_clients]得到,这个值最应该=0。
【4】使用内存峰值:①、监控 Redis 内存使用峰值,可以通过命令[config set maxmemory]设置允许使用的最大内存。②、为了防止发生 swap 导致 Redis 性能骤减,甚至由于使用内存超标导致系统kill,建议used_memory_peak(占用内存的峰值)的值与maxmemory 的值有个安全区间,例如1G。
【5】内存碎片率:

①、mem_fragmentation_ratio = used_memory_rss/use_memory 当值大于1时,表示分配的内存超过实际使用的内存,数值越大[利用率不高],碎片率越严重。当这个值小于1时,表示发生了 swap(Swap分区在系统的物理内存不够用的时候,把物理内存中的一部分空间释放出来,以供当前运行的程序使用) ,既可用内存不足。
②、Redis4.0 有一个主要的特性就是优化内存碎片率问题(Memory defragmentation)。在redis.conf 配置文件中[ACTIVE DEFRAGMENTATION:碎片整理允许 Redis 压缩内存空间,从而回收内存。此特性默认是关闭的,可以通过命令CONFIG SET activedefrag yes 热启动这个特性]。
③、当内存使用量(use_memory)很小的时候,这个值参考价值不大。所以建议 used_memory 至少 1G 以上才考虑对内存碎片率进行监控。
【6】缓存命中率:①、keyspace_misses/keyspace_hits 这两个指标用来统计缓存的命中率,keyspace_misses值未命中次数,keyspace_hits 表示命令次数。keyspace_hits/(keyspace_misses+keyspace_hits) 就是缓存命中率。②、如果缓存命中率过低,那么要排查对缓存的用法是否有问题。
【7】OPS:instantaneous_pos_per_sec 表示缓存的OPS(operation per second 每秒操作次数)
【8】持久化:①、rdb_last_bgsave_status/aof_last_bgrewrite_status,即最近一次或者最后一次 RDB/AOF 持久化是否有问题,这两个值都应该时 “OK”。②、由于 Redis 持久化时会 fork 子进程,且 fork 是一个完全阻塞的过程,所以可以监控 fork 耗时即:latest_fork_usec,单位是微妙,如果这个值比较大会影响业务,甚至出现 timeout 。
【9】失效KEY:如果把 Redis 当缓存使用,那么建议所有的 key 都设置 expire 属性,通过命令 redis-cli info Keyspace 得到每个 db 中 key 的数量和设置了 expire属性的 key 的属性,且 expires(表示设置了超时时间的key个数) 需要等于 keys。

猜你喜欢

转载自blog.csdn.net/ximenjianxue/article/details/132149019
今日推荐