Redis持久化¶
一. 持久化概述¶
- Replication复制
- Sentinel哨兵
- Cluster集群
- Dump同步
- Cache缓存
默认情况下,redis工作室的所有数据都是存储在内存中的,不论是否又磁盘上的持久化数据,都是工作于内存当中。redis本身就是一个内存的数据库。如果redis崩溃或断电导致所有数据丢失。所以redis提供了持久化功能来保证数据的可靠性。
Redis持久化有两种实现:RDB和AOF
二. 两种持久化¶
1. RDB¶
RDB持久化是指在指定时间间隔将内存中的数据集快照写入磁盘。实际操作过程就是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件用二进制压缩存储。

RDB持久化是默认启动的持久化机制;按事先定制的策略周期性的将数据保存至磁盘,数据文件默认为dump.rdb
RDB持久化有两种方式
Ⅰ. 借助于配置文件所定义的save和策略进行保存
Ⅱ. 客户端使用SAVE或BGSAVE命令启动快照保存机制
SAVE命令:是同步保存,在客户端使用save保存快照时,是在redis主线程中保存快照;因为redis的主线程是用于处理请求的,所以此时会阻塞所有客户端的请求,每次的保存快照都是把内存中的数据完整保存一份,并非是增量的,如果内存中的数据比较大,而还有大量的写操作请求时,此方式会引起大量的I/O,会导致redis性能下降
BGSAVE命令:异步方式,将立即返回结果,但自动在后台保持操作,所以BGSAVE命令启动后,前台不会被占用,客户端的请求是不会被阻塞(主进程不会被阻塞)
如果是在配置文件中定义的BGSAVE,那么redis在持久化的时候,则会开启另外的进程去处理,不会阻塞redis的主进程
如果是在配置文件中定义的BGSAVE,那么redis在持久化的时候,则会开启另外的进程去处理,不会阻塞redis的主进程
redis的RDB持久化不足之处则是,一旦数据出现问题,由于RDB的数据不是最新的,所以基于RDB恢复过来的数据一定会有一部分数据丢失,也就是RDB保存之后的修改的数据会丢失
2. AOF¶
AOF:Append Only File,持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的记录方式可以打开文件看到详细的操作记录。AOF类似于MySQL的二进制日志,记录每一次redis的写操作命令,以顺序IO的方式附加在指定文件的尾部,时使用追加的方式实现,这也叫做一种附加日志类型的持久化机制,由于每一次操作都记录,则会随着时间长而增大文件的容量,AOF不像RDB,RDB是保存数据集本身。

3. 两者的优缺点¶
RDB的优势
1). 采用RDB方式,那么你的整个Redis数据库只包含一个文件,这对于文件备份而言是非常方便的,因为我们可以非常轻松的将一个单独的文件压缩后在转移到其他存储介质上
2). 性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的就是fork出子进程,之后再有子进程完成这些持久化的工作,这样就可以极大地避免服务进程执行IO操作了
3). 相比AOF机制,如果数据量很大,RDB的启动效率会更高。
RDB的劣势
1). 如果你想要保证数据的高可用性,及最大限度地避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失
2). 由于RDB是通过fork子进程来协助完成数据持久化工作,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是一秒钟
AOF的优势
1). AOF的机制可以带来更高的数据安全性,即数据持久性。Redis中提供了三种同步策略,即每秒同步、每修改同步和不同步
事实上,对于每秒同步,一旦系统出现宕机现象,那么这么一秒钟之内修改的数据将会丢失
而每修改同步,即每当发生数据变化是,都会被立即记录到磁盘中。至于不同步,无需多言,我想大家都能正确的理解它。
2). 由于AOF机制对日志文件的羞辱操作采用的是append(附加模式),因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃的问题也不用担心。在redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题
3). 如果日志过大,Redis可以自动启动rewrite机制。即Redis以append模式不断地将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。
4). AOF包含一个1格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。
AOF的劣势
1). 对于相同数量的数据集而言,AOF文件通常大于RDB文件。RDB在恢复大数据集时的速度比AOF的恢复速度要快。
2).根据同步策略的不同,AOF在运行效率上往往会慢于RDB
4. 配置文件中于RDB相关的参数¶
stop-writes-on-bgsave-error yes //在进行快照备份时,一旦发生错误是否停止写入操作
rdbcompression yes //RDB文件是否使用压缩,使用压缩会消耗CPU
rdbchecksum yes
//是否对RDB文件做校验码检测,此项定义在redis启动时加载RDB文件是否对文件检查校验码,在redis生成RDB文件是会乘车校验信息,在redis再次启动或装在RDB文件时,是否检测校验信息。如果检测的情况下会消耗时间,会导致redis启动时慢,但是能够判断RDB文件是否产生错误
dbfilename dump.rdb //定义RDB文件名称
<span style="color:blue">dir /redis/data //定义RDB文件存放的目录路径</span>
requirepass 123456 //设置密码
#关闭redis。这里注意设置完密码后关闭redis需要携带密码
[root@1 ~]# redis-cli -a 123456 shutdown
#启动redis服务
[root@1 ~]# redis-server /usr/local/redis/etc/redis.conf
#进入数据库
[root@1 ~]# redis-cli -a 123456
127.0.0.1:6379> config get dir
1) "dir"
2) "/redis/data"
#设置成功
5. 配置文件中于AOF相关的参数¶
appendonly yes //定义是否开启AOF功能,默认为no,<span style="color:red">启用修改为yes</span>
appendfilename "appendonly.aof" //定义AOF文件
appenddirname "appendonlydir"
appendfsync always
//标识每次收到命令时立即写入磁盘上的AOF文件。虽然时最好的持久化功能,但是每次写命令时候都会有磁盘I/O操作,容易影响redis性能
appendfsync everysec //表示每秒钟写一次,不管每秒钟收到多少个写请求都往磁盘的AOF文件写入一次
appendfsync no //表示append功能不会触发写操作,所有的写操作都是提交给OS,有OS自行觉得如何写的
no-appendfsync-on-rewrite no //当此项为yes时,表示在重写时对于新的写操作不同步,而是暂存在内存中。
auto-aof-rewrite-percentage 100 //表示当前AOF文件的大小是上次重写AOF文件的2倍时,自动日志重写过程
auto-aof-rewrite-min-size 64mb //定义AOF文件重写过程的条件,最少为定义大小则触发重写过程
## 注意:持久化本身不能取代备份。还应该制定备份策略,对redis数据库定期进行备份
6. RDB与AOF同时启用时需注意¶
(1). BGSAVE和BGREWRITEAOF不会同时执行,为了避免对磁盘的I/O影响过大,在某一时刻只会允许一者执行
如果BGSAVE在执行时,用户手动执行BGREWRITEAOF。redis会立即返回OK,但是redis不会同时执行。会等BGSAVE执行完成再执行BGREWRITEAOF
(2)在REDIS服务器启动用于恢复数据时,会优先使用AOF
7. 数据恢复¶
每次redis重启都会去读取相对于的文件,如果误删除可以用备份文件直接替换原来的文件,dump.rdb或者appendonly.aof。
三. redis主从架构¶
1. 复制的工作原理¶
主库会基于ping check 方式检查从库是否在线,如果在线则直接同步数据文件至从服务端,从服务端也可以主动发送同步请求到主服务端。主库如果是启动了持久化功能时,会不断的同步数据到磁盘上。主库一旦受到从库的同步请求后,主库会将内存中的数据做快照,然后把数据文件同步给从库。从库得到以后是保存在本地文件(磁盘),而后则把该文件装在大内存中完成数据重建。
a. 启动redis slave
b. slave会向master发送同步命令,请求主库上的数据。不论从时第一次连接,还是非第一次连接,master此时都会启动一个后台的子进程将数据快照保存在数据文件中,然后把数据文件发送给slave
c. slave收到数据文件以后保存到本地,而后把文件重载装入内存
主从工作原理¶
1). 从库发起同步请求
2). 主库收到请求后执行bgsave保存当前内存中的数据到磁盘
3). 主库将持久化的数据发送给从库的数据目录
4). 从库收到主库的持久化数据后,先清空自己当前内存中的所有数据
5). 从库将主库发送过来的持久化文件加载到自己的内存中
2. 特点¶
1). 一个Master可以有多个Slave
2). 支持链式复制(一个slave也可以是其他slave的slave)
3). Master以非阻塞的方式同步数据至slave(master可以通过处理多个slave的读写请求,slave端在同步数据时也可以使用非阻塞方式)
3. 启动复制功能¶
a. 在第二台机器上安装redis
mount /dev/sr0 /media/cdrom
cat <<EOF > /etc/yum.repos.d/redis.repo
[Redis]
name=Redis
baseurl=http://packages.redis.io/rpm/rockylinux9
enabled=1
gpgcheck=1
EOF
dnf clean all&&dnf makecache
curl -fsSL https://packages.redis.io/gpg > /tmp/redis.key
sudo rpm --import /tmp/redis.key
sudo yum -y install redis
cat <<EOF >> /etc/security/limits.conf
* soft nofile 10032
* hard nofile 10032
EOF
cat <<EOF >> /etc/sysctl.conf
net.core.somaxconn = 511
vm.overcommit_memory = 1
EOF
b. 配置slave的主
在3和4中复制如下脚本
IP_host=$(ip a s ens160|grep global|awk -F'[/ ]' '{print $2}'|cut -d/ -f1)
sed -i "/^bind/ s/$/ $IP_host/" /etc/redis/redis.conf
cat <<EOF >> /etc/redis/redis.conf
requirepass 123456
replicaof 192.168.100.99 6379
#指定master IP,端口
masterauth 123456
#指定主redis连接认证密码
EOF
systemctl restart redis
c. 查看主从信息
[root@3 ~]# redis-cli -h 192.168.100.101 -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.100.101:6379> info replication
# Replication
role:slave
master_host:192.168.100.99
master_port:6379
master_link_status:up
master_last_io_seconds_ago:2
master_sync_in_progress:0
slave_read_repl_offset:182
slave_repl_offset:182
replica_full_sync_buffer_size:0
replica_full_sync_buffer_peak:0
master_current_sync_attempts:5
master_total_sync_attempts:363
master_link_up_since_seconds:102
master_client_io_thread:0
total_disconnect_time_sec:3
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:5191c52cc7a483cd3d8d0d7d4f5937b1183279da
master_replid2:a30b1206f0e01280e4d84d7692d930f63711b0b2
master_repl_offset:182
second_repl_offset:43
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:182
4. 测试¶
a. 在1上插入数据
[root@1 ~]# redis-cli -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> set test aa
OK
127.0.0.1:6379> get test
"aa"
b. 到3上查看
四. 使用sentinel实现主从架构高可用¶
redis主从切换技术的方法时:当主服务器宕机后,需要手动把一台服务器切换为主服务器,这就需要人工干预,费时费力,还会造成一段时间内服务器不可用。
这不是一种推荐的方式。更多时候我们会优先考虑哨兵模式
1. sentinel的工作过程¶
sentinel 即可以部署在另外的主机上也可以部署在redis主机上,sentinel既能监控又能提供配置功能,向sentinel指明主redis服务器即可(仅监控主服务器),sentinel 可以从主服务中获取主从架构信息,并分辨从节点。sentinel可以监控当前整个主从服务器架构的工作状态,一旦发现master离线的情况,sentinel会从多个从服务器中选择并提升一个从节点成为主节点,当主节点被从节点取代以后,那么IP地址则发生了变化,客户之前连接的主节点IP无法连接,此时可以向sentinel发起查询请求,sentinel会告知客户端新的主节点的IP,所以sentinel是redis在主从架构中实现高可用的解决方案。为了防止sentinel误判和单点故障,sentinel也应该部署为集群,sentinel多个节点同时监控redis主从架构,一旦有一个sentinel节点发现redis的主节点不在线时的情况,sentinel会与其他的sentinel节点协商,看其他的sentinel 节点是否也同样发现redis的主节点不在线,如果sentinel的多个点节点都发现redis的主节点都为离线,那么则判定redis主节点为离线状态,以此方式避免误判,同样也避免了单点故障。
2. sentinel¶
1). 用于管理多个redis实现高可用HA
2). 监控多个redis服务节点
3). 自动故障转移
sentinel 是一个分布式系统,可以在一个架构中运行多个sentinel进程,多个进程之间使用“流言协议”接收redis主节点是否离线,并使用“投票协议”是否实现故障转移,选择哪一个redis的从服务器成为主服务器
该方案有个缺陷:因为主从实例地址(IP,PORT)是不同的,当故障发生进行主从切换后,应用程序无法知道新地址,在Jedis2.2.2中新增了对Sentinel的支持,应用通过redis.clients.jedis.JedisSentinelPool.getResource()取得的 Jedis 实例会及时更新到新的主实例地址
Jedis 是 Redis 官方推荐的Java连接开发工具。要在Java开发中使用好Redis中间件,必须对Jedis熟悉才能写成漂亮的代码。运维人员不下需要关心redis集群连接接,这个是开发人员作的工作。如果要提供统一访问出口。可以使用Twemproxy中间件部署集群。
3. 配置哨兵模式¶
第一台redis配置主redis
执行
#创建存放日志的目录
mkdir -p /var/log/redis
#修改配置文件
sed -i 's@^logfile.*@logfile "/var/log/redis/6379.log"@' /usr/local/redis/etc/redis.conf
#或者sed -i 's@^#\?logfile.*@logfile "/var/log/redis/6379.log"@' /usr/local/redis/etc/redis.conf
echo "masterauth 123456" >>/usr/local/redis/etc/redis.conf
#或者sed -i 's@^#\?masterauth.*@masterauth 123456@' /usr/local/redis/etc/redis.conf
#杀死redis服务再启动
pkill redis-server
redis-server /usr/local/redis/etc/redis.conf
if ss -tuanp|grep redis &>/dev/null;then
echo "redis服务重启成功"
else echo "redis服务启动失败"
fi
#更好的方案
# 如果配置里有 masterauth 行(不管有没有被注释),就替换成新的
#sed -i "s@^#\\?masterauth.*@masterauth 123456@" /usr/local/redis/etc/redis.conf
# 如果配置里没有 masterauth 行,就追加到末尾
#grep -q '^masterauth' /usr/local/redis/etc/redis.conf || echo "masterauth 123456" >> /usr/local/redis/etc/redis.conf
第二三台redis配置
执行
# 匹配 logfile "" 或 logfile " "(空或纯空格)
#if grep -E '^logfile "[[:space:]]*"' /etc/redis/redis.conf;then
# sed -i "s/^logfile "[[:space:]]*/logfile "/var/log/redis/6379.log"/"
#else echo
#fi
#pidfile把#去掉
sed -i 's@^#pidfile@pidfile@' /etc/redis/redis.conf
systemctl restart redis
查看主从信息
[root@4 ~]# redis-cli -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:192.168.100.99
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_read_repl_offset:2392
slave_repl_offset:2392
replica_full_sync_buffer_size:0
replica_full_sync_buffer_peak:0
master_current_sync_attempts:76
master_total_sync_attempts:76
master_link_down_since_seconds:-1
total_disconnect_time_sec:0
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:5191c52cc7a483cd3d8d0d7d4f5937b1183279da
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:2392
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
测试
[root@1 ~]# redis-server /usr/local/redis/etc/redis.conf
[root@1 ~]# redis-cli -p 6379 -a 123456 -h 192.168.100.99
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.100.99:6379> set key1 abc
OK
192.168.100.99:6379> get key1
"abc"
[root@3 ~]# redis-cli -h 192.168.100.101 -a 123456 -p 6379
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.100.101:6379> get key1
"abc"
[root@4 ~]# redis-cli -h 192.168.100.102 -a 123456 -p 6379
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.100.102:6379> get key1
"abc"
4. 部署sentinel¶
三个sentinel节点的sentinel.conf文件配置一样,如果是在同一主机上,则需要使用不同端口号
Ⅰ. 生成sentinel配置文件
redis-sentinel 可以理解为运行有着特殊代码的 redis,redis 自身也可以运行sentinel,sentinel 也依赖配置文件,在 redis 解压目录下有一个sentinel.conf文件,copy 一份进行修改用于保存sentinel不断收集的状态信息.
cp -f /root/redis-7.4.1/sentinel.conf /usr/local/redis/etc/
sed -i 's/daemonize no/daemonize yes/' /usr/local/redis/etc/sentinel.conf
sed -i 's@^logfile.*@logfile "/var/log/redis/sentinel_26379.log"@' /usr/local/redis/etc/sentinel.conf
sed -i 's@sentinel monitor mymaster 127.0.0.1 6379 2@sentinel monitor mymaster 192.168.100.99 6379 2@' /usr/local/redis/etc/sentinel.conf
cat << EOF >> /usr/local/redis/etc/sentinel.conf
bind 0.0.0.0
#添加监控一定要写0.0.0.0,不然无法进行主从切换
sentinel auth-pass mymaster 123456
EOF
5. 复制到34上¶
[root@1 redis-7.4.1]# scp /usr/local/redis/etc/sentinel.conf root@192.168.100.101:/etc/redis/sentinel/sentinel.conf
sentinel.conf 100% 15KB 9.3MB/s 00:00
[root@1 redis-7.4.1]# scp /usr/local/redis/etc/sentinel.conf root@192.168.100.102:/etc/redis/sentinel/sentinel.conf
sentinel.conf 100% 15KB 9.7MB/s 00:00
[root@1 redis-7.4.1]#
6. 启动sentinel¶
[root@1 redis-7.4.1]# redis-server /usr/local/redis/etc/sentinel.conf --sentinel
[root@1 redis-7.4.1]# ss -tuanp|grep 26379
tcp LISTEN 0 511 0.0.0.0:26379 0.0.0.0:* users:(("redis-server",pid=5186,fd=8))
[root@3 ~]# sed -i 's/daemonize yes/daemonize no/' /etc/redis/sentinel/sentinel.conf
[root@3 ~]# echo "supervised systemd" >> /etc/redis/sentinel/sentinel.conf
[root@3 ~]# systemctl start redis-sentinel
[root@3 ~]# ss -tuanp|grep 26379
tcp LISTEN 0 511 0.0.0.0:26379 0.0.0.0:* users:(("redis-sentinel",pid=27143,fd=8))
tcp TIME-WAIT 0 0 192.168.100.101:45004 192.168.100.99:26379
tcp ESTAB 0 0 192.168.100.101:56698 192.168.100.99:26379 users:(("redis-sentinel",pid=27143,fd=17))
tcp TIME-WAIT 0 0 192.168.100.101:40646 192.168.100.102:26379
tcp TIME-WAIT 0 0 192.168.100.101:26379 192.168.100.99:58450
tcp ESTAB 0 0 192.168.100.101:26379 192.168.100.99:49394 users:(("redis-sentinel",pid=27143,fd=19))
tcp TIME-WAIT 0 0 192.168.100.101:26379 192.168.100.102:57488
tcp ESTAB 0 0 192.168.100.101:26379 192.168.100.102:38514 users:(("redis-sentinel",pid=27143,fd=20))
tcp ESTAB 0 0 192.168.100.101:42904 192.168.100.102:26379 users:(("redis-sentinel",pid=27143,fd=18))
[root@4 ~]# sed -i 's/daemonize yes/daemonize no/' /etc/redis/sentinel/sentinel.conf
[root@4 ~]# echo "supervised systemd" >> /etc/redis/sentinel/sentinel.conf
[root@4 ~]# systemctl start redis-sentinel
[root@4 ~]# ss -tuanp|grep 26379
tcp LISTEN 0 511 0.0.0.0:26379 0.0.0.0:* users:(("redis-sentinel",pid=1684,fd=8))
tcp ESTAB 0 0 192.168.100.102:42954 192.168.100.99:26379 users:(("redis-sentinel",pid=1684,fd=17))
tcp ESTAB 0 0 192.168.100.102:26379 192.168.100.101:46766 users:(("redis-sentinel",pid=1684,fd=20))
tcp ESTAB 0 0 192.168.100.102:50798 192.168.100.101:26379 users:(("redis-sentinel",pid=1684,fd=18))
tcp ESTAB 0 0 192.168.100.102:26379 192.168.100.99:34442 users:(("redis-sentinel",pid=1684,fd=19))