前言:最近在使用Redis实现了一个消息队列用作MQ中间件,开发过程中发现从Redis中获取的数据有时候会不准确,也没有作持久化与数据恢复,于是回去查阅了相关资料,也顺便做个总结。
持久化:
redis将数据从某种形式同步到硬盘中,使得重启后可以根据硬盘中的记录恢复数据,该过程即为持久化。
Redis持久化:redis支持两种方式的持久化,分别是RDB和AOF方式,RDB方式是根据指定规则定时将内存总的数据存储在硬盘上,AOF是在每次执行完命令后将命令本身记录下来,两者可以结合使用。
RDB:快照,当符合一定条件时,redis会自动将内存中的所有数据生成一份副本并存储在硬盘上,该过程即为快照。
redis进行快照的情况:
根据配置规则进行自动快照:进行快照的条件可以在配置文件中自定义,由两个参数构成;时间窗口M和改动的键的个数N,每当时间M内被更改的键的个数大于N时,即符合自动快照条件,如:save 9001;15分钟(900秒)内有一个键被修改则进行
用户执行SAVE或BGSAVE命令:
SAVE命令:执行该命令时,Redis同步进行快照操作,在快照执行过程中会阻塞所有来自客户端的请求,应尽量少用
BGSAVE命令:手动执行快照是推荐使用该命令,BGSAVE命令可以在后台异步进行快照操作,执行快照同时服务器还能继续相应来自客户端的请求,执行命令后redis会返回OK表示开始快照操作
执行FLUSHALL命令:执行该命令时,redis会清楚数据库中的所有数据,前提是定义了自动快照条件
执行复制(replication)时:设置主从模式时,redis会在复制初始化时进行自动快照
RDB快照原理:redis默认会将快照文件存储在redis当前进程的工作目录中的dump.rdb文件中,可以通过配置dir和dbfilename参数分别制定快照文件的存储路径和文件名,快照过程如下:
redis使用fork函数复制一份当前线程(父进程)的副本(子进程)父进程继续接受并处理客户端发来的命令,而子进程开始将内存中的数据写入硬盘中 临时文件当子进程写入完成所有数据后会用该临时文件替换旧的RDB文件,至此一次快照操作完成。
提示:执行fork时,操作系统会使用写时复制策略,即fork函数发生的一刻父子进程共享同一内存数据(虚拟内存不同,但物理内存相同),当父进程更改其中某片数据时(如执行一个写命令),操作系统会将该片数据复制一份以保证子进程的数据不受影响,因此新的RDB文件存储的是执行fork一刻的内存数据
写时复制策略:
fork子进程完全复制父进程的栈空间,也复制了页表,但没有复制物理页面,所以这时虚拟地址相同,物理地址也相同,但是会把父子共享的页面标记为“只读”(类似mmap的private的方式),如果父子进程一直对这个页面是同一个页面,知道其中任何一个进程要对共享的页面“写操作”,这时内核会复制一个物理页面给这个进程使用,同时修改页表。而把原来的只读页面标记为“可写”,留给另外一个进程使用。
AOF方式:当使用redis存储非临时数据时,一般需要打开AOF持久化来降低进程中止导致的数据丢失。
开启AOF:默认情况下redis没有开启AOF方式的持久化,可以通过命令:appendonly yes 开启AOF持久化,开启后每执行一条会更改redis中的数据的命令,redis就会将该命令写入硬盘中的AOF文件,AOF文件的保存位置与RDB文件的位置相同,都是通过dir参数设置,默认文件名是appendonly.aof。
AOF实现:AOF文件以纯文本的形式记录redis的执行写命令,内容为原始的通信协议内容。
同步硬盘数据:由于操作系统的缓存机制,AOF文件数据先进入系统的硬盘缓存,在默认情况下系统会每30秒会执行一次同步操作,以便将硬盘缓存中的内容真正地写入硬盘,在这30秒中的过程中如果系统异常退出则会导致硬盘缓存中的数据丢失,为此在redis中可以通过appendfsync参数设置同步的时机:#appendfsync always
appendfsync everysec
#appendfsync no默认情况下,redis采用everysec规则,即每秒执行一次同步操作,always表示每次执行写入都会执行同步,最安全也是最慢, no表示不主动进行同步操作而是跟随操作系统,最快也是最不安全
redis允许同时使用AOF和RDB,既能保证数据安全又使得备份操作简化,此时重新启动时redis会使用AOF文件来恢复数据。
参考书籍:
1.《Redis设计与实现》
2.《Redis入门指南》