redis的持久化、主从复制、哨兵机制

最近拿到了腾讯暑期实习的offer,抓紧学一学不会的东西,比如redis。看的是这位老师的教程极客时间链接还有《redis设计与实现》、《redis使用手册》这两本书都是黄健宏老师写的。部分是直接引用,来帮助自己理解redis的原理。感谢两位老师!

需要学习redis的知识

在这里插入图片描述

redis的模块

在这里插入图片描述

redis的基本类型

有五种String、List、Hash、Sorted Set、Set下图就是基本类型底层实现的数据结构。
在这里插入图片描述

压缩列表

压缩列表挺好玩的,就是为了节约内存而设计出来的,如小整数、短字符串。压缩列表可以有任意多个节点,每个节点可以保存一个字节数组或者一个整数值,节点字节大小也是任意的。

压缩列表拥有这五个成员变量,
在这里插入图片描述
entryX结点所含有的元素,previous_entry_length保存是上一个结点的长度,从后往前遍历就可以靠它,得到前一位的起始地址的指针。如果前一个结点长度是小于254字节的,previous_entry_length是一个字节的,否则是5个字节。这也带来一个问题就是连锁更新。连锁更新通俗的讲就是连续好多个previous_entry_length长度都是1个字节的,但是插入了一个元素长度大于254字节的entry,他后面的entry的previous_entry_length从1字节变为5字节,后面依次都要更改,像多米诺骨牌一样,产生了连锁反应。不过书中也提到不用太担心连锁更新影响的压缩列表的性能,因为必要条件是大量连续,只有几个的话是不影响的

encoding记录当前数据的类型和长度。

content字面意思记录着这个entry的内容。
在这里插入图片描述
不过我感觉应该叫他压缩双相链表,可以看源码的前言。不过根据课程描述,我觉得应该是空间连续的双向链表。内存空间连续方便CPU的缓存,链表方便插入和删除。本质上就是内存空间连续的链表
在这里插入图片描述

哈希表

有一个总的哈希桶存放着所有节点,节点中有两个指针,key和value都是指针,可以指向任意的数据类型。
在这里插入图片描述
当哈希冲突的时候会有链式哈希来解决,但是链式哈希最后会蜕化成O(N)的时间复杂度,解决办法就是rehash。redis是有两个总局哈希桶的一开始使用哈希桶1,哈希桶2不分配内存。当大小不够了哈希桶2就分配更大的空间,在把哈希桶1的内容搬到哈希桶2中,数据量大的话会造成阻塞,所以redis就是渐进式的rehash,慢慢把哈希桶1的数据挪到哈希桶2中。

引用一下原文。简单来说就是在第二步拷贝数据时,Redis 仍然正常处理客户端请求,每处理一个请求时,从哈希表 1 中的第一个索引位置开始,顺带着将这个索引位置上的所有 entries 拷贝到哈希表 2 中;等处理下一个请求时,再顺带拷贝哈希表 1 中的下一个索引位置的 entries。如下图所示:
在这里插入图片描述
我对上述的话的理解+结合书的理解总结如下
渐进式rehash就是一次挪一点一次挪一点,分摊到了多次请求的过程处理中了,避免了大量数据移动而造成的阻塞。在rehash的时候是有一个变量rehashIndex = 0来标识当前的索引位置,在rehash的时候不仅会执行指定的操作也会将rehashIndex 下标的值挪到新的哈希表中。如果长时间没有挪动,rehash是有定时任务的会过了多长时间自动挪,所以渐进rehash不会很耗时的。当rehashIndex == -1的时候说明rehash的操作已经完成了。在渐进式rehash的过程中,如果进行操作的话会先查找哈希表1,如果没有找到,再去查找哈希表2。(假设哈希表1是老的表,哈希表2是新的表)

跳表

就是给链表加了个索引,方便查找元素。

范围查询的小细节

不仅可以rehash,还可以一次渐进式遍历。万物皆可渐进式?哈哈哈,感觉还挺好玩的
在这里插入图片描述

关于redis单线程的误解

引用原文了没想到单线程竟然是这个样子。
在这里插入图片描述

AOF文件

AOF(append only file)当电脑关机了数据保存在这里恢复数据。一般日志都是写前日志,写前日志都是在操作之前先记录更改的值,防止值已经更改了但是日志没有写上。而AOF是写后日志,他记录的不是值,而是每一条的命令。AOF持久化是通过保存redis服务器所执行的写命令来记录数据库状态的,也就是说读命令是不会进行任何操作的。

AOF写回操作的策略

在这里插入图片描述
第一种,安全性能最好,但是效率最低,断电等意外事情最多
第二种,安全性能中等,效率中等,属于折中方式
第三种,就是看操作系统的心情了,操作系统会给他放入内存缓冲区当中的,当他觉得缓冲区满了或者超时的时候才写入,但是出现突发事情,死的也是最惨的。。。。但他的速度是真滴快。系统有fsync和fdatasync两个同步函数强制让操作系统将缓冲区中的数据写入到硬盘里面。
在这里插入图片描述

AOF重写

啥是重写呢?比如我们set key 1记录到AOF缓冲区了,这时候我们又set key 2,set key 3,set key n,如果一条一条记录那真是太占地方了啊,如何解决的呢?保留最终值,AOF缓冲区只有最终状态的指令set key n。将多条指令最终变成一条指令保存。扫描整个数据库然后生成新的AOF文件,以达到减小体积的目的。

AOF何时触发重写机制

该水友在评论区指出的一点,也是课程和书中没有的内容。

为了减小aof文件的体量,可以手动发送“bgrewriteaof”指令,通过子进程生成更小体积的aof,然后替换掉旧的、大体量的aof文件。

也可以配置自动触发

1)auto-aof-rewrite-percentage 100

2)auto-aof-rewrite-min-size 64mb

这两个配置项的意思是,在aof文件体量超过64mb,且比上次重写后的体量增加了100%时自动触发重写。我们可以修改这些参数达到自己的实际要求
在这里插入图片描述

AOF如何载入与数据还原

redis重建需要重写所有命令,这些命令是记录在AOF中的,那么如何写呢?redis是采用一个伪终端的方法,是不带网络连接的,伪终端读取AOF文件将他写入到redis服务器中以达到数据还原。直到读完AOF文件。

AOF后台重写

一开始我们只有一个缓冲区——AOF缓冲区,我们把命令全部放到这里。然后fork一个子进程,子进程保存AOF缓冲区的副本,子进程将AOF缓冲区写入硬盘中。不是线程的原因避免了操作一个缓冲区而加锁的情况。而redis还可以继续在AOF缓冲区加入新的命令。这种就会造成数据不一致了,解决办法再加一个缓冲区,叫做AOF重写缓冲区,当子进程将AOF缓冲区写入到硬盘的时候,redis服务器会把执行的所有命令写入到AOF重写缓冲区中,这样当子进程重写好之后,再将AOF重写缓冲区的东西加到新写入的文件。从而保证了数据的一致性。

在子进程写文件的时候,AOF缓冲区和AOF重写缓冲区都是会把追加新的命令的。新命令写入到AOF缓冲区也保持了数据一致性。
在这里插入图片描述

rdb持久化

rdb(redis database),Redis 的数据都在内存中,为了提供所有数据的可靠性保证,它执行的是全量快照,也就是说,把内存中的所有数据都记录到磁盘中。rdb文件是redis默认的持久化方式。rdb文件保存的是某一个时刻的状态。rdb文件是比AOF文件恢复数据快的,因为rdb文件是一个压缩二进制文件,不需要像AOF文件一条一条的读指令去恢复redis服务器。但是数据越来越多,往磁盘写的时间花费也就越来越大了。

rdb文件持久化可以手动也可以自动持久化(服务器配置选项定期执行)。手动的话可以可以使用两个SAVE和BGSAVE,SAVE会阻塞redis服务器,读写都不能操作。SAVE命令的过程如下所示,阻塞当前进程创建新的RDB文件,然后替换掉原有的RDB文件。
在这里插入图片描述
BGSAVE另起一个进程开始持久化,不影响主进程。不过有一点需要注意的是做快照的时候数据是不能修改的。redis服务器是可以正常运行的,修改数据的时候就用到COW技术了,COW技术处理写操作
在这里插入图片描述
BGSAVE命令处理的流程

在这里插入图片描述

rdb快照时间间隔短的话可能会导致两个问题,磁盘的压力突然就增大了,可能第一个快照没做完就要准备做第二个快照了。还有一个问题是会导致频繁fork子进程,拷贝内容给子进程会阻塞主进程。

解决办法就跟渐进rehash似的,间隔短就可以做增量快照,啥是增量快照?就是记住那个值被修改了,就专门修改这个即可。但记住需要高效的数据结构比如哈希表,会占用大量内存,还有什么办法?

AOF文件和RDB文件的优缺点

AOF文件持久化的安全性能比RDB文件的安全性要高。因为RDB文件快照间隔时间不够短,短的话会频繁fork子进程造成阻塞主进程。

AOF存储的是协议文本,所以他的体积会比RDB文件大的多,所以生成AOF文件所需的时间也会比RDB文件长。RDB是直接通过数据恢复数据库,AOF是通过伪终端来恢复数据,并且RDB文件相对于AOF文件较小。所以RDB文件恢复数据的速度是比AOF文件恢复数据库快的。AOF文件体积大的时候,AOF重写会占用大量资源,fork子进程的时候会导致服务器相对的阻塞。

RDB 文件和AOF文件混合使用

redis4.0之后AOF文件和RDB文件混合使用,AOF重写的时候生成RDB数据(毕竟AOF文件重写的时候会做一个全部扫描生成所有命令,不如生成体积小的RDB数据),再有写操作都追加到AOF数据中就是之前生成RDB数据的后面(不理解没关系,看下图)。混合使用的好处AOF文件也不会过大了,安全性能也提高了。混合模式是会得到一个全新的AOF文件,上面是RDB文件,下面是追加之后的写命令的AOF文件。
在这里插入图片描述
AOF重写的时候后台进行RDB数据生成,之后的写操作保存在AOF缓冲区中。RDB数据写完之后再将AOF缓冲区的东西加到末尾。当第二次AOF文件重写的时候就可以清空上一次AOF文件缓冲区了,因为第二次做AOF文件重写的时候保存在AOF文件修改的数据已经修改了。
在这里插入图片描述

主从复制

介绍了那么多理论,主从复制和后面的哨兵机制打算拿Linux系统进行模拟了,动手实战一下,动手玩一玩还是蛮好玩的(写博客之前先玩了会哈哈哈)。

为什么要主从复制?比如说一台主机吞吐量不够啦、或者就这一台redis数据库服务器,他宕机了那岂不是会影响业务?主从复制是redis提供的一个最基础的多机功能。主数据库负责读和写,从数据库负责读(也可以给他写操作)之后我们会进行模拟。他们之间的关系可以理解为:主数据库可以有多个小弟(从数据库),而小弟(从数据库)只能有一个leeder(主数据库)。

主服务器如何给从服务器数据?

流程是下面的这张图,我还简单介绍一下吧。首先主从服务器是怎么保证数据同步呢?传输RDB文件(主数据库自动调用BGSAVE命令生成一个RDB文件),对就是前面介绍的RDB文件,这样从数据库收到了RDB文件进行读取载入数据,之后在有客户端写操作给主数据库,主数据库在把命令发送给从数据库。值得一提的是,如果同时上线了多台从数据库,我的主数据库又没有发生写操作,那我要一个一个复制RDB文件吗?不,不需要,主数据库直接发送原来生成的RDB文件就行,避免了无谓的RDB文件生成操作。
在这里插入图片描述
哇,上面说的这些感觉redis好牛逼,我也觉得redis好牛逼。redis这么牛逼了那么我们还会碰到什么问题?简单思考一下,主数据库宕机了怎么办?从数据库宕机了又恢复了怎么办?如何解决这俩个问题?

主库宕机了怎么办

后面我们将会介绍哨兵机制来解决主库宕机问题,哨兵机制也真的好好玩。

从库宕机了怎么办

从库宕机又恢复了,我们是否要进行全量更新,简单说就是主库是否要重新传送一份新的RDB文件吗?分情况来讨论是否需要传新的RDB文件,因为传一个完整的RDB文件是非常耗时的。

全量复制:新生成一个RDB文件,把这整个RDB文件给从库

增量复制:先给了从库一个RDB文件,在把之后主库写命令给从库,只给从库之后的写命令,而不是大头的RDB文件。

首先介绍一下repl_backlog_buffer这个缓冲区,当有一个从库的时候就会有这个缓冲区。他是一个环形缓冲区,没有从库的时候是没有这个缓冲区的。类似于循环队列,头和尾是相连的,长下面图片这个样子的。主库有一个偏移量master_repl_offset,每个从库也有一个偏移量slave_repl_offset。
在这里插入图片描述
repl_backlog_buffer的size假设是20,主库写的偏移量也是20,那么进行求余%操作等于0就是要从头写了,就会覆盖之前缓存的写的命令了。

第一种情况:从库宕机了又恢复了,他告诉主库我的偏移量是0,主库一算我都大你一圈了我之前的保存的写命令都没了,那主库只能让从库全量复制了,主库重新生成一个RDB文件给从库。

第二种情况:从库宕机了又恢复了,从库告诉主库我(从库)的偏移量是10,主库一计算,之前保存的命令还在,可以让从库增量复制就这样减少开销,提高性能。

修改repl_backlog_size这个配置参数就可以修改repl_backlog_buffer缓冲区的大小了。

主库给从库传输数据的缓冲区

repl_backlog_buffer缓冲区是当有从库的时候才会有他的记录下来,主数据库给客户端传输数据还是给从数据库传输都走另一个缓冲区replication buffer。我们可以这么理解,当从库宕机恢复进行的是增量复制的时候,主库从repl_backlog_buffer缓冲区挑出从库没有的数据,将这些数据写入到replication buffer缓冲区中。然后主库在send这个buff,类似于send(fd,replication buffer,strlen(replication buffer),0);差不多这个样子。

无盘复制

当主库硬盘性能较差的时候,可以不创建RDB进行数据的同步。redis-diskless-sync 这个选项配置为yes即可。

主从服务器的结构

最初的样子大概是这个样子的图18-1,从服务器数量多的话主服务器就一直给从服务器传输rdb文件,主服务器啥都没干就传输rdb文件了。这样可不行啊,让从服务器也连接从服务器,这样减轻主服务器的负担。就形成了下面图18-2的样子了。

在这里插入图片描述

配置主从服务器

终于到了动手环节啦,我们先开启一个redis进程当主数据库。这个配置项我就修改了端口号改成了9996。
在这里插入图片描述
执行下面这条命令,连接主数据库,客户端再输入role可以看到连接数据库的信息。master表示连接的是主库,slave表示连接的是从库

./redis-cli -p 9996
role

在这里插入图片描述
我们现在再开启一个从库,可以用下面的这条命令启动一个redis数据库,port指的是启动的端口号,–replicaof后面跟着他的主库的IP和端口号。后面的&表示变成后台启动。接着客户端连接这个redis从服务器,使用role这个命令来查看一下,果然是slave,并且不可以进行写命令,当然了给他写的权限也是可以的。

./redis-server --port 9997 --replicaof 127.0.0.1 9996 &

在这里插入图片描述

主库更改会同步给从库,想上文那样。更新了主数据库,从数据库也会跟着更新。
在这里插入图片描述
kill从数据库,然后再启动从数据库,模拟从数据库宕机在恢复,发现也是数据是同步的。
在这里插入图片描述
dump.rdb生成的时间没有改变,说明从数据库恢复并没有重传一遍rdb文件
在这里插入图片描述

未完待续。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值