文章目录
  1. 1. 环境准备
  2. 2. 版本日志
  3. 3. RDB方式持久化
  4. 4. AOF方式持久化
  5. 5. 备份建议
  6. 6. 总结

通过示例分析,深入了解redis数据持久化策略执行机制;

环境准备

1
➜ docker run -itd --name lredis -p7002:6379 -v ~/workbench/docker/redis/conf/redis.conf:/etc/redis/redis.conf redis redis-server /etc/redis/redis.conf
版本日志

1
2
3
4
5
6
7
➜ docker logs -f lredis
1:C 24 Oct 2018 09:24:03.601 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 24 Oct 2018 09:24:03.602 # Redis version=5.0.0, bits=64, commit=00000000, modified=0, pid=1, just started
...
1:M 24 Oct 2018 09:24:03.606 # Server initialized
1:M 24 Oct 2018 09:24:03.606 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
1:M 24 Oct 2018 09:24:03.606 * Ready to accept connections
RDB方式持久化

Redis默认的持久化方式是RDB,并且默认是打开的。RDB的保存有方式分为主动保存被动保存主动保存可以在redis-cli中输入save即可;被动保存需要满足配置文件中设定的触发条件,满足触发条件后,数据才会被保存为快照,正是因为这样才说RDB的数据完整性是比不上AOF;
触发保存条件后,会在指定的目录生成一个名为dump.rdb的文件,等到下一次启动Redis时,Redis会去读取该目录下的dump.rdb文件,将里面的数据恢复到Redis;

dump.rdb文件路径,

1
2
3
4
5
➜  ~ docker exec -it lredis redis-cli
127.0.0.1:6379> config get dir
1) "dir"
2) "/data"
127.0.0.1:6379>

目前官方默认的触发条件可以在redis.conf中看到,

1
2
3
4
5
save 900 1              #在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。

save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。

save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。

为了便于测试, 把上面默认的配置修改为,

1
2
3
4
5
save 900 1              #在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。

save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。

save 20 5 #在20秒之后,如果至少有5个key发生变化,则dump内存快照。

  1. RDB被动触发保存实践
    input
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    ➜  ~ docker exec -it lredis redis-cli
    127.0.0.1:6379> set a 1
    OK
    127.0.0.1:6379> set b 1
    OK
    127.0.0.1:6379> set c 1
    OK
    127.0.0.1:6379> set d 1
    OK
    127.0.0.1:6379> set e 1
    OK
    127.0.0.1:6379> set f 1
    OK
    127.0.0.1:6379> scan 0
    1) "0"
    2) 1) "a"
    2) "e"
    3) "b"
    4) "d"
    5) "f"
    6) "c"

RDB被动保存成功生成了rdb文件及日志,

1
2
➜  ~ docker exec -it lredis ls /data
dump.rdb

日志记录,

1
2
3
4
5
6
7
1:M 24 Oct 2018 09:47:49.394 * DB loaded from disk: 0.000 seconds
1:M 24 Oct 2018 09:47:49.394 * Ready to accept connections
1:M 24 Oct 2018 09:48:48.758 * 5 changes in 20 seconds. Saving...
1:M 24 Oct 2018 09:48:48.759 * Background saving started by pid 25
25:C 24 Oct 2018 09:48:48.768 * DB saved on disk
25:C 24 Oct 2018 09:48:48.769 * RDB: 0 MB of memory used by copy-on-write
1:M 24 Oct 2018 09:48:48.860 * Background saving terminated with success

日志提示redis检测到20秒内有至少5条记录被改动,满足redis.conf中对于RDB数据保存的条件,所以这里执行数据保存操作,并且提示开辟了一个25的进程出来执行保存操作,最后提示保存成功;

现在将redis进程kill,哪些数据会被保存?

通过命令docker restart lredis模拟Redis异常关闭,然后再启动Redis,再次查看之前set设置的内容,

1
2
3
4
5
6
7
8
127.0.0.1:6379> scan 0
1) "0"
2) 1) "b"
2) "e"
3) "f"
4) "d"
5) "a"
127.0.0.1:6379>

发现c不见了, 可见RDB方式的数据完整性是不可靠的,除非断掉的那一刻正好是满足触发条件的条数;

关闭RDB方式持久化
修改redis.conf配置为,

1
2
3
4
save ""
#save 900 1
#save 300 10
#save 20 5

重复上述RDB被动保存过程,

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
32
33
34
35
36
➜ docker exec -it lredis redis-cli
127.0.0.1:6379> scan 0
1) "0"
2) 1) "a"
2) "d"
3) "c"
4) "e"
5) "b"
127.0.0.1:6379> set f 1
OK
127.0.0.1:6379> set i 1
OK
127.0.0.1:6379> set j 1
OK
127.0.0.1:6379> scan 0
1) "0"
2) 1) "a"
2) "j"
3) "f"
4) "d"
5) "c"
6) "e"
7) "b"
8) "i"
127.0.0.1:6379> exit
➜ docker restart lredis
lredis
➜ docker exec -it lredis redis-cli
127.0.0.1:6379> scan 0
1) "0"
2) 1) "e"
2) "d"
3) "b"
4) "c"
5) "a"
127.0.0.1:6379>

发现后面添加的3条记录并没有被保存,恢复数据的时候仅仅只是恢复了之前的5条。并且观察Redis服务端窗口日志,并未发现像之前一样的触发保存的提示,证明RDB方式已经被关闭;

通过配置文件关闭被动触发,那么主动关闭是否还会生效呢?

  1. RDB主动保存实践
    通过del命令删除几条记录,然后输入save命令执行保存操作,
    input opt
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    127.0.0.1:6379> scan 0
    1) "0"
    2) 1) "e"
    2) "d"
    3) "b"
    4) "c"
    5) "a"
    127.0.0.1:6379> del e d
    (integer) 2
    127.0.0.1:6379> scan 0
    1) "0"
    2) 1) "b"
    2) "c"
    3) "a"
    127.0.0.1:6379> save
    OK

log output

1
2
1:M 24 Oct 2018 10:09:55.295 * Ready to accept connections
1:M 24 Oct 2018 10:14:28.366 * DB saved on disk

然后执行 docker restart lredis,

1
2
3
4
5
6
7
8
9
➜  ~ docker restart lredis
lredis
➜ ~ docker exec -it lredis redis-cli
127.0.0.1:6379> scan 0
1) "0"
2) 1) "a"
2) "c"
3) "b"
127.0.0.1:6379>

可以看到当RDB被动保存关闭后,可以通过主动save保存成功, 证明主动关闭不受 配置文件的影响;

除了save还有其他的保存方式么?

Redis提供了savebgsave这两种不同的保存方式, 并且这两个方式在执行的时候都会调用rdbSave函数,但它们调用的方式各有不同:

save直接调用rdbSave方法, 阻塞Redis主进程,直到保存完成为止。在主进程阻塞期间,服务器不能处理客户端的任何请求。

bgsavefork出一个子进程,子进程负责调用rdbSave,并在保存完成之后向主进程发送信号,通知保存已完成。因为rdbSave在子进程被调用,所以 Redis服务器在bgsave执行期间仍然可以继续处理客户端的请求。

显然, save是同步操作, bgsave是异步操作。bgsave命令的使用方法和save命令的使用方法是一样的,

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> scan 0
1) "0"
2) 1) "a"
2) "c"
3) "b"
127.0.0.1:6379> set e 1
OK
127.0.0.1:6379> set d 1
OK
127.0.0.1:6379> bgsave
Background saving started

redis除了提供savebgsave来保存数据外, 还可以通过shutdown命令来保存数据,不过要让shutdown保存数据生效需要打开持久化配置才行,即应将redis.conf中的配置从

1
save ""

修改为如下,打开被动保存持久化配置才生效;

1
2
3
save 900 1
save 300 10
save 60 10000

总结RDB持久化有被动保存主动保存两种方式,

被动保存即通过redis.conf通过保存条件触发被动保存,这种情况数据有可能丢失;
主动保存即通过显示执行save, bgsave,shutdown(在被动保存配置下才生效)命令,这种情况数据不会丢失;

AOF方式持久化

redis默认没有开启AOF,需要修改redis.conf配置文件中开启,

1
2
# 将appendonly改为 yes
appendonly yes

AOF可以需要设置同步方式

1
2
3
appendfsync always  # 每次有数据修改发生时都会写入AOF文件(安全但是费时)。
appendfsync everysec # 每秒钟同步一次,该策略为AOF的缺省策略。
appendfsync no # 从不同步。高效但是数据不会被持久化。

根据上面的设置重启redis后,可见data目录下已创建了aof空文件,

1
2
3
4
➜  ~ docker exec -it lredis ls -lh /data
total 4.0K
-rw-r--r-- 1 redis redis 0 Oct 24 10:31 appendonly.aof
-rw-r--r-- 1 redis redis 122 Oct 24 10:20 dump.rdb

input

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
32
33
34
35
36
37
38
39
40
41
42
43
44
➜  ~ docker exec -it lredis redis-cli
127.0.0.1:6379> set name china
OK
127.0.0.1:6379> set age 80
OK
127.0.0.1:6379> set city "shanghai.china"
OK
127.0.0.1:6379> scan 0
1) "0"
2) 1) "age"
2) "name"
3) "city"
127.0.0.1:6379> quit
➜ ~ docker exec -it lredis cat /data/appendonly.aof
*2
$6
SELECT
$1
0
*3
$3
set
$4
name
$5
china
*3
$3
set
$3
age
$2
80
*3
$3
set
$4
city
$14
shanghai.china
➜ ~ docker exec -it lredis ls -lh /data
total 8.0K
-rw-r--r-- 1 redis redis 131 Oct 24 10:35 appendonly.aof
-rw-r--r-- 1 redis redis 122 Oct 24 10:20 dump.rdb

由上可见, appendonly.aof文件由0增大到131 bytes, 可见AOF方式持久化成功了, 可以看见appendonly.aof文件中不仅仅保存了设置的变量及值,这些变量及值前后还有一些特殊的符号,这正是根据redis采用的RESP文本协议生成的, 详情可见之前总结的 redis专题12 redis通信协议
分析

1
2
3
$14
shanghai.china
多字符用$开头, $后边紧跟字符串的长度, 然后是 \r\n, 然后是字符串值本身

AOF 同样也会把del等执行命令保存到AOF文件中;

当关闭RDB持久化方式, 只打开AOF方式时, 显示执行savebgsave 都会将当前数据同时保存到AOF文件和RDB文件中;

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
➜ docker exec -it lredis ls -lh /data
total 0
-rw-r--r-- 1 redis redis 0 Oct 24 10:55 appendonly.aof
➜ docker exec -it lredis redis-cli
127.0.0.1:6379> set name china
OK
127.0.0.1:6379> set age 80
OK
127.0.0.1:6379> quit
➜ docker exec -it lredis ls -lh /data
total 4.0K
-rw-r--r-- 1 redis redis 87 Oct 24 10:55 appendonly.aof
➜ docker exec -it lredis redis-cli
127.0.0.1:6379> set city shanghai
OK
127.0.0.1:6379> save
OK
127.0.0.1:6379> quit
➜ docker exec -it lredis ls -lh /data
total 8.0K
-rw-r--r-- 1 redis redis 124 Oct 24 10:56 appendonly.aof
-rw-r--r-- 1 redis redis 131 Oct 24 10:56 dump.rdb
➜ docker exec -it lredis redis-cli
127.0.0.1:6379> set local library
OK
127.0.0.1:6379> bgsave
Background saving started
127.0.0.1:6379> quit
➜ docker exec -it lredis ls -lh /data
total 8.0K
-rw-r--r-- 1 redis redis 161 Oct 24 10:56 appendonly.aof
-rw-r--r-- 1 redis redis 146 Oct 24 10:56 dump.rdb
➜ docker exec -it lredis cat /data/appendonly.aof
*2
$6
SELECT
$1
0
*3
$3
set
$4
name
$5
china
*3
$3
set
$3
age
$2
80
*3
$3
set
$4
city
$8
shanghai
*3
$3
set
$5
local
$7
library

RDB方式切换到AOF方式
Redis2.2或以上版本,可以在不重启的情况下,从RDB切换到AOF,
为最新的dump.rdb文件创建一个备份、将备份放到一个安全的地方。执行以下两条命令:

1
2
redis-cli config set appendonly yes
redis-cli config set save “”

执行的第一条命令开启了AOF功能: Redis会阻塞直到初始AOF文件创建完成为止, 之后Redis会继续处理命令请求, 并开始将写入命令追加到AOF文件末尾;
通过上述CONFIG SET命令设置的配置, 在重启redis服务器之后将失效,重启会依然按照之前的配置启动,所以建议在redis.conf配置中也应同步修改;

备份建议

确保你的数据有完整的备份,磁盘故障、节点失效等问题问题可能让你的数据消失不见, 不进行备份是非常危险的。
Redis对于数据备份是非常友好的, 因为你可以在服务器运行的时候对RDB文件进行复制, RDB 文件一旦被创建, 就不会进行任何修改。 当服务器要创建一个新的RDB文件时,先将文件的内容保存在一个临时文件里面, 当临时文件写入完毕时, 程序才使用rename(2)原子地用临时文件替换原来的RDB文件。
即无论何时, 复制RDB文件都是绝对安全的。

创建一个定期任务, 每小时将一个RDB文件备份到一个文件夹, 并且每天将一个RDB文件备份到另一个文件夹;

确保快照的备份都带有相应的日期和时间信息, 每次执行定期任务脚本时, 使用 find 命令来删除过期的快照, 比如保留最近48小时内的每小时快照, 还可以保留最近一两个月的每日快照;

至少每天一次, 将RDB 备份到你的数据中心之外, 或者至少是备份到你运行Redis服务器的物理机器之外;

总结

  • 为实践redis持久化机制,创建了docker容器环境;
  • 针对RDB方式持久化,分别测试了其主动保存和被动保存机制, 被动保存存在丢数据的可能,而主动保存则不会, 被动保存通过配置触发保存条件实现, 主动保存主要通过显示执行save,bgsave,shutdown(需开启被动保存)来执行数据保存操作;
  • 针对AOF方式持久化进行了实例分析测试,AOF 开启后自动保存操作记录到AOF文件, 当显示执行save, bgsave,shutdown操作时也会自动保存数据到AOFRDB文件中;
  • 探讨了在不停服情况下从RDB方式切换至AOF的方法,并给出了几点备份建议;

作者署名:朴实的一线攻城狮
本文标题:redis专题16 数据持久化实践
本文出处:http://researchlab.github.io/2018/10/13/redis-16-storage-solution-opt/
版权声明:本文由Lee Hong创作和发表,采用署名(BY)-非商业性使用(NC)-相同方式共享(SA)国际许可协议进行许可,转载请注明作者及出处, 否则保留追究法律责任的权利。

@全栈炼狱之路

关注微信公众号 @全栈炼狱之路

总访问:
总访客: