面试宝典系列-读《深刻学习Redis(2):持久化》概要

原文连接:深刻学习Redis(2):持久化html

Redis的5种对象类型(字符串、哈希、列表、集合、有序集合)redis

查看内存方法:info memory数据库

内存碎片是Redis在分配、回收物理内存过程当中产生的。例如,若是对数据的更改频繁,并且数据之间的大小相差很大,可能致使redis释放的空间在物理内存中并无释放,但redis又没法有效利用,这就造成了内存碎片。内存碎片不会统计在used_memory中。安全

内存碎片的产生与对数据进行的操做、数据的特色等都有关;此外,与使用的内存分配器也有关系:若是内存分配器设计合理,能够尽量的减小内存碎片的产生。后面将要说到的jemalloc便在控制内存碎片方面作的很好。服务器

解决内存碎片:若是Redis服务器中的内存碎片已经很大,能够经过安全重启的方式减少内存碎片:由于重启以后,Redis从新从备份文件中读取数据,在内存中进行重排,为每一个数据从新选择合适的内存dan元,减少内存碎片。网络

jemalloc做为Redis的默认内存分配器,在减少内存碎片方面作的相对比较好。jemalloc在64位系统中,将内存空间划分为小、大、巨大三个范围;每一个范围内又划分了许多小的内存块单位;当Redis存储数据时,会选择大小最合适的内存块进行存储。app

jemalloc划分的内存dan元以下图所示:负载均衡

 

在Redis中,实现高可用的技术主要包括持久化、复制、哨兵和集群,下面分别说明它们的做用,以及解决了什么样的问题。函数

  1. 持久化:持久化是最简单的高可用方法(有时甚至不被归为高可用的手段),主要做用是数据备份,即将数据存储在硬盘,保证数据不会因进程退出而丢失。
  2. 复制:复制是高可用Redis的基础,哨兵和集群都是在复制基础上实现高可用的。复制主要实现了数据的多机备份,以及对于读操做的负载均衡和简单的故障恢复。缺陷:故障恢复没法自动化;写操做没法负载均衡;存储能力受到单机的限制。
  3. 哨兵:在复制的基础上,哨兵实现了自动化的故障恢复。缺陷:写操做没法负载均衡;存储能力受到单机的限制。
  4. 集群:经过集群,Redis解决了写操做没法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案。

持久化的功能:Redis是内存数据库,数据都是存储在内存中,为了不进程退出致使数据的永久丢失,须要按期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘;当下次Redis重启时,利用持久化文件实现数据恢复。除此以外,为了进行灾难备份,能够将持久化文件拷贝到一个远程位置。性能

Redis持久化分为RDB持久化AOF持久化:前者将当前数据保存到硬盘,后者则是将每次执行的写命令保存到硬盘(相似于MySQL的binlog);因为AOF持久化的实时性更好,即当进程意外退出时丢失的数据更少,所以AOF是目前主流的持久化方式,不过RDB持久化仍然有其用武之地。 

RDB持久化:

将当前进程中的数据生成快照保存到硬盘(所以也称做快照持久化),保存的文件后缀是rdb;当Redis从新启动时,能够读取快照文件恢复数据。

手动触发:save命令和bgsave命令均可以生成RDB文件。

save命令会阻塞Redis服务器进程,直到RDB文件建立完毕为止,在Redis服务器阻塞期间,服务器不能处理任何命令请求(save基本被废弃)。而bgsave命令会建立一个子进程,由子进程来负责建立RDB文件,父进程(即Redis主进程)则继续处理请求。

自动触发:自动触发最多见的状况是在配置文件中经过save m n,指定当m秒内发生n次变化时,会触发bgsave。

Redis的save m n,是经过serverCron函数、dirty计数器、和lastsave时间戳来实现的。

serverCron是Redis服务器的周期性操做函数,默认每隔100ms执行一次;该函数对服务器的状态进行维护,其中一项工做就是检查 save m n 配置的条件是否知足,若是知足就执行bgsave。

dirty计数器是Redis服务器维持的一个状态,记录了上一次执行bgsave/save命令后,服务器状态进行了多少次修改(包括增删改);而当save/bgsave执行完成后,会将dirty从新置为0。

lastsave时间戳也是Redis服务器维持的一个状态,记录的是上一次成功执行save/bgsave的时间。

RDB载入:

RDB文件的载入工做是在服务器启动时自动执行的,并无专门的命令。可是因为AOF的优先级更高,所以当AOF开启时,Redis会优先载入AOF文件来恢复数据;只有当AOF关闭时,才会在Redis服务器启动时检测RDB文件,并自动载入。服务器载入RDB文件期间处于阻塞状态,直到载入完成为止。

AOF持久化

因为须要记录Redis的每条写命令,所以AOF不须要触发,下面介绍AOF的执行流程。

AOF的执行流程包括:

  • 命令追加(append):将Redis的写命令追加到缓冲区aof_buf;
  • 文件写入(write)和文件同步(sync):根据不一样的同步策略将aof_buf中的内容同步到硬盘;
  • 文件重写(rewrite):按期重写AOF文件,达到压缩的目的。

文件重写是指按期重写AOF文件,减少AOF文件的体积。须要注意的是,AOF重写是把Redis进程内的数据转化为写命令,同步到新的AOF文件;不会对旧的AOF文件进行任何读取、写入操做! 

文件重写之因此可以压缩AOF文件,缘由在于:

  • 过时的数据再也不写入文件
  • 无效的命令再也不写入文件:若有些数据被重复设值(set mykey v1, set mykey v2)、有些数据被删除了(sadd myset v1, del myset)等等
  • 多条命令能够合并为一个:

AOF重写过程:

  1. Redis父进程首先判断当前是否存在正在执行 bgsave/bgrewriteaof的子进程,若是存在则bgrewriteaof命令直接返回,若是存在bgsave命令则等bgsave执行完成后再执行。这个主要是基于性能方面的考虑。
  2. 父进程执行fork操做建立子进程,这个过程当中父进程是阻塞的
  3.  父进程fork后,bgrewriteaof命令返回”Background append only file rewrite started”信息并再也不阻塞父进程,并能够响应其余命令。Redis的全部写命令依然写入AOF缓冲区,并根据appendfsync策略同步到硬盘,保证原有AOF机制的正确。
  4.  因为fork操做使用写时复制技术,子进程只能共享fork操做时的内存数据。因为父进程依然在响应命令,所以Redis使用AOF重写缓冲区(图中的aof_rewrite_buf)保存这部分数据,防止新AOF文件生成期间丢失这部分数据。也就是说,bgrewriteaof执行期间,Redis的写命令同时追加到aof_buf和aof_rewirte_buf两个缓冲区。
  5.  子进程根据内存快照,按照命令合并规则写入到新的AOF文件。
  6.  子进程写完新的AOF文件后,向父进程发信号,父进程更新统计信息,具体能够经过info persistence查看。
  7.  父进程把AOF重写缓冲区的数据写入到新的AOF文件,这样就保证了新AOF文件所保存的数据库状态和服务器当前状态一致。
  8.  使用新的AOF文件替换老文件,完成AOF重写。

 伪客户端:由于Redis的命令只能在客户端上下文中执行,而载入AOF文件时命令是直接从文件中读取的,并非由客户端发送;所以Redis服务器在载入AOF文件以前,会建立一个没有网络链接的客户端,以后用它来执行AOF文件中的命令,命令执行的效果与带网络链接的客户端彻底同样。

RDB和AOF各有优缺点:

RDB持久化

  • 优势:RDB文件紧凑,体积小,网络传输快,适合全量复制;恢复速度比AOF快不少。固然,与AOF相比,RDB最重要的优势之一是对性能的影响相对较小。
  • 缺点:RDB文件的致命缺点在于其数据快照的持久化方式决定了必然作不到实时持久化,而在数据愈来愈重要的今天,数据的大量丢失不少时候是没法接受的,所以AOF持久化成为主流。此外,RDB文件须要知足特定格式,兼容性差(如老版本的Redis不兼容新版本的RDB文件)。

AOF持久化

  • 与RDB持久化相对应,AOF的优势在于支持秒级持久化、兼容性好,缺点是文件大、恢复速度慢、对性能影响大。

Redis单机的内存不能过大 :

  • 当面对请求的暴增,须要从库扩容时,Redis内存过大会致使扩容时间太长;
  • 当主机宕机时,切换主机后须要挂载从库,Redis内存过大致使挂载速度过慢;
  • 以及持久化过程当中的fork操做,下面详细说明。

父进程经过fork操做能够建立子进程,子进程建立后,父子进程共享代码段,不共享进程的数据空间,可是子进程会得到父进程的数据空间的副本。在操做系统fork的实际实现中,基本都采用了写时复制技术,即在父/子进程试图修改数据空间以前,父子进程实际上共享数据空间;可是当父/子进程的任何一个试图修改数据空间时,操做系统会为修改的那一部分(内存的一页)制做一个副本。虽然fork时,子进程不会复制父进程的数据空间,可是会复制内存页表(页表至关于内存的索引、目录);父进程的数据空间越大,内存页表越大,fork时复制耗时也会越多。

若是Redis单机内存达到了10GB,fork时耗时可能会达到百毫秒级别

相关文章
相关标签/搜索