Redis实战——数据安全与性能保障
持久化选项
Redis是一个内存数据库,但为了避免数据丢失,也提供了两种将数据持久化到磁盘的方式:快照方式和AOF方式(只追加文件)。这两种方式各有各的好处,主要看应用场景,而且它俩并不是非此即彼的,它们可以共同使用。
快照持久化
快照持久化保存redis内存数据在某一时刻的副本,可以通过BGSAVE和SAVE指令来保存快照,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:
- 60秒内收到10000次写入
- 300秒内收到10次写入
- 900秒内收到1次写入
stop-writes-on-bgsave-error on指定了在快照创建失败的情况下仍然继续执行写命令,rdbcompression yes指定了将快照文件进行压缩,dbfilename指定了快照文件的位置,在我这里是src/dump.rdb
更新丢失
如果某些原因使得Redis异常退出,那么自从上一次更新快照之后的所有更新将丢失。
创建快照的办法
- BGSAVE
- SAVE
- 配置文件中指定的自动save
- Redis正常退出(接收到关闭服务器的命令或标准的TERM信号),执行一个SAVE命令,阻塞客户端,不再接受任何请求,SAVE执行完毕关闭服务器
- 主从复制时,从服务器连接到主服务器后会执行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文件时是否可以向其中追加命令,官方推荐除了你有延迟问题之外,其它情况下都设置为no。auto-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事务
未完...