如果有人问:你会把Redis用在哪里?大概率的回答应该是当做缓存使用,缓解数据库在高并发下的压力。这就让我们不得不考虑一个问题如果Redis实例挂了,内存数据全部丢失我们应该怎么办。
方法一 从数据库慢速恢复,这也许是一个看起来可行的办法,但是实际的情况可能大部分是不行的。由于缓存是为数据库扛压的,也就是说线上流量很大,是数据库扛不住的,缓存失效首先导致的数据库的崩溃,缓存重启如果是空数据,流量依旧会打在数据库上,形成无限崩溃的局面,这时事故在所难免。只能通过限制流量,快速重新预热缓存,重新数据库来恢复服务。
方法二 实现Redis的持久化,用数据文件来恢复。
首先介绍Redis AOF 持久化 。
学过MySQL的同学可能知道数据库写数据是先写redo log日志再写磁盘。这样是为了故障时的恢复。 AOF日志正好相反,Redis是先执行命令,写入内存在记录日志
AOF把命令以文本形式记录。例如:"set testkey testvalue"
为什么Redis选择先执行命令后写日志?Redis的诉求是快,作为缓存可以容忍丢失一部分数据。先执行命令可以保证日志一定正确,且不会阻塞当前写操作。
这种设计还有两个值得说明的问题。
1、如果你把Redis当作数据库来使用,对数据丢失的容忍度较小,则需要调整appendfsync参数的写回策略
- Always 每条命令都立马同步
- Everysec 每秒写回
- No 写回的操作交给操作系统来决定。
2、AOF操作如果太慢会对主线程的性能产生影响
3、AOF的文件太大应该怎么办?太大会让数据恢复非常缓慢 追加命令变慢 占用空间
AOF提供了文件重写机制。压缩同一个key的数据变化
AOF的重写是主进程fork后台子进程bgrewriteaof完成的,fork操作依旧会阻塞主进程,但避免了拷贝所有内存数据阻塞主线程。fork采用了操作系统的写时复制 COW,避免了一次性拷贝大量内存数据造成长时间阻塞。这种方式只会拷贝内存页表(虚拟内存和物理内存的映射表)。所以实际情况不建议单机Redis内存过大。
子进程bgrewriteaof和主进程拥有相同的内存地址空间。如果在子进程拷贝数据给新的AOF文件时,Redis收到写操作,主进程操作一个存在的key,会拷贝这个key对应的内存数据,申请新的内存空间,逐渐主子进程内存数据分离。值得注意的是Linux有一个Huge Page特性,一般的页只有4k大小,大页面可以分配2M,由于如果是bigkey申请了大块内存还是会阻塞主进程,内存申请这么大必然阻塞影响性能。Redis部署机器建议关闭Huge page。