Redis持久化与主从复制的实践


Posted in Redis onApril 27, 2021

为什么需要持久化

Redis是基于内存的NoSQL数据库,读写速度自然快,但内存是瞬时的,在redis服务关闭或重启之后,redis存放在内存的数据就会丢失,为了解决这个问题,redis提供了两种持久化方式,以便在发生故障后恢复数据。

持久化选项

redis提供了两种不同的持久化方式来将数据存储到硬盘中。一种是快照方式(也叫RDB方式),它可以将莫一时刻存在于redis中的所有数据存储到硬盘;另一种叫只追加文件(AOF)方式,它会定时的复制redis执行的所有写命令到硬盘。这两种持久化方式各有千秋,既可以同时使用,也可以独立使用,在某些情况下甚至可以两种都不使用。

RDB方式

RDB方式也称快照方式,通过创建快照来保存某个时间点上的数据副本(.rdb)到硬盘。在重启服务器后,redis会加载这个rdb文件来还原数据。先来看一下rdb持久化配置。
vi redis.conf打开redis的配置文件,找到SNAPSHOTTING部分,发现如下内容:

save 900 1
save 300 10
save 60 10000
……
dbfilename dump.rdb
dir ./

说明

  • save seconds changes:表示在seconds秒后,如果有不少与changes个key发生改变,则保存一次快照。可以看到,rdb持久化默认是开启的,并且配置了三个save选项,如果想要关闭rdb持久化,将所有的save注释掉就好了
  • dbfilename:rdb文件名
  • dir:rdb文件存放路径

创建快照

BGSAVE:
BGSAVE命令可以用于创建一个快照,在redis接收到BGSAVE命令后会fork出一个子进程,子进程负责将快照写入硬盘,而父进程则继续处理命令请求。需要注意的是redis在创建子进程时会阻塞父进程,时间长短与redis占用的内存大小成正比。
除了手动的调用BGSAVE命令外,BGSAVE命令的触发条件有如下两种:

  • 用户配置了save选项,从redis最近一次创建快照开始算起,当任意一个save选项的条件被满足时,会触发一次BGSAVE命令。
  • 在进行主从复制连接时,刚连上来的从服务器会向主服务器发送SYNC命令请求数据同步,在主服务器收到SYNC命令后,会执行一次BGSAVE命令,后将生成的rdb文件发送给从服务器进行数据同步。

SAVE:
SAVE命令同样可以创建一个快照,但与BGSAVE命令不同的是SAVE命令不会创建子进程,所以接收到SAVE命令的redis服务器在快照创建完毕之前不会响应其他任何命令。由于在创建快照的过程中没有其他进程抢夺资源,所以SAVE命令创建快照的速度会比BGSAVE命令创建快照更快一些。即使这样,SAVE命令也并不常用,通常只会在没有足够内存或等待快照生成完毕也无所谓的情况下才会使用。
例如,当redis收到SHUTDOWN命令关闭服务时,就会执行一次SAVE命令,阻塞所有客户端,并在SAVE命令执行完毕后关闭。

RDB方式的优劣

优势:

仅用一个文件备份数据,灾后易于恢复相比于aof,rdb文件更小,并且加载rdb文件恢复数据也更快

劣势:

如果redis服务因故障关闭或重启,会丢失最近一次快照创建后写入的数据当数据量很大的时候,创建子进程会导致redis较长时间的停顿

AOF方式

简单来说,AOF持久化会将被执行的写命令写到aof文件的末尾,以此来记录数据发生的变化。因此,redis只要从头到尾重新执行一遍aof文件中包含的所有写命令,就可以恢复数据。

打开redis配置文件可以看到:

# 是否开启aof持久化,默认为关闭(no)
appendonly yes
# 设置对aof文件的同步频率
# 每接收到一条写命令就进行一次同步,数据保障最有力,但对性能影响十分严重
appendfsync always
# 每秒进行一次同步,推荐
appendfsync everysec
# 由操作系统来决定何时进行同步
appendfsync no
# 重写aof相关
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

重写/压缩aof文件

由于aof持久化会不断地记录redis的写命令,随着redis的运行,aof文件会越来越大,占用过多的硬盘空间,并增加redis进行数据还原操作的时间。因此,必须要有避免aof文件体积过大的控制方案。

redis提供了BGREWRITEAOF命令对aof文件进行重写,BGREWRITEAOF会通过移除原aof文件中冗余的命令来尽可能的减小aof文件的体积。BGREWRITEAOF的工作原理与BGSAVE很像,会由redis创建一个子进程,再由子进程对aof文件进行重写。

当然,BGREWRITEAOF命令同样也有自动触发的机制,可通过配置auto-aof-rewrite-percentageauto-aof-rewrite-min-size来自动执行。例如,配置了auto-aof-rewrite-percentage 100 和 auto-aof-rewrite-min-size 64mb,并且开启了aof持久化,那么在aof文件体积大于64mb且当前文件比上一次重写后的文件体积大了一倍(100%)以上时,redis会自动执行BGREWRITEAOF命令。

AOF持久化的优劣

优势

可以将丢失数据的时间窗口降低至1秒,并且不会对性能在成太大影响aof对于日志文件采用的是追加模式,因此在写入过程中即使出现宕机,也不会破坏日志文件中已经存在的内容;若只写入一半数据就宕机,在redis下次启动时,可通过redis-check-aod工具来解决数据一致性的问题

劣势

aof文件的体积一直是AOF持久化最大的缺陷,即使有重写aof文件的机制存在载入aof文件恢复数据的过程会比载入rdb文件耗时更长

 主从复制

尽管redis性能十分优秀,但还是会遇到无法快速处理请求的问题,为了抗高并发带来的数据库性能问题,redis可以像关系型数据库一样进行主从复制、读写分离。即向主服务器写入数据,从服务器实时收到更新,并使用从服务器处理所有的读请求,而不是像以前一样将所有读请求都发送给主服务器,造成主服务器压力过大,通常读请求会随机地选择使用哪一个从服务器,从而使负载均衡地分配到每一个从服务器上。下图是一个简单的redis主从架构。

Redis持久化与主从复制的实践

主从复制配置

首先在你的redis目录下执行vi redis6380.conf在当前目录下创建一个redis配置文件,写入如下内容:

include /usr/local/redis-4.0.13/redis.conf
port 6380
pidfile /var/run/redis_6380.pid
logfile 6380.log
dbfilename dump6380.rdb

说明:

  • include:向当前配置文件中引入所指向的配置文件的配置信息,这里引入的是redis默认配置文件,其中已经设置过远程访问、密码等,没必要在新的配置文件中重新设置。对于有必要重新配置的配置信息来说(如端口号),include行下进行的配置可以覆盖引用的配置。
  • port:端口号,我们的主从服务器是跑在同一台虚拟机上的,因此需要配置不同的端口号。
  • pidfile:自定义的pid文件,后台程序的pid存在这个文件里。
  • logfile:日志文件。
  • dbfilename:rdb文件的名字。

经过上述操作,一个新的主服务器就配置好了,接下来配置从服务器,同样在当前目录下创建一个redis配置文件起名redis6382vi redis6382.conf

include /usr/local/redis-4.0.13/redis.conf
port 6382
pidfile /var/run/redis_6382.pid
logfile 6382.log
dbfilename dump6382.rdb
slaveof 127.0.0.1 6380
masterauth 主服务器的密码

其中有一些从服务器额外的配置:

  • slaveof:表示我是谁的从服务器,需要制定主服务器的ip地址和端口号
  • masterauth:假如你的主服务器配置了密码,那么需要在此进行配置,否则从服务器将无法连接到主服务器

其他的从服务器配置也都类似,注意分配端口号,我这里又配置了一个6384。
配置成功后,在src目录下使用./redis-server ../redis6380.conf就可以开启主服务器了,接下来开启从服务器会自动连到主服务器上,注意指定对应的配置文件。
执行ps -ef | grep redis看到如下内容则表示主从服务器启动成功:

root      2625     1  0 16:15 ?        00:00:00 ./redis-server *:6380
root      2630     1  0 16:15 ?        00:00:00 ./redis-server *:6382
root      2636     1  0 16:15 ?        00:00:00 ./redis-server *:6384

在主从服务器都启动好了以后,进入主服务器的客户端./redis-cli -p 6380 -a 你的密码,执行info replication可以查看主从服务器信息,如下

127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6382,state=online,offset=336,lag=1
slave1:ip=127.0.0.1,port=6384,state=online,offset=336,lag=1
master_replid:b5c68a979b28d2a9ef53476510758b5d1795418b
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:336
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:336

同样,在从服务器客户端中执行上述命令,也能够得到信息

127.0.0.1:6384> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6380
master_link_status:up
master_last_io_seconds_ago:2
master_sync_in_progress:0
slave_repl_offset:686
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:b5c68a979b28d2a9ef53476510758b5d1795418b
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:686
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:15
repl_backlog_histlen:672

至此,一个一主两从、读写分离的redis架构已经配置好并成功启动了。

主从复制的启动过程

Redis持久化与主从复制的实践

上图是旧版主从Redis的启动过程,需要特殊说明的几点是:

  • 从服务器在进行初始连接的时候,数据库中原有的所有数据都将被丢失,并替换成主服务器发送来的数据
  • 从服务器不负责key的过期操作,而是被动的接受主服务器发来的命令,当一个 master 让一个 key 到期(或由于 LRU 算法将之驱逐)时,它会合成一个 DEL 命令并传输到所有的 slave
  • SYNC是一个非常耗费资源的操作,在BGSAVE期间主服务器的总吞吐量下降,接着耗费大量的主从服务器的网络资源传送rdb文件,在从服务器载入rdb文件时会无法响应客户端的请求;但SYNC最大的缺陷是在从服务器因断线进行重新连接时,没必要申请一个rdb文件从头再加载一次,因为这个新的rdb文件中包含的大部分数据很可能在断线之前就已经写入了从服务器,此时从服务器只需要得到在断线期间写入的数据就得了

 部分重同步

为了弥补旧版复制的缺陷,Redis从2.8版本开始使用PSYNC命令代替SYNC命令。PSYNC有完整重同步和部分重同步两种模式,其中完整重同步和上述的旧版同步差不多,也是得发个rdb。但是部分重同步很牛X了:它可以只将断线期间的写入主服务器的写命令发送给从服务器,耗费资源更少,速度也快的多。如下图。

Redis持久化与主从复制的实践

部分重同步的实现原理并不复杂,由三部分构成:复制偏移量(offset)、复制积压缓冲区和服务器运行id(runid)

复制偏移量
复制偏移量是用来确认主从服务器的同步状态的。主从服务器各自维护一份复制偏移量,当主服务器向从服务器发送了N个字节的数据时,就将自己的复制偏移量加上N;从服务器收到N个字节的数据也会将自己的复制偏移量加上N。通过比较主从双方的复制偏移量就可以很容易的确认同步状态。

Redis持久化与主从复制的实践

复制积压缓冲区
复制积压缓冲区是由主服务器维护的一个固定长度的先进先出的队列,在主服务器进行命令传播的时候会顺道让命令入队到复制积压缓冲区中,如下:

Redis持久化与主从复制的实践

由于复制积压缓冲区是一个固定长度的队列,所以它只会保存最近一段时间内执行的写命令,并为队列中的每个字节记录对应的复制偏移量。在从服务器发送PSYNC命令时,会携带上自己的复制偏移量,主服务器拿着这个偏移量去自己的复制积压缓冲区中查看offset+1(即断线后执行的下一个命令)还在不在队列中。如果还在,表示可以执行部分重同步,后面会将从offset+1到队尾的所有数据发送给从服务器;如果不在,那从服务器只能老老实实的去做完全重同步。

Redis持久化与主从复制的实践

服务器运行Id
服务器运行Id说白了就是看主从服务器断线之前是不是一家子。每一个redis服务器都有自己的运行id,主从初次连接时,主服务器会把自己的服务器运行id发送给从服务器保存起来,从服务器在重连接的时候会把之前保存的主服务器runid一并发给主服务器,主服务器会拿着这个runid和自己的runid进行比对。如果一致,则表示该从服务器之前确实是从自己这里断线的,接下来进行偏移量的检查;如果不一致,则表示这个从服务器先前是其他主服务器的slave,直接打去做完全重同步。

在之前执行info replication命令的时候就可以看到服务器运行id和复制偏移量。

综上,一个新版redis复制的同步过程大致如下:

Redis持久化与主从复制的实践

到此这篇关于Redis持久化与主从复制的实践的文章就介绍到这了,更多相关Redis持久化与主从复制内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Redis 相关文章推荐
Redis6.0搭建集群Redis-cluster的方法
May 08 Redis
redis三种高可用方式部署的实现
May 11 Redis
浅谈Redis的几个过期策略
May 27 Redis
比较几种Redis集群方案
Jun 21 Redis
k8s部署redis cluster集群的实现
Jun 24 Redis
基于Redis结合SpringBoot的秒杀案例详解
Oct 05 Redis
详解redis在微服务领域的贡献
Oct 16 Redis
Redis 持久化 RDB 与 AOF的执行过程
Nov 07 Redis
高并发下Redis如何保持数据一致性(避免读后写)
Mar 18 Redis
redis sentinel监控高可用集群实现的配置步骤
Apr 01 Redis
Redis中key的过期删除策略和内存淘汰机制
Apr 12 Redis
Redis高并发缓存架构性能优化
May 15 Redis
浅谈Redis在直播场景的实践方案
Apr 27 #Redis
redis限流的实际应用
Apr 24 #Redis
Redis安装启动及常见数据类型
redis配置文件中常用配置详解
Apr 14 #Redis
Redis遍历所有key的两个命令(KEYS 和 SCAN)
Apr 12 #Redis
浅谈redis五大数据结构和使用场景
Redis如何一键部署脚本
You might like
实现“上一页”和“下一页按钮
2006/10/09 PHP
php生成WAP页面
2006/10/09 PHP
php面向对象全攻略 (七) 继承性
2009/09/30 PHP
PHP中去除换行解决办法小结(PHP_EOL)
2011/11/27 PHP
php取得字符串首字母的方法
2015/03/25 PHP
PHP unset函数原理及使用方法解析
2020/08/14 PHP
JQuery 动画卷页 返回顶部 动画特效(兼容Chrome)
2010/02/15 Javascript
谈谈关于JavaScript 中的 MVC 模式
2013/04/11 Javascript
js opener的使用详解
2014/01/11 Javascript
JS辨别访问浏览器判断是android还是ios系统
2014/08/19 Javascript
js实现TAB切换对应不同颜色的代码
2015/08/31 Javascript
详解AngularJS中的表单验证(推荐)
2016/11/17 Javascript
nodejs入门教程三:调用内部和外部方法示例
2017/04/24 NodeJs
node删除、复制文件或文件夹示例代码
2019/08/13 Javascript
JavaScript Array对象基本方法详解
2019/09/03 Javascript
JS获取一个字符串中指定字符串第n次出现的位置
2021/02/10 Javascript
简明 Python 基础学习教程
2007/02/08 Python
python定时检查某个进程是否已经关闭的方法
2015/05/20 Python
详解Python中的strftime()方法的使用
2015/05/22 Python
使用selenium模拟登录解决滑块验证问题的实现
2019/05/10 Python
Python环境Pillow( PIL )图像处理工具使用解析
2019/09/12 Python
Django模板导入母版继承和自定义返回Html片段过程解析
2019/09/18 Python
Python 列表中的修改、添加和删除元素的实现
2020/06/11 Python
Linux安装Python3如何和系统自带的Python2并存
2020/07/23 Python
python+pygame实现坦克大战小游戏的示例代码(可以自定义子弹速度)
2020/08/11 Python
python 模拟登陆github的示例
2020/12/04 Python
在PyCharm中安装PaddlePaddle的方法
2021/02/05 Python
纯CSS3代码实现switch滑动开关按钮效果
2016/08/30 HTML / CSS
CSS3弹性布局内容对齐(justify-content)属性使用详解
2017/07/31 HTML / CSS
canvas裁剪clip()函数的具体使用
2018/03/01 HTML / CSS
自动化工程专业个人应聘自荐信
2013/09/26 职场文书
大专生工程监理求职信
2013/10/04 职场文书
运动会广播稿500字
2014/01/28 职场文书
小学生竞选班干部演讲稿(5篇)
2014/09/12 职场文书
公司领导班子召开党的群众路线教育实践活动总结大会新闻稿
2014/10/21 职场文书
2014年教育实习工作总结
2014/11/22 职场文书