redis 数据持久化

redis 数据持久化

redis 数据持久化的方式:snapshotting (快照)、append-only-file (aof方式)。 其中snapshotting 方式是默认的方式.

snapshotting(快照方式)

快照是redis的默认持久化方式。 这种方式是将内存中的数据以快照的方式写入到二进制文件 dump.rdb。可以通过配置文件设置自动做快照持久化的方式。可以配置redis在n秒内如果超过m个key被修改就可以做快照。

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
// 配置快照的方式

################################ SNAPSHOTTING ################################
#
# Save the DB on disk:
#
# save <seconds> <changes>
#
# Will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# In the example below the behaviour will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
#
# Note: you can disable saving completely by commenting out all "save" lines.
#
# It is also possible to remove all the previously configured save
# points by adding a save directive with a single empty string argument
# like in the following example:
#
# save ""

save 900 1 // 15分钟内如果超过1个key值被修改,则发起快照保存数据
save 300 10 //5 分钟内如果超过10个key值被修改, 则发起快照保存数据
save 60 10000 // 1 分钟内如果超过10000个key值被修改, 则发起快照保存数据

快照的保存过程

1.  redis 调用fork, 产生一个进程。
2.  父进程继续处理 client的请求,子进程负责把内存中的数据写入到临时文件中。由于os的实时复制机制, 父子进程会共享相同的物理页面。当父进程处理写请求时,os会为父进程要修改的页面创建副本, 而不是写共享页面。所以子进程地址空间的数据是fork时刻整个数据的一个快照。
3.  当子进程将快照写入到临时文件完毕后,用临时文件替换原来的快照文件,子进程退出。

缺点:

1.  client也可以使用`save`命令或者`bgsave`命令通知redis做一次持久化。save 操作是在主线程中保存快照。由于redis是一个主线程来处理所有的client的请求。这种方式会阻塞所有的client的请求。所以不推荐使用。
2.  每次快照持久化都是将内存数据完整的写入磁盘一次, 并不是增量的只同步变更的数据,如果数据量大的话,而且写操作比较多, 比如会引起大量磁盘的io操作, 可能会严重影响性能。

快照测试

启动服务:

redis-server

1
2
3
4
5
6
7
// 启动客户端
redis-cli -p 6379
127.0.0.1:6379> set name anziguoer
OK
127.0.0.1:6379> get name
"anziguoer"
127.0.0.1:6379>
1
2
3
4
// 查看服务端的日志信息
50034:M 17 Jul 2019 10:25:20.150 * DB loaded from disk: 0.000 seconds
50034:M 17 Jul 2019 10:25:20.150 * Ready to accept connections
50034:M 17 Jul 2019 10:25:37.147 * DB saved on disk
1
2
3
4
5
// 查看数据库文件 dump.rdb
var/db/redis
➜ ll
total 8
-rw-r--r-- 1 yyl admin 113B 7 17 10:27 dump.rdb // 持久化的数据文件

append-only-file(aof)方式

另外, 由于快照的方式是在一定的时间间隔内做一次快照,如果redis意外down掉的话,就会丢失最后一次快照的所有修改。如果应用要求不能丢失任何的数据的话, 可以采用 aof的方式。

aof比快照有更好的持久化, 是由于在使用aof的持久化时, redis会将收到的每一个写命令都通过write函数追加到文件当中(appendonly.aof)。 当redis重启时会通过重新写命令在内存中重建整个数据库的内容。当然由于os会在内核中缓存write做的修改,所以可能不是立即写到磁盘上面。这样aof方式的持久化也还是可能有丢失部分修改。不过可以通过配置信息告诉redis我们通过fsync函数强制os写入到磁盘的时机。

(aof)方式测试

1
2
3
4
appendonly yes // 开启 aof的持久化
# appendfsync always appendfsync everysec
appendfsync everysec // aof每秒钟写入一次
# appendfsync no

测试方式同snapshotting(快照)。查看文件:

1
2
3
4
5
var/db/redis
➜ ll
total 16
-rw-r--r-- 1 yyl admin 63B 7 17 10:43 appendonly.aof // aof持久化文件
-rw-r--r-- 1 yyl admin 113B 7 17 10:42 dump.rdb

aof 方式问题

aof 的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用 incr test 命令 100 次,文件中必须保存全部的 100 条命令,其实有 99 条都是多余的。因为要恢复数 据库的状态其实文件中保存一条 set test 100 就够了。为了压缩 aof 的持久化文件。redis 提 供了 bgrewriteaof 命令。收到此命令 redis 将使用与快照类似的方式将内存中的数据以命令 的方式保存到临时文件中,最后替换原来的文件。具体过程如下:

  • redis 调用 fork ,现在有父子两个进程
  • 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
  • 父进程继续处理 client 请求,除了把写命令写入到原来的 aof 文件中。同时把收到的写命 令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。
  • 当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。然 后父进程把缓存的写命令也写入到临时文件。
  • 现在父进程可以使用临时文件替换老的 aof 文件,并重命名,后面收到的写命令也开始 往新的 aof 文件中追加。

参考

  • redis 实战
坚持原创技术分享,您的支持将鼓励我继续创作!
//