RDB 和 AOF对比。
RDB和AOF都能实现Redis缓存数据的持久化。
对于RDB来说,通过主线程fork出子进程生成RDB快照文件的方式来保存数据,一般用于中从复制,故障恢复等数据同步场景。
由于是通过fork子进程来实现,所以属于异步执行,在不频繁时不会影响主线程的性能(fork操作由主线程完成),但是在频繁触发时,也会影响主线程。
主要原因是因为:
fork进程操作相当于将当前主线程Copy一份出来,也是需要一定时间的。
假如触发RDB操作间隔较短,每次都需要fork出一个子进程,子进程进行快照生成,如果内存数据太多,带宽不够的情况下会造成上一次fork子进程尚未执行完,新的操作就又开始,然后两个操作均分带宽,更慢,进而导致三个、四个陷入死循环,最后导致不可用。
由于RDB无法频繁触发(一般是在分钟级别,无法做到秒级),所以使用RDB会造成数比较严重的据丢失问题。
对于AOF来说,主要是通过记录写操作命令方式来进行数据持久化,类似与Mysql的binlog文件,一般用于故障恢复,主从复制不会使用。
由于是通过记录写操作命令实现,且是由主线程同步操作,所以会对主线程有一定影响,但是属于顺序写,也很快。此外,由于记录的是每一次的写操作,所以针对同一个key的操作会有很多的冗余,导致AOF文件过大,过大情况对于数据的读取、写入就会有影响,因此AOF提供了重写机制,来减少AOF文件大小。
AOF写入时机有三种,每次写入、每秒写入、NO(操作系统控制写回),所以AOF可以做到秒级的数据持久化,非常可靠。
AOF的重写有哪些风险
AOF重写也RDB一样,都是通过fork子进程来操作的,可以在一定程度上避免主线程阻塞。
AOF重写可以总结为:一个拷贝,两处日志。
一个拷贝是指每次重写时,会把内存拷贝一份给fork出的子进程,然后将这部分数据转换为命令的形式,写入AOF文件。
两处日志是指,由于子进程在写AOF文件时,主线程没有阻塞,还在处理请求,而且重写的AOF文件还未完成,无法代替未重写的AOF文件,因此此时还需要继续往原AOF文件中写入。此时,为了方便子进程写完Copy内存数据后,继续执行这段时间新的写入命令,因此还需要再写一个新的AOF缓冲文件,当AOF缓冲文件也成功写入重写后的AOF日志文件时,就可以替换掉原来的AOF文件。至此完成了AOF重写。
在了解了AOF具体的执行过程后,风险也就呼之欲出了。
- 与RDB一样,既然是fork子进程,那么就需要考虑是否有频繁触发导致主线程不可用风险。因此在频繁的写操作情况下,有可能会不断的触发重写。
- AOF重写时主线程要记录两个文件,频繁写,写长度大的数据,都会对主线程使用造成影响。
主从复制为什么用RDB
主从复制使用RDB是因为,RDB由子进程来操作,且生成RDB文件比较快,以及就像AOF重写后的日志一样,体积比较小,传输很快,恢复就比较快。
RDB 为什么要比AOF快
说RDB比AOF快是因为,RDB文件可以直接加载到内存中。而AOF只能一条、一条的执行命令加载,如果操作日志非常多,Redis恢复就很慢,影响到正常使用。
使用一个 2 核 CPU、4GB 内存、500GB 磁盘的云主机运行 Redis,Redis 数据库的数据量大小差不多是 2GB。当时 Redis主要以修改操作为主,写读比例差不多在 8:2 左右,也就是说,如果有 100 个请求,80 个请求执行的是修改操作。在这个场景下,用 RDB 做持久化有什么风险吗
凡是用RDB做持久化,均有比较严重的数据丢失问题,可以了解一下RDB持久化触发条件。因此在这种写多读少的场景,用RDB做持久化数据丢失风险很高。
由于只有4GB的内存,而且Redis数据有2GB,那么在生成RDB文件时,如果并发较高,写时复制文件大小就有可能为2GB的百分之80,也就是1.6GB,很容易撑满内存,造成OOM。
由于只有2核CPU,在生成RDB文件时主线程、子进程都需要CUP核运行,如果还有其他后台线程在跑,那么此时会发生竞争CPU资源问题,会影响线程的处理速度。
Redis导致阻塞的操作有哪些?
-
操作数据时主线程需要阻塞等待操作结果,返回给客户端。
此时如果操作数据进行复杂的聚合操作,或者查询集合全量数据,那么这种耗时操作就会导致Redis阻塞。
此外对一个大Key的删除也会造成Redis阻塞,因为为了高效的管理和使用内存空间,在应用释放内存时,操作系统需要把释放掉的内存块插入一个空闲内存块的链表,便于后续再分配。这个操作本身需要一定的事假,如果一下释放太多的内存,这个操作时间就会增加,就会造成Redis主线程的阻塞。
同理清空数据库这种同时删除大量Key操作,也会阻塞主线程。
fork子进程时主线程会阻塞。
-
AOF写磁盘操作
因为Redis记录AOF日志是写磁盘文件,而且是由主线程直接操作的,因此会阻塞到主线程。(AOF重写日志是fork子进程实现的)
主从同步时,从库需要阻塞直到同步操作完成。
Redis cluster 是如何做数据迁移的?是否会阻塞主线程?
Redis读写缓存,异步写回场景如何实现?
对于读写缓存来说,如果同步写数据到数据库那么就无法发挥缓存快的优势了,比直接写库还要慢一些。因此可以考虑做异步写回,写内存成功后就直接返回成功,后续再异步写数据库。
对于数据一致性要求很高的建议直接写库好了。
写完内存后,发布一个事件、消息异步完成写库操作。写库不成功记得记录数据并告警,人为修补,保证最终一致性。
如果没有设置过期时间,可以通过定时(注意内存淘汰策略是否设置合理),设置过期时间可以看是否可以监听过期。
Redis内存淘汰策略有哪些?
从类型上说有三种,一种是针对设置了过期时间的,一种是针对所有Key的,最后一种是不淘汰。
对于设置过期时间的来说有:LRU、LFU、Random、TTL
对于所有KEY来说有:LRU、LFU、Random
什么是LRU算法?Redis 如何实现LRU算法?
LFU
Redis 分布式锁与 Zookeeper对比
Redis 实现分布式锁的过程
key/value分别是什么,,怎么考虑重入,怎么考虑超时问题,
RedLock 分布式算法锁 与 单节点锁 对比,区别,使用场景。 为什么不使用集群方式提升高可用。
Redis 实现分布式锁为什么要使用单实例?
Redis ACID 与数据库 ACID 对比
数据库隔离级别,RR 情况,在一个事务未提交时,是否处于锁状态,其他事务无法修改。
ACID中的一致性如何理解?是否有不一致的情况?
Redis怎么实现主从同步。
Redis 主从同步怎么保证数据一致性?
主从同步因为网络或者其他原因导致数据同步耗时较长,从库与主库数据不一致问题。
从库可以读取到主库已过期数据。
哨兵模式下,在主从切换期间,如何处理客户端请求?会阻塞么?
如果原主库在切换期间,恢复正常,此时会出现什么情况?
怎么理解脑裂,脑裂会造成什么影响?
如何应对脑裂问题?
假设我们将min-slaves-to-write设置为1,把min-slaves-max-lag设置为12s,把哨兵的down-after-milliseconds设置为10s,主库因为某些原因卡住了15s,导致哨兵判断主库客观下线,开始进行主从切换。同时,因为原主库卡住了15s,没有一个从库能和原主库在12s内进行数据复制,原主库也无法接收客户端请求了。这样一来,主从切换完成后,也只有新主库能接收请求,不会发生脑裂,也就不会发生数据丢失的问题了。
- 这种情况下,从库和原主库12s内无法进行数据复制,那么12s内主库是否依然可以接收到客户端请求,然后在哨兵进行主从切换时,主从之间就会有12s的数据没有进行同步,会丢失12s的数据。这种情况只能解决脑裂,但是无法保证数据不丢失。
Redis在fork子进程生成RDB文件时,是复制内存数据还是共享内存数据?
Redis是fork子进程生成RDB文件,fork生成的子进程可以共享主线程所有的内存数据,类似与引用传递。但是如果主线程在子进程生成RDB文件时,需要操作内存数据应该怎么办呢?
这就说到了Redis的写时复制技术,写时复制技术会保证共享内存不变,在有新的写入操作时,针对要操作的key复制一份,生成该数据的副本,然后对副本执行修改操作,这样共享的内存并无变化,子进程可以无误的把内存中的原始数据写入RDB文件,写入完成后再到副本中,对修改的数据进行读取。