Redis实战——数据安全与性能保障


持久化选项

Redis是一个内存数据库,但为了避免数据丢失,也提供了两种将数据持久化到磁盘的方式:快照方式和AOF方式(只追加文件)。这两种方式各有各的好处,主要看应用场景,而且它俩并不是非此即彼的,它们可以共同使用。

快照持久化

快照持久化保存redis内存数据在某一时刻的副本,可以通过BGSAVESAVE指令来保存快照,BGSAVE在一个后台进程中将快照写入硬盘,SAVE则在Redis服务器的进程中写入,这导致Redis将不能服务新请求。

在redis配置文件中,配置快照的选项有这几个:

save 60 1000
stop-writes-on-bgsave-error on
rdbcompression yes
dbfilename dump.rdb

save 60 1000指定了从上一次保存快照开始,如果60秒内redis接收到了1000次写入,那么就自动触发BGSAVE来保存快照。下图中,redis使用了三个save

  1. 60秒内收到10000次写入
  2. 300秒内收到10次写入
  3. 900秒内收到1次写入

stop-writes-on-bgsave-error on指定了在快照创建失败的情况下仍然继续执行写命令,rdbcompression yes指定了将快照文件进行压缩,dbfilename指定了快照文件的位置,在我这里是src/dump.rdb

更新丢失

如果某些原因使得Redis异常退出,那么自从上一次更新快照之后的所有更新将丢失。

创建快照的办法

  1. BGSAVE
  2. SAVE
  3. 配置文件中指定的自动save
  4. Redis正常退出(接收到关闭服务器的命令或标准的TERM信号),执行一个SAVE命令,阻塞客户端,不再接受任何请求,SAVE执行完毕关闭服务器
  5. 主从复制时,从服务器连接到主服务器后会执行SYNC命令同步数据,此时主服务器需要执行BGSAVE

AOF持久化

当你的应用不能忍受快照持久化的数据丢失,可以使用AOF持久化。

AOF维护一个只追加的文件,当收到写入请求时,将这个命令追加到AOF文件的尾部。Redis只需要执行AOF中记录的所有内容便可以恢复文件中所记录的数据集。

AOF的相关配置如下

appendonly no
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

appendonly是是否开启AOF,appendfsync是何时将写入的文件flush到磁盘,可以选择always每条写指令、everysec每秒钟、no永远不显式同步。always不会丢失更新,但是它会产生大量的磁盘IO,影响Redis的速度,everysec会丢失一秒钟之间的数据,但它不会造成太大的性能下降,no不建议使用,因为何时将指令刷到AOF文件在这种情况下由操作系统决定,我们没法控制。

重写AOF文件

AOF文件的体积会随着系统运行不断增大,重写(rewrite)操作创建一个子进程将AOF文件中冗余的指令去掉,将文件瘦身。

no-appendfsync-on-rewrite指定在重写AOF文件时是否可以向其中追加命令,官方推荐除了你有延迟问题之外,其它情况下都设置为noauto-aof-rewrite-percentage指定了当AOF文件增长了多少百分比时进行rewrite,auto-aof-rewrite-min-size指定了要对AOF文件进行瘦身,它必须大于多少M,这两个是and关系。

主从复制

Redis支持传统的主从复制

一台Redis服务器默认运行在master模式下,该模式是可读可写的。可以通过slaveof host port来连接一个主服务器,连接后,该台服务器变成slave模式,slave模式只读。可以使用slaveof no one来让当前服务器取消与主服务器的连接,重新运行回master模式。

当从服务器连接主服务器后,主服务器执行BGSAVE操作,即后台保存当前数据库的镜像,当这个保存操作完成后,主服务器会通过网络向从服务器发送这个镜像,并且在这期间,主服务器尽量接收并在缓存中处理新来的请求,等待传输结束,主服务器再将这些传输期间接受的命令发给从服务器。

当有新的写命令在主服务器执行,主服务器会向从服务器提交相同的命令。

使用Docker搭建Redis主从复制

拉取redis容器

docker pull redis

运行两个redis实例,一个绑定在本地的20001端口,一个在20002端口

docker run --name redis-20001 -p 20001:6379 -d redis redis-server
docker run --name redis-20002 -p 20002:6379 -d redis redis-server

使用redis-cli连接这两个服务器

redis-cli -h localhost -p 20001
redis-cli -h localhost -p 20002

一会儿,我们使用redis-20001作为主服务器,现在查看主的ip地址

docker inspect redis-20001 | grep IPAddress

现在,我们分别在两台服务器中写入data

现在把redis-20002作为redis-20001的从服务器,再次获取data,已经刷新成主服务器的了

在主服务器中更新,发现从服务器也更新了

主从链

Redis的主服务器可以有多台从服务器来分享读取压力,但随着从服务器变多,主服务器向所有从服务器同步更新的压力越来越重。可以使用如下的树状或任何形状的多层主从复制结构构成主从链。

可以在每台机器上开启appendonly yes选项和appendfsync everysec来进行每秒一次的aof持久化。

处理系统故障

验证快照文件和AOF文件

在系统发生故障后,快照文件和AOF文件有可能处于损坏状态(考虑当更新这两个文件时系统发生断电),Redis提供了两个命令来检查这两个文件是否损坏。

  • redis-check-aof [--fix] ,检查aof文件是否损坏,当--fix被指定,将aof中第一个不完整的命令到文件末尾的所有数据删除

  • redis-check-rdb ,检查rdb文件是否损坏

并没有办法可以修复出错的快照文件,可以通过备份多个并通过散列校验来更加保险的恢复数据。

更换故障主服务器

现在假设,有服务器A和B,A是B的主服务器,这时候A坏了,我们可以通过在B上进行SAVE,然后把B的快照文件通过网络发给C,然后让C来代替A做B的主服务器。

创建两个容器实例,A和B

? docker run --name redis-server-A -p 20001:6379 -d redis
redis-server
d1a5bbc3e63dcb2205d218fb4e732c37c111fcf91c7aa7d947d40527f812d452
? docker run --name redis-server-B -p 20002:6379 -d redis
redis-server
ae32a8e8faf2ebffdbc9fdad293fedf03d9e3c0b9188d0b485c327f151d85f0e

获取它们的IP:

建立A和B的主从关系

现在假设A崩溃,这里把容器A关闭了,B显示已经连不上主服务器了:

在B中使用SAVE保存快照:

将B中的dump.rdb文件拷贝到宿主机:

docker cp redis-server-B:/data/dump.rdb ./

创建容器实例C,并且将dump.rdb导入:

docker run -p20003:6379 --rm --name redis-server-C -v $(pwd)/dump.rdb:/data/dump.rdb redis

这里必须在创建容器时导入,先创建容器后覆盖这个文件没用。

在容器C的启动日志中已经看到了它读取RDB文件的日志

查看B和C的/data/dump.rdb二者的散列值相同,说明二者是一个文件

连接容器C,确实能获取到data

查看容器C的IP地址,它占用了A之前的地址

将B作为C的从服务器,并获取之前由A设置的值

Redis事务

未完...