【关闭文件、AOF 刷盘、释放内存这三个任务都有各自的任务队列】所以不是单线程
Redis有两种持久化方案:
-
RDB持久化
-
AOF持久化
-
基于Redis集群解决单机Redis存在的问题
【Redis是单进程的】
【也有人做分布式section】
【主从集群中多个从就是做负载均衡的】
单机的Redis存在四大问题:
RDB持久化
【执行 bgsave 过程中,Redis 依然可以继续处理操作命令的,也就是数据是能被修改的,关键的技术就在于写时复制技术(Copy-On-Write, COW)。】
RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据。快照文件称为RDB文件,默认是保存在当前运行目录。【全量拷贝】
1.执行时机
RDB持久化在四种情况下会执行:
-
执行save命令
-
执行bgsave命令
-
Redis停机时
-
触发RDB条件时
1)save命令
执行下面的命令,可以立即执行一次RDB:
redis-cli中执行save
save命令会导致主进程执行RDB,这个过程中其它所有命令都会被阻塞。只有在数据迁移时可能用到。
2)bgsave命令
下面的命令可以异步执行RDB:
bgsave
这个命令执行后会开启独立进程完成RDB,主进程可以持续处理用户请求,不受影响。
3)停机时
Redis停机时会执行一次save命令,实现RDB持久化。
4)触发RDB条件
Redis内部有触发RDB的机制,可以在redis.conf文件中找到,格式如下:
# 900秒内,如果至少有1个key被修改,则执行bgsave , 如果是save "" 则表示禁用RDBsave 900 1 save 300 10 save 60 10000
RDB的其它配置也可以在redis.conf文件中设置:
# 是否压缩 ,建议不开启,压缩也会消耗cpu,磁盘的话不值钱rdbcompression yes# RDB文件名称dbfilename dump.rdb # 文件保存的路径目录dir ./
2.RDB原理
bgsave开始时会fork主进程得到子进程【此时主进程是阻塞的,所以要加快fork的速度】,子进程共享主进程的内存数据。完成fork后读取内存数据并写入 RDB 文件。
fork采用的是copy-on-write(写时复制技术):内存大时还是很耗时,还需要大量磁盘IO,对性能影响大。
-
当主进程执行读操作时,访问共享内存;
-
当主进程执行写操作时,则会拷贝一份数据(数据副本),执行写操作。fork会把共享内存标记为read-only,以后主进程读操作时也往副本,页表变了。
【所有进程都没法操作物理内存,所以由操作系统给每个进程分配一个虚拟内存,操作系统还会维护虚拟内存和物理内存之间的映射关系表(页表),从而实现读写。fork的过程就是对页表做拷贝】
【redis一般预留空间,防止被副本翻倍消耗完】
3.小结
RDB方式bgsave的基本流程?
-
fork主进程得到一个子进程,共享内存空间
-
子进程读取内存数据并写入新的RDB文件
-
用新RDB文件替换旧的RDB文件
RDB会在什么时候执行?save 60 1000代表什么含义?
-
默认是服务停止时
-
代表60秒内至少执行1000次修改则触发RDB
-
宕机数据会丢失
RDB的缺点?
-
RDB执行间隔时间长,两次RDB之间写入数据有丢失的风险
-
fork子进程、压缩、写出RDB文件都比较耗时
AOF持久化
【大大提高数据的安全性,弥补RDB】
1.AOF原理
AOF全称为Append Only File(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件。【不像RDB每次都从头开始写文件,累加】
【先写到Redis,再把命令写到AOF文件,将来恢复只需要重新执行命令】
2.AOF配置
AOF默认是关闭的,需要修改redis.conf配置文件来开启AOF:
# 是否开启AOF功能,默认是noappendonly yes# AOF文件的名称appendfilename "appendonly.aof"
AOF的命令记录的频率也可以通过redis.conf文件来配:
# 表示每执行一次写命令,立即记录到AOF文件,先操作内存再写到磁盘(性能最差)appendfsync always # 写命令执行完先放入AOF缓冲区,然后表示每隔1秒将缓冲区数据写到AOF文件,是默认方案appendfsync everysec # 写命令执行完先放入AOF缓冲区,由操作系统决定何时将缓冲区内容写回磁盘appendfsync no
三种刷盘策略对比:
3.AOF文件重写
【AOF执行rewrite的缓冲区。无法设置容量上限】
因为是记录命令,AOF文件会比RDB文件大的多。而且AOF会记录对同一个key的多次写操作,但只有最后一次写操作才有意义。通过执行bgrewriteaof命令(后台子进程 bgrewriteaof来完成),可以让AOF文件执行重写功能,用最少的命令达到相同效果。
【子进程带有主进程的数据副本,这里使用子进程而不是线程,防止共享数据更改(只读)(主进程修改了会发生「写时复制」,所以用副本保证数据安全)时使用锁降低性能】
【aof文件压缩后看不懂,此时再加命令会接着写,不过不会反压缩回来】
set num 123 和 set num 666
都是对num的操作,第二次会覆盖第一次的值,因此第一个命令记录下来没有意义。【读取当前数据库中的所有键值对,然后将每一个键值对用一条命令记录到「新的 AOF 文件」】
所以重写命令后,AOF文件内容就是:mset name jack num 666
Redis也会在触发阈值时自动去重写AOF文件。阈值也可以在redis.conf中配置:
# AOF文件比上次文件 增长超过多少百分比则触发重写auto-aof-rewrite-percentage 100# AOF文件体积最小多大以上才触发重写 auto-aof-rewrite-min-size 64mb
RDB与AOF对比
RDB和AOF各有自己的优缺点,如果对数据安全性要求较高,在实际开发中往往会结合两者来使用。
【一般两者一起用】
【RDB相当于备份,异地容灾,机房毁了,aof也废了】
【aof写磁盘多,好在是异步的】
混合
前部分是rdb,当同步过程新来的就会进行aof降低数据丢失率。
-
兼容性差,如果开启混合持久化,那么此混合持久化 AOF 文件,就不能用在 Redis 4.0 之前版本了。
混合持久化是在AOF持久化的基础上,定期进行RDB持久化。具体实现方式是在AOF重写时,将RDB文件以二进制压缩格式写入到AOF文件的开头,之后的数据再以AOF格式追加到文件的末尾。这样,在Redis重启时,可以优先加载RDB部分的数据,快速恢复大部分数据,然后再通过AOF部分的命令来更新内存中的数据,以保证数据的完整性。
总结
Redis的持久化虽然可以保证数据安全,但也会带来很多额外的开销,因此持久化请遵循下列建议:
-
用来做缓存的Redis实例尽量不要开启持久化功能【提高查询效率的不需要持久化,查询时再写一遍就好。对于安全性要求高的,比如分布式锁、库存、验订单的流水需要持久化(可以专门放在一个redis实例)】
-
建议关闭RDB持久化功能,使用AOF持久化【可以混合。rdb会丢失数据】
-
利用脚本定期在slave节点做RDB,实现数据备份
-
设置合理的rewrite阈值,避免频繁的bgrewrite
-
配置no-appendfsync-on-rewrite = yes,禁止在rewrite期间做aof,避免因AOF引起的阻塞【主从同步时还是要fork(大量磁盘IO),rewrite也有磁盘IO且时间长,aof刷盘也需要读写磁盘。rewrite占用磁盘高会影响aof刷盘】fsync刷盘
-
部署有关建议:
-
Redis实例的物理机要预留足够内存,应对fork和rewrite【fork可能翻倍,写时复制】
-
单个Redis实例内存上限不要太大,例如4G或8G。可以加快fork的速度、减少主从同步、数据迁移压力
-
不要与CPU密集型应用部署在一起【fork等CPU要求高。es也高】
-
不要与高硬盘负载应用一起部署。例如:数据库、消息队列
-