Redis有两种持久化的方式:快照(RDB
文件)和追加式文件(AOF
文件)html
-
RDB持久化方式是在一个特定的间隔保存某个时间点的一个数据快照。redis
-
AOF(Append only file)持久化方式则会记录每个服务器收到的写操做。数据回复时,这些记录的操做会逐条执行从而重建出原来的数据。写操做命令 记录的格式跟Redis协议一致,以追加的方式进行保存。数据库
Redis的持久化是能够禁用的,两种方式的持久化是能够同时存在的,可是当Redis重启时,AOF文件会被优先用于重建数据。缓存
1、RDB
RDB就是Snapshot存储,是默认的持久化方式。按照必定的策略周期性的将数据保存到磁盘。对应产生的数据文件为dump.rdb,经过配置文件中的save参数来定义快照的周期。Redis支持将当前数据的快照存成一个数据文件实现持久化。而一个持续写入的数据库如何生成快照呢。Redis借助了fork命令的copy on write机制。在生成快照时,将当前进程fork出一个子进程,而后在子进程中循环全部的数据,将数据写成为RDB文件。安全
Client 也可使用save或者bgsave命令通知redis作一次快照持久化。save操做是在主线程中保存快照的,因为redis是用一个主线程来处理全部 client的请求,这种方式会阻塞全部client请求,因此不推荐使用。另外一点须要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不 是增量的只同步脏数据。若是数据量大的话,并且写操做比较多,必然会引发大量的磁盘io操做,可能会严重影响性能。 bash
Redis的RDB文件不会坏掉,由于其写操做是在一个新进程中进行的。当生成一个新的RDB文件时,Redis生成的子进程会先将数据写到一个临时文件中,而后经过原子性rename系统调用将临时文件重命名为RDB文件。这样在任什么时候候出现故障,Redis的RDB文件都老是可用的。而且Redis的RDB文件也是Redis主从同步内部实现中的一环服务器
主从同步
第一次Slave向Master同步的实现是:app
Slave向Master发出同步请求,Master先dump出rdb文件,而后将rdb文件全量传输给slave,而后Master把缓存的命令转发给Slave,初次同步完成。工具
第二次以及之后的同步实现是:post
Master将变量的快照直接实时依次发送给各个Slave。但无论什么缘由致使Slave和Master断开重连都会重复以上两个步骤的过程。
Redis的主从复制是创建在内存快照的持久化基础上的,只要有Slave就必定会有内存快照发生。
工做原理
-
Redis调用fork(),产生一个子进程。
-
父进程继续处理client请求,子进程把内存数据写到一个临时的RDB文件。因为os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面建立副本,而不是写共享的页面。因此子进程的地址空间内的数据是fork时刻整个数据库的一个快照。
-
当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,而后子进程退出
优势
-
RDB文件是一个很简洁的单文件,它保存了某个时间点的Redis数据,很适合用于作备份。你能够设定一个时间点对RDB文件进行归档,这样就能在须要的时候很轻易的把数据恢复到不一样的版本。
-
RDB很适合用于灾备。单文件很方便就能传输到远程的服务器上。
-
RDB的性能很好,须要进行持久化时,主进程会fork一个子进程出来,而后把持久化的工做交给子进程,本身不会有相关的I/O操做。
-
比起AOF,在数据量比较大的状况下,RDB的启动速度更快。
缺点
-
RDB容易形成数据的丢失。假设每5分钟保存一次快照,若是Redis由于某些缘由不能正常工做,那么从上次产生快照到Redis出现问题这段时间的数据就会丢失了。
-
RDB使用
fork()
产生子进程进行数据的持久化,若是数据比较大的话可能就会花费点时间,形成Redis中止服务几毫秒。若是数据量很大且CPU性能不是很好的时候,中止服务的时间甚至会到1秒。
文件路径和名称
默认Redis会把快照文件存储为当前目录下一个名为dump.rdb
的文件。要修改文件的存储路径和名称,能够经过修改配置文件redis.conf
实现:
1
2
3
4
5
|
# RDB文件名,默认为dump.rdb。
dbfilename dump.rdb
# 文件存放的目录,AOF文件一样存放在此目录下。默认为当前工做目录。
dir
./
|
保存点(RDB的启用和禁用)
你能够配置保存点,使Redis若是在每N秒后数据发生了M次改变就保存快照文件。例以下面这个保存点配置表示每60秒,若是数据发生了1000次以上的变更,Redis就会自动保存快照文件:
1
|
save 60 1000
|
保存点能够设置多个,Redis的配置文件就默认设置了3个保存点:
1
2
3
4
5
|
# 格式为:save <seconds> <changes>
# 能够设置多个。
save 900 1
#900秒后至少1个key有变更
save 300 10
#300秒后至少10个key有变更
save 60 10000
#60秒后至少10000个key有变更
|
若是想禁用快照保存的功能,能够经过注释掉全部"save"配置达到,或者在最后一条"save"配置后添加以下的配置:
1
|
save
""
|
错误处理
默认状况下,若是Redis在后台生成快照的时候失败,那么就会中止接收数据,目的是让用户能知道数据没有持久化成功。可是若是你有其余的方式能够监控到Redis及其持久化的状态,那么能够把这个功能禁止掉。
1
|
stop-writes-on-bgsave-error
yes
|
数据压缩
默认Redis会采用LZF
对数据进行压缩。若是你想节省点CPU的性能,你能够把压缩功能禁用掉,可是数据集就会比没压缩的时候要打。
1
|
rdbcompression
yes
|
数据校验
从版本5的RDB的开始,一个CRC64
的校验码会放在文件的末尾。这样更能保证文件的完整性,可是在保存或者加载文件时会损失必定的性能(大概10%)。若是想追求更高的性能,能够把它禁用掉,这样文件在写入校验码时会用0
替代,加载的时候看到0
就会直接跳过校验
1
|
rdbchecksum
yes
|
手动生成快照
Redis提供了两个命令用于手动生成快照。
SAVE
SAVE命令会使用同步的方式生成RDB快照文件,这意味着在这个过程当中会阻塞全部其余客户端的请求。所以不建议在生产环境使用这个命令,除非由于某种缘由须要去阻止Redis使用子进程进行后台生成快照(例如调用fork(2)
出错)。
BGSAVE
BGSAVE命令使用后台的方式保存RDB文件,调用此命令后,会马上返回OK
返回码。Redis会产生一个子进程进行处理并马上恢复对客户端的服务。在客户端咱们可使用LASTSAVE命令查看操做是否成功。
1
2
3
4
|
127.0.0.1:6379> BGSAVE
Background saving started
127.0.0.1:6379> LASTSAVE
(integer) 1433936394
|
配置文件里禁用了快照生成功能不影响
SAVE
和BGSAVE
命令的效果。
2、AOF
快照并非很可靠。若是服务器忽然Crash了,那么最新的数据就会丢失。而AOF文件则提供了一种更为可靠的持久化方式。每当Redis接受到会修改数据集的命令时,就会把命令追加到AOF文件里,当你重启Redis时,AOF里的命令会被从新执行一次,重建数据
原理
-
redis调用fork ,如今有父子两个进程
-
子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
-
父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证若是子进程重写失败的话并不会出问题
-
当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。而后父进程把缓存的写命令也写入到临时文件
-
如今父进程可使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加
优势
-
比RDB可靠。你能够制定不一样的fsync策略:不进行fsync、每秒fsync一次和每次查询进行fsync。默认是每秒fsync一次。这意味着你最多丢失一秒钟的数据。
-
AOF日志文件是一个纯追加的文件。就算服务器忽然Crash,也不会出现日志的定位或者损坏问题。甚至若是由于某些缘由(例如磁盘满了)命令只写了一半到日志文件里,咱们也能够用
redis-check-aof
这个工具很简单的进行修复。 -
当AOF文件太大时,Redis会自动在后台进行重写。重写很安全,由于重写是在一个新的文件上进行,同时Redis会继续往旧的文件追加数据。新文件上会写入能重建当前数据集的最小操做命令的集合。当新文件重写完,Redis会把新旧文件进行切换,而后开始把数据写到新文件上。
-
AOF把操做命令以简单易懂的格式一条接一条的保存在文件里,很容易导出来用于恢复数据。例如咱们不当心用
FLUSHALL
命令把全部数据刷掉了,只要文件没有被重写,咱们能够把服务停掉,把最后那条命令删掉,而后重启服务,这样就能把被刷掉的数据恢复回来。
缺点
-
在相同的数据集下,AOF文件的大小通常会比RDB文件大。
-
在某些fsync策略下,AOF的速度会比RDB慢。一般fsync设置为每秒一次就能得到比较高的性能,而在禁止fsync的状况下速度能够达到RDB的水平。
-
在过去曾经发现一些很罕见的BUG致使使用AOF重建的数据跟原数据不一致的问题。
启用AOF
把配置项appendonly
设为yes
:
1
|
appendonly
yes
|
文件路径和名称
1
2
3
4
5
|
# 文件存放目录,与RDB共用。默认为当前工做目录。
dir
./
# 默认文件名为appendonly.aof
appendfilename
"appendonly.aof"
|
可靠性
你能够配置Redis调用fsync的频率,有三个选项:
-
每当有新命令追加到AOF的时候调用fsync。速度最慢,可是最安全。
-
每秒fsync一次。速度快(2.4版本跟快照方式速度差很少),安全性不错(最多丢失1秒的数据)。
-
从不fsync,交由系统去处理。这个方式速度最快,可是安全性没有保证
推荐使用每秒fsync一次的方式(默认的方式),由于它速度快,安全性也不错。相关配置以下:
1
2
3
|
# appendfsync always
appendfsync everysec
# appendfsync no
|
日志重写
随着写操做的不断增长,AOF文件会愈来愈大。例如你递增一个计数器100次,那么最终结果就是数据集里的计数器的值为最终的递增结果,可是AOF文件里却会把这100次操做完整的记录下来。而事实上要恢复这个记录,只须要1个命令就好了,也就是说AOF文件里那100条命令其实能够精简为1条。因此Redis支持这样一个功能:在不中断服务的状况下在后台重建AOF文件。
工做原理以下:
-
Redis调用fork(),产生一个子进程。
-
子进程把新的AOF写到一个临时文件里。
-
主进程持续把新的变更写到内存里的buffer,同时也会把这些新的变更写到旧的AOF里,这样即便重写失败也能保证数据的安全。
-
当子进程完成文件的重写后,主进程会得到一个信号,而后把内存里的buffer追加到子进程生成的那个新AOF里。
咱们能够经过配置设置日志重写的条件:
1
2
3
4
5
6
7
8
9
10
|
#在日志重写时,不进行命令追加操做,而只是将其放在缓冲区里,避免与命令的追加形成DISK IO上的冲突。
#设置为yes表示rewrite期间对新写操做不fsync,暂时存在内存中,等rewrite完成后再写入,默认为no,建议yes
no-appendfsync-on-rewrite
yes
# Redis会记住自从上一次重写后AOF文件的大小(若是自Redis启动后还没重写过,则记住启动时使用的AOF文件的大小)。
# 若是当前的文件大小比起记住的那个大小超过指定的百分比,则会触发重写。
# 同时须要设置一个文件大小最小值,只有大于这个值文件才会重写,以防文件很小,可是已经达到百分比的状况。
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
|
要禁用自动的日志重写功能,咱们能够把百分比设置为0:
1
|
auto-aof-rewrite-percentage 0
|
Redis 2.4以上才能够自动进行日志重写,以前的版本须要手动运行BGREWRITEAOF这个命令。
数据损坏修复
若是由于某些缘由(例如服务器崩溃)AOF文件损坏了,致使Redis加载不了,能够经过如下方式进行修复:
-
备份AOF文件。
-
使用
redis-check-aof
命令修复原始的AOF文件:1$ redis-check-aof --fix
-
可使用
diff -u
命令看下两个文件的差别。 -
使用修复过的文件重启Redis服务。
从RDB切换到AOF
这里只说Redis >= 2.2版本的方式:
-
备份一个最新的
dump.rdb
的文件,并把备份文件放在一个安全的地方。 -
运行如下两条命令:
12$ redis-cli config
set
appendonly
yes
$ redis-cli config
set
save
""
-
确保数据跟切换前一致。
-
确保数据正确的写到AOF文件里。
第二条命令是用来禁用RDB的持久化方式,可是这不是必须的,由于你能够同时启用两种持久化方式。
记得对配置文件
redis.conf
进行编辑启用AOF,由于命令行方式修改配置在重启Redis后就会失效。
从上面看出,RDB和AOF操做都是顺序IO操做,性能都很高。而同时在经过RDB文件或者AOF日志进行数据库恢复的时候,也是顺序的读取数据加载到内存中。因此也不会形成磁盘的随机读。
到底选择什么呢?下面是来自官方的建议:
一般,若是你要想提供很高的数据保障性,那么建议你同时使用两种持久化方式。若是你能够接受灾难带来的几分钟的数据丢失,那么你能够仅使用RDB。不少用户仅使用了AOF,可是咱们建议,既然RDB能够时不时的给数据作个完整的快照,而且提供更快的重启,因此最好仍是也使用RDB。
在数据恢复方面:RDB的启动时间会更短,缘由有两个
-
一是RDB文件中每一条数据只有一条记录,不会像AOF日志那样可能有一条数据的屡次操做记录。因此每条数据只须要写一次就好了。
-
另外一个缘由是RDB文件的存储格式和Redis数据在内存中的编码格式是一致的,不须要再进行数据编码工做,因此在CPU消耗上要远小于AOF日志的加载。
注意:
上面说了RDB快照的持久化,须要注意:在进行快照的时候(save),fork出来进行dump操做的子进程会占用与父进程同样的内存,真正的copy-on-write,对性能的影响和内存的耗用都是比较大的。好比机器8G内存,Redis已经使用了6G内存,这时save的话会再生成6G,变成12G,大于系统的8G。这时候会发生交换;要是虚拟内存不够则会崩溃,致使数据丢失。因此在用redis的时候必定对系统内存作好容量规划。
目前,一般的设计思路是利用Replication机制来弥补aof、snapshot性能上的不足,达到了数据可持久化。即Master上Snapshot和AOF都不作,来保证Master的读写性能,而Slave上则同时开启Snapshot和AOF来进行持久化,保证数据的安全性。
3、对Redis持久化的测试
经过上面的理论对snapshot和aof有了必定的理解,下面开始进行一些测试
一、redis.conf 开启snapshot 关闭aof
1
2
3
4
5
6
7
8
9
10
|
save 900 1
save 300 10
save 60 10000
rdbcompression no
rdbchecksum no
dbfilename redis.rdb
dir
/home/backup/redis
appendonly no
|
测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
[root@localhost redis]
# ./src/redis-cli
127.0.0.1:6379> keys *
1)
"a"
127.0.0.1:6379>
set
b 2
OK
127.0.0.1:6379>
set
c 3
OK
127.0.0.1:6379>
set
d 4
OK
127.0.0.1:6379> keys *
1)
"c"
2)
"a"
3)
"aa"
4)
"b"
5)
"d"
127.0.0.1:6379> save
OK
#保存,进行持久化,每执行一次save,会在日至里面记录一条:" * DB saved on disk "
127.0.0.1:6379> lpush aa 1
(integer) 1
127.0.0.1:6379> lpush aa 2
(integer) 2
|
持久化验证,重启redis
1
2
3
4
5
6
|
127.0.0.1:6379> keys *
1)
"c"
2)
"a"
3)
"aa"
4)
"b"
5)
"d"
|
lpush 操做在 save以后,可是重启以后仍然有这个数据
什么缘由呢,咱们能够查看一下日志
1
2
3
4
5
6
|
6720:signal-handler (1453738444) Received SIGTERM scheduling
shutdown
...
6720:M 26 Jan 00:14:04.896
# User requested shutdown...
6720:M 26 Jan 00:14:04.896 * Saving the final RDB snapshot before exiting.
6720:M 26 Jan 00:14:04.932 * DB saved on disk
6720:M 26 Jan 00:14:04.932 * Removing the pid
file
.
6720:M 26 Jan 00:14:04.932
# Redis is now ready to exit, bye bye...
|
从日志里面能够看到,正常关闭redis,在关闭前执行save命令。 用kill的效果和上面同样,属于正常关闭
那异常关闭呢?当以kill -9 的形式发送信号
1
2
3
4
5
6
|
127.0.0.1:6379>
set
ss 1
Could not connect to Redis at 127.0.0.1:6379: Connection refused
not connected> get ss
Could not connect to Redis at 127.0.0.1:6379: Connection refused
not connected> get ss
(nil)
|
经过测试,开启RDB持久化,在知足save条件、手动save、正常关闭的时候数据都会被持久化;而异常关闭终止的时候数据会丢失
二、redis.conf 关闭snapshot,关闭aof
1
2
3
4
5
6
7
8
9
|
#save 900 1
#save 300 10
#save 60 10000
rdbcompression no
rdbchecksum no
dbfilename redis.rdb
dir
./
appendonly no
|
操做
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
redis 127.0.0.1:6379> keys *
(empty list or
set
)
redis 127.0.0.1:6379>
set
name
test
OK
redis 127.0.0.1:6379> save
OK
redis 127.0.0.1:6379>
set
aa 1
OK
redis 127.0.0.1:6379>
#重启redis
redis 127.0.0.1:6379> keys *
#发现刚才没有被保存的key丢失了
1)
"name"
|
从上面的结果看出,关闭持久化,只有在手动save的时候数据都会被持久化,正常关闭的时候数据丢失。若是从一开始到关闭写入数据的期间没有手动save,则数据所有丢失,既然能手动save间接的说明了快照一直都存在,因此不能说是禁止snapshot,应该是禁止自动snapshot功能。
三、redis.conf 关闭snapshot,开启aof
1
2
3
4
5
6
7
8
|
appendonly
yes
appendfilename redis.aof
# appendfsync always
appendfsync everysec
# appendfsync no
no-appendfsync-on-rewrite no
auto-aof-rewrite-min-size 64mb
|
操做
1
2
3
4
5
6
7
8
9
10
11
12
13
|
redis 127.0.0.1:6379> keys *
1)
"name"
#修改开启AOF参数,重启数据库:
redis 127.0.0.1:6379> keys *
(empty list or
set
)
redis 127.0.0.1:6379>
#数据库里面没有记录
#查看日志:
#* DB loaded from append only file: 0.000 seconds
#发现是从0字节的aof文件里面同步数据,为何不一样步rdb的数据?原来redis代码里面写好了优先级,AOF>RDB
|
查看源代码 redis.c grep 'DB loaded from' ./ -R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
void loadDataFromDisk(void) {
long long start = ustime();
if
(server.aof_state == REDIS_AOF_ON) {
if
(loadAppendOnlyFile(server.aof_filename) == REDIS_OK)
redisLog(REDIS_NOTICE,
"DB loaded from append only file: %.3f seconds"
,(float)(ustime()-start)
/1000000
);
}
else
{
if
(rdbLoad(server.rdb_filename) == REDIS_OK) {
redisLog(REDIS_NOTICE,
"DB loaded from disk: %.3f seconds"
,
(float)(ustime()-start)
/1000000
);
}
else
if
(errno != ENOENT) {
redisLog(REDIS_WARNING,
"Fatal error loading the DB: %s. Exiting."
,strerror(errno));
exit
(1);
}
}
}
|
这里须要注意的是:当中途开启AOF,重启让生效的时候,不能第2次正常重启了
由于第一次重启让aof生效的时候,启动redis已经读取这个文件了,致使此时的redis数据为空的(优先级)。第二次重启,则会把这个空的数据save到RDB文件,这样致使RDB原有的数据被替换,致使数据丢失。因此必定要当心,为了不悲剧的发生,当要重启redis的时候最好都备份下RDB文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
redis 127.0.0.1:6379> keys *
(empty list or
set
)
redis 127.0.0.1:6379>
set
name tt
OK
redis 127.0.0.1:6379> save
OK
#开启aof参数
#第一次重启
redis 127.0.0.1:6379> keys *
#如上面所说的优先级缘由:aof > rdb,结果为空
(empty list or
set
)
#第2次正常重启,把上面空的结果save到了RDB,数据丢失。此时的db是空的,日志记录 "* DB saved on disk"
redis 127.0.0.1:6379> keys *
(empty list or
set
)
#数据已经被初始化了,数据丢失
|
这里就有一个问题,好比在用redis的时候,刚开始只开启RDB的持久方式,AOF没有开启,在跑一段时间以后想开启AOF,那如何把RDB的数据直接写到AOF文件呢?有2种方法
a、在开启AOF以前,先执行bgrewriteaof,再重启
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
redis 127.0.0.1:6379> keys *
#查看是否有数据
(empty list or
set
)
redis 127.0.0.1:6379>
set
name ttd
OK
redis 127.0.0.1:6379> keys *
1)
"name"
redis 127.0.0.1:6379> bgsave
#保存数据
Background saving started
redis 127.0.0.1:6379> keys *
1)
"name"
#只有一个RDB文件,没有AOF文件
redis 127.0.0.1:6379> bgrewriteaof
#执行合并重写功能,生成AOF文件
Background append only
file
rewriting started
#这时候去打开redis.conf 文件中的aof参数(appendonly yes),重启生效。
#日志里面出现:* DB loaded from append only file: 0.000 seconds
redis 127.0.0.1:6379> keys *
#数据还在
1)
"name"
#查看文件
[root@localhost data]
# od -c redis.aof
0000000 * 2 \r \n $ 6 \r \n S E L E C T \r \n
0000020 $ 1 \r \n 0 \r \n * 3 \r \n $ 3 \r \n S
0000040 E T \r \n $ 4 \r \n n a m e \r \n $ 4
0000060 \r \n j a c k \r \n
0000070
|
b、利用CONFIG GET/SET 的方法动态修改配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
redis 127.0.0.1:6379> BGSAVE
Background saving started
#此时,只有rdb文件
#动态修改参数,把aof功能开启:appendonly yes
redis 127.0.0.1:6379> CONFIG SET appendonly
yes
#动态修改参数
OK
redis 127.0.0.1:6379> CONFIG GET append*
1)
"appendonly"
2)
"yes"
3)
"appendfsync"
4)
"everysec"
redis 127.0.0.1:6379>
#aof文件已经生成,而且有数据(同步rdb)
#日志里面的信息:* Background append only file rewriting started by pid 3165
#由于参数是动态修改的,在重启以后会失效,因此在维护的时候修改redis.conf文件的参数便可
|
从上面的结果看出,redis重启载入数据的时候,读取aof的文件要先于rdb文件,因此尽可能一开始开启aof选项,不要在中途开启。
经过日志能够很清楚的知道redis经过那个文件来取数据的:
1
2
|
RDB: * DB loaded from disk: 0.000 seconds
AOF: * DB loaded from append only
file
: 0.000 seconds
|
保存数据则是
1
2
|
RDB:* DB saved on disk
AOF: * Calling fsync() on the AOF
file
|
四、redis.conf 开启snapshot,开启aof
1
2
3
4
5
6
7
8
9
10
11
12
|
save 900 1
save 300 10
save 60 10000
appendonly
yes
appendfilename zhoujy.aof
# appendfsync always
appendfsync everysec
# appendfsync no
no-appendfsync-on-rewrite no
auto-aof-rewrite-min-size 64mb
|
经过上面的这些测试,已经说明RDB和AOF他们的操做方式,以及如重启时的载入,重启时将按照如下优先级恢复数据到内存:
-
若是只配置AOF,重启时加载AOF文件恢复数据
-
若是同时 配置了RBD和AOF,启动是只加载AOF文件恢复数据
-
若是只配置RDB,启动时候加载dump文件恢复数据
4、Redis数据备份
备份很简单,只
关于Redis持久化
Redis有两种持久化的方式:快照(RDB
文件)和追加式文件(AOF
文件)
-
RDB持久化方式是在一个特定的间隔保存某个时间点的一个数据快照。
-
AOF(Append only file)持久化方式则会记录每个服务器收到的写操做。数据回复时,这些记录的操做会逐条执行从而重建出原来的数据。写操做命令 记录的格式跟Redis协议一致,以追加的方式进行保存。
Redis的持久化是能够禁用的,两种方式的持久化是能够同时存在的,可是当Redis重启时,AOF文件会被优先用于重建数据。
1、RDB
RDB就是Snapshot存储,是默认的持久化方式。按照必定的策略周期性的将数据保存到磁盘。对应产生的数据文件为dump.rdb,经过配置文件中的save参数来定义快照的周期。Redis支持将当前数据的快照存成一个数据文件实现持久化。而一个持续写入的数据库如何生成快照呢。Redis借助了fork命令的copy on write机制。在生成快照时,将当前进程fork出一个子进程,而后在子进程中循环全部的数据,将数据写成为RDB文件。
Client 也可使用save或者bgsave命令通知redis作一次快照持久化。save操做是在主线程中保存快照的,因为redis是用一个主线程来处理全部 client的请求,这种方式会阻塞全部client请求,因此不推荐使用。另外一点须要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不 是增量的只同步脏数据。若是数据量大的话,并且写操做比较多,必然会引发大量的磁盘io操做,可能会严重影响性能。
Redis的RDB文件不会坏掉,由于其写操做是在一个新进程中进行的。当生成一个新的RDB文件时,Redis生成的子进程会先将数据写到一个临时文件中,而后经过原子性rename系统调用将临时文件重命名为RDB文件。这样在任什么时候候出现故障,Redis的RDB文件都老是可用的。而且Redis的RDB文件也是Redis主从同步内部实现中的一环
主从同步
第一次Slave向Master同步的实现是:
Slave向Master发出同步请求,Master先dump出rdb文件,而后将rdb文件全量传输给slave,而后Master把缓存的命令转发给Slave,初次同步完成。
第二次以及之后的同步实现是:
Master将变量的快照直接实时依次发送给各个Slave。但无论什么缘由致使Slave和Master断开重连都会重复以上两个步骤的过程。
Redis的主从复制是创建在内存快照的持久化基础上的,只要有Slave就必定会有内存快照发生。
工做原理
-
Redis调用fork(),产生一个子进程。
-
父进程继续处理client请求,子进程把内存数据写到一个临时的RDB文件。因为os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面建立副本,而不是写共享的页面。因此子进程的地址空间内的数据是fork时刻整个数据库的一个快照。
-
当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,而后子进程退出
优势
-
RDB文件是一个很简洁的单文件,它保存了某个时间点的Redis数据,很适合用于作备份。你能够设定一个时间点对RDB文件进行归档,这样就能在须要的时候很轻易的把数据恢复到不一样的版本。
-
RDB很适合用于灾备。单文件很方便就能传输到远程的服务器上。
-
RDB的性能很好,须要进行持久化时,主进程会fork一个子进程出来,而后把持久化的工做交给子进程,本身不会有相关的I/O操做。
-
比起AOF,在数据量比较大的状况下,RDB的启动速度更快。
缺点
-
RDB容易形成数据的丢失。假设每5分钟保存一次快照,若是Redis由于某些缘由不能正常工做,那么从上次产生快照到Redis出现问题这段时间的数据就会丢失了。
-
RDB使用
fork()
产生子进程进行数据的持久化,若是数据比较大的话可能就会花费点时间,形成Redis中止服务几毫秒。若是数据量很大且CPU性能不是很好的时候,中止服务的时间甚至会到1秒。
文件路径和名称
默认Redis会把快照文件存储为当前目录下一个名为dump.rdb
的文件。要修改文件的存储路径和名称,能够经过修改配置文件redis.conf
实现:
1
2
3
4
5
|
# RDB文件名,默认为dump.rdb。
dbfilename dump.rdb
# 文件存放的目录,AOF文件一样存放在此目录下。默认为当前工做目录。
dir
./
|
保存点(RDB的启用和禁用)
你能够配置保存点,使Redis若是在每N秒后数据发生了M次改变就保存快照文件。例以下面这个保存点配置表示每60秒,若是数据发生了1000次以上的变更,Redis就会自动保存快照文件:
1
|
save 60 1000
|
保存点能够设置多个,Redis的配置文件就默认设置了3个保存点:
1
2
3
4
5
|
# 格式为:save <seconds> <changes>
# 能够设置多个。
save 900 1
#900秒后至少1个key有变更
save 300 10
#300秒后至少10个key有变更
save 60 10000
#60秒后至少10000个key有变更
|
若是想禁用快照保存的功能,能够经过注释掉全部"save"配置达到,或者在最后一条"save"配置后添加以下的配置:
1
|
save
""
|
错误处理
默认状况下,若是Redis在后台生成快照的时候失败,那么就会中止接收数据,目的是让用户能知道数据没有持久化成功。可是若是你有其余的方式能够监控到Redis及其持久化的状态,那么能够把这个功能禁止掉。
1
|
stop-writes-on-bgsave-error
yes
|
数据压缩
默认Redis会采用LZF
对数据进行压缩。若是你想节省点CPU的性能,你能够把压缩功能禁用掉,可是数据集就会比没压缩的时候要打。
1
|
rdbcompression
yes
|
数据校验
从版本5的RDB的开始,一个CRC64
的校验码会放在文件的末尾。这样更能保证文件的完整性,可是在保存或者加载文件时会损失必定的性能(大概10%)。若是想追求更高的性能,能够把它禁用掉,这样文件在写入校验码时会用0
替代,加载的时候看到0
就会直接跳过校验
1
|
rdbchecksum
yes
|
手动生成快照
Redis提供了两个命令用于手动生成快照。
SAVE
SAVE命令会使用同步的方式生成RDB快照文件,这意味着在这个过程当中会阻塞全部其余客户端的请求。所以不建议在生产环境使用这个命令,除非由于某种缘由须要去阻止Redis使用子进程进行后台生成快照(例如调用fork(2)
出错)。
BGSAVE
BGSAVE命令使用后台的方式保存RDB文件,调用此命令后,会马上返回OK
返回码。Redis会产生一个子进程进行处理并马上恢复对客户端的服务。在客户端咱们可使用LASTSAVE命令查看操做是否成功。
1
2
3
4
|
127.0.0.1:6379> BGSAVE
Background saving started
127.0.0.1:6379> LASTSAVE
(integer) 1433936394
|
配置文件里禁用了快照生成功能不影响
SAVE
和BGSAVE
命令的效果。
2、AOF
快照并非很可靠。若是服务器忽然Crash了,那么最新的数据就会丢失。而AOF文件则提供了一种更为可靠的持久化方式。每当Redis接受到会修改数据集的命令时,就会把命令追加到AOF文件里,当你重启Redis时,AOF里的命令会被从新执行一次,重建数据
原理
-
redis调用fork ,如今有父子两个进程
-
子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
-
父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证若是子进程重写失败的话并不会出问题
-
当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。而后父进程把缓存的写命令也写入到临时文件
-
如今父进程可使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加
优势
-
比RDB可靠。你能够制定不一样的fsync策略:不进行fsync、每秒fsync一次和每次查询进行fsync。默认是每秒fsync一次。这意味着你最多丢失一秒钟的数据。
-
AOF日志文件是一个纯追加的文件。就算服务器忽然Crash,也不会出现日志的定位或者损坏问题。甚至若是由于某些缘由(例如磁盘满了)命令只写了一半到日志文件里,咱们也能够用
redis-check-aof
这个工具很简单的进行修复。 -
当AOF文件太大时,Redis会自动在后台进行重写。重写很安全,由于重写是在一个新的文件上进行,同时Redis会继续往旧的文件追加数据。新文件上会写入能重建当前数据集的最小操做命令的集合。当新文件重写完,Redis会把新旧文件进行切换,而后开始把数据写到新文件上。
-
AOF把操做命令以简单易懂的格式一条接一条的保存在文件里,很容易导出来用于恢复数据。例如咱们不当心用
FLUSHALL
命令把全部数据刷掉了,只要文件没有被重写,咱们能够把服务停掉,把最后那条命令删掉,而后重启服务,这样就能把被刷掉的数据恢复回来。
缺点
-
在相同的数据集下,AOF文件的大小通常会比RDB文件大。
-
在某些fsync策略下,AOF的速度会比RDB慢。一般fsync设置为每秒一次就能得到比较高的性能,而在禁止fsync的状况下速度能够达到RDB的水平。
-
在过去曾经发现一些很罕见的BUG致使使用AOF重建的数据跟原数据不一致的问题。
启用AOF
把配置项appendonly
设为yes
:
1
|
appendonly
yes
|
文件路径和名称
1
2
3
4
5
|
# 文件存放目录,与RDB共用。默认为当前工做目录。
dir
./
# 默认文件名为appendonly.aof
appendfilename
"appendonly.aof"
|
可靠性
你能够配置Redis调用fsync的频率,有三个选项:
-
每当有新命令追加到AOF的时候调用fsync。速度最慢,可是最安全。
-
每秒fsync一次。速度快(2.4版本跟快照方式速度差很少),安全性不错(最多丢失1秒的数据)。
-
从不fsync,交由系统去处理。这个方式速度最快,可是安全性没有保证
推荐使用每秒fsync一次的方式(默认的方式),由于它速度快,安全性也不错。相关配置以下:
1
2
3
|
# appendfsync always
appendfsync everysec
# appendfsync no
|
日志重写
随着写操做的不断增长,AOF文件会愈来愈大。例如你递增一个计数器100次,那么最终结果就是数据集里的计数器的值为最终的递增结果,可是AOF文件里却会把这100次操做完整的记录下来。而事实上要恢复这个记录,只须要1个命令就好了,也就是说AOF文件里那100条命令其实能够精简为1条。因此Redis支持这样一个功能:在不中断服务的状况下在后台重建AOF文件。
工做原理以下:
-
Redis调用fork(),产生一个子进程。
-
子进程把新的AOF写到一个临时文件里。
-
主进程持续把新的变更写到内存里的buffer,同时也会把这些新的变更写到旧的AOF里,这样即便重写失败也能保证数据的安全。
-
当子进程完成文件的重写后,主进程会得到一个信号,而后把内存里的buffer追加到子进程生成的那个新AOF里。
咱们能够经过配置设置日志重写的条件:
1
2
3
4
5
6
7
8
9
10
|
#在日志重写时,不进行命令追加操做,而只是将其放在缓冲区里,避免与命令的追加形成DISK IO上的冲突。
#设置为yes表示rewrite期间对新写操做不fsync,暂时存在内存中,等rewrite完成后再写入,默认为no,建议yes
no-appendfsync-on-rewrite
yes
# Redis会记住自从上一次重写后AOF文件的大小(若是自Redis启动后还没重写过,则记住启动时使用的AOF文件的大小)。
# 若是当前的文件大小比起记住的那个大小超过指定的百分比,则会触发重写。
# 同时须要设置一个文件大小最小值,只有大于这个值文件才会重写,以防文件很小,可是已经达到百分比的状况。
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
|
要禁用自动的日志重写功能,咱们能够把百分比设置为0:
1
|
auto-aof-rewrite-percentage 0
|
Redis 2.4以上才能够自动进行日志重写,以前的版本须要手动运行BGREWRITEAOF这个命令。
数据损坏修复
若是由于某些缘由(例如服务器崩溃)AOF文件损坏了,致使Redis加载不了,能够经过如下方式进行修复:
-
备份AOF文件。
-
使用
redis-check-aof
命令修复原始的AOF文件:1$ redis-check-aof --fix
-
可使用
diff -u
命令看下两个文件的差别。 -
使用修复过的文件重启Redis服务。
从RDB切换到AOF
这里只说Redis >= 2.2版本的方式:
-
备份一个最新的
dump.rdb
的文件,并把备份文件放在一个安全的地方。 -
运行如下两条命令:
12$ redis-cli config
set
appendonly
yes
$ redis-cli config
set
save
""
-
确保数据跟切换前一致。
-
确保数据正确的写到AOF文件里。
第二条命令是用来禁用RDB的持久化方式,可是这不是必须的,由于你能够同时启用两种持久化方式。
记得对配置文件
redis.conf
进行编辑启用AOF,由于命令行方式修改配置在重启Redis后就会失效。
从上面看出,RDB和AOF操做都是顺序IO操做,性能都很高。而同时在经过RDB文件或者AOF日志进行数据库恢复的时候,也是顺序的读取数据加载到内存中。因此也不会形成磁盘的随机读。
到底选择什么呢?下面是来自官方的建议:
一般,若是你要想提供很高的数据保障性,那么建议你同时使用两种持久化方式。若是你能够接受灾难带来的几分钟的数据丢失,那么你能够仅使用RDB。不少用户仅使用了AOF,可是咱们建议,既然RDB能够时不时的给数据作个完整的快照,而且提供更快的重启,因此最好仍是也使用RDB。
在数据恢复方面:RDB的启动时间会更短,缘由有两个
-
一是RDB文件中每一条数据只有一条记录,不会像AOF日志那样可能有一条数据的屡次操做记录。因此每条数据只须要写一次就好了。
-
另外一个缘由是RDB文件的存储格式和Redis数据在内存中的编码格式是一致的,不须要再进行数据编码工做,因此在CPU消耗上要远小于AOF日志的加载。
注意:
上面说了RDB快照的持久化,须要注意:在进行快照的时候(save),fork出来进行dump操做的子进程会占用与父进程同样的内存,真正的copy-on-write,对性能的影响和内存的耗用都是比较大的。好比机器8G内存,Redis已经使用了6G内存,这时save的话会再生成6G,变成12G,大于系统的8G。这时候会发生交换;要是虚拟内存不够则会崩溃,致使数据丢失。因此在用redis的时候必定对系统内存作好容量规划。
目前,一般的设计思路是利用Replication机制来弥补aof、snapshot性能上的不足,达到了数据可持久化。即Master上Snapshot和AOF都不作,来保证Master的读写性能,而Slave上则同时开启Snapshot和AOF来进行持久化,保证数据的安全性。