• Redis持久化


    Redis是内存数据库,但是一旦服务器宕机,内存中的数据将会全部丢失。

    最简单的恢复方式是从后端数据库恢复,但这种方式有两个问题:

    1. 频繁访问数据库,会给数据库带来巨大的压力;
    2. 从数据库中读取相比从Redis中读取要慢很多,会导致应用响应变慢

    因此,Redis要实现持久化,避免从后端数据库中进行恢复。

    Redis有两种持久化机制:AOF(Append Only File)日志和RDB快照。今天先来学习AOF日志。

    什么是AOF日志?

    AOF日志是通过保存Redis写命令来记录数据库数据的。大多数的数据库采用的是写前日志(WAL),例如MySQL,通过写前日志两阶段提交,实现数据和逻辑的一致性。想了解更多关于两阶段提交的内容,点击查看

    而AOF日志采用写后日志,即先写内存,后写日志。

    为什么采用写后日志?

    Redis要求高性能,采用写日志有两方面好处:

    1. 避免额外的检查开销
    2. 不会阻塞当前的写操作

    但这种方式存在潜在风险:

    1. 如果命令执行完成,写日志之前宕机了,会丢失数据。
    2. 主线程写磁盘压力大,导致写盘慢,阻塞后续操作。

    如何实现AOF日志?

    AOF日志记录Redis的每个写命令,步骤分为:命令追加(append)、文件写入(write)和文件同步(sync)。

    命令追加

    当AOF持久化功能打开了,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器的 aof_buf 缓冲区。

    文件写入和同步

    关于何时将 aof_buf 缓冲区的内容写入AOF文件中,Redis提供了三种写回策略:

    • Always,同步写回:每个写命令执行完,立马同步地将日志写回磁盘;
    • Everysec,每秒写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;
    • No,操作系统控制的写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。

    三种写回策略的优缺点

    上面的三种写回策略体现了一个重要原则:trade-off,取舍,指在性能和可靠性保证之间做取舍

    关于AOF的同步策略是涉及到操作系统的 write 函数和 fsync 函数的,在《Redis设计与实现》中是这样说明的:

    为了提高文件写入效率,在现代操作系统中,当用户调用write函数,将一些数据写入文件时,操作系统通常会将数据暂存到一个内存缓冲区里,当缓冲区的空间被填满或超过了指定时限后,才真正将缓冲区的数据写入到磁盘里。

    这样的操作虽然提高了效率,但也为数据写入带来了安全问题:如果计算机停机,内存缓冲区中的数据会丢失。为此,系统提供了fsyncfdatasync同步函数,可以强制操作系统立刻将缓冲区中的数据写入到硬盘里,从而确保写入数据的安全性。

    如何配置AOF?

    默认情况下,Redis是没有开启AOF的,可以通过配置redis.conf文件来开启AOF持久化,关于AOF的配置如下:

    复制代码
    # appendonly参数开启AOF持久化
    appendonly no
    
    # AOF持久化的文件名,默认是appendonly.aof
    appendfilename "appendonly.aof"
    
    # AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的
    dir ./
    
    # 同步策略
    # appendfsync always
    appendfsync everysec
    # appendfsync no
    
    # aof重写期间是否同步
    no-appendfsync-on-rewrite no
    
    # 重写触发配置
    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb
    
    # 加载aof出错如何处理
    aof-load-truncated yes
    
    # 文件重写策略
    aof-rewrite-incremental-fsync yes
    复制代码

    AOF重写

    AOF会记录每个写命令到AOF文件,随着时间越来越长,AOF文件会变得越来越大。如果不加以控制,会对Redis服务器,甚至对操作系统造成影响,而且AOF文件越大,数据恢复也越慢。

    为了解决AOF文件体积膨胀的问题,Redis提供AOF文件重写功能来对AOF文件进行“瘦身”。Redis通过创建一个新的AOF文件来替换现有的AOF,新旧两个AOF文件保存的数据相同,但新AOF文件没有了冗余命令。

    简单来说,AOF重写就是把旧AOF日志文件的多条命令,在重写后变成新日志文件的一条命令。

    AOF重写会阻塞吗?AOF重写是由后台线程bgrewriteaof来完成的。

    AOF重写过程

    用一句话总结:一个拷贝,两处日志。一个拷贝指一份内存拷贝,两处日志分别是一处正在使用的AOF日志,另一处是新的AOF重写日志。

    下图是AOF重写过程:

    AOF重写过程

    拓展

    关于AOF重写过程的潜在阻塞风险

    前面提到AOF重写不会阻塞,指的是在AOF重写过程不会阻塞主线程,因为是通过后台bgrewriteaof线程来执行的。

    但是在fork子进程的时候,fork这个瞬间一定是会阻塞主线程的。

    fork采用的是操作系统提供的写时复制(Copy On Write)机制,避免一次性拷贝造成的阻塞。但fork子进程需要拷贝进程必要的数据结构,其中有一项是拷贝内存页表(虚拟内存和物理内存的映射索引表),这个拷贝过程会消耗大量的CPU资源,在拷贝完成之前,整个进程是会阻塞的。

    拷贝内存页完成后,子进程与父进程指向相同的内存地址空间,也就是说此时虽然产生了子进程,但是并没有申请与父进程相同的内存大小。

    那什么时候父子进程才会真正内存分离呢?在写发生时,才真正拷贝内存的数据,这个过程中,父进程也可能会产生阻塞风险。

    因为内存分配是以页为单位进行分配的,默认4K,如果父进程此时操作的是一个bigkey,重新申请大块内存耗时会变长,可能会产生阻塞风险。

    另外,如果操作系统开启了内存大页机制(Huge Page,页面大小2M),那么父进程申请内存时阻塞的概率将会大大提高,所以在Redis机器上需要关闭Huge Page机制。

    为什么AOF重写不复用原AOF日志

    有两方面原因:

    1. 父子进程写同一个文件会产生竞争问题,影响父进程的性能。
    2. 如果AOF重写过程中失败了,相当于污染了原本的AOF文件,无法做恢复数据使用。

    AOF重写需要手动触发吗?

    可以设置自动触发,通过配置这两个参数auto-aof-rewrite-min-sizeauto-aof-rewrite-percentage

    • auto-aof-rewrite-min-size:表示运行AOF重写时文件的最小大小,默认为64MB
    • auto-aof-rewrite-percentage:当前AOF文件大小和上一次重写后AOF文件大小的差值,再除以上一次重写后AOF文件大小

    当AOF文件大小同时超出上面两个配置项,会触发AOF重写。

  • 相关阅读:
    内置的包装类
    子类继承父类的哪些成员
    JAVA笔记140-使用this语句解决构造器重载相互调用问题
    Java学习
    AngularJs2
    Angular
    检测是否所有的栏位都已经填充了内容了。(可以用来判断动态放置的东西和外加的框是否一致)
    上下各有一个框,框里有元素(点击下面的元素,显示到上面的框里面去,按一定顺序排放)
    Nashorn 在JDK 8中融合Java与JavaScript之力
    2014年Facebook的开源成就
  • 原文地址:https://www.cnblogs.com/ma13461749958/p/14235481.html
Copyright © 2020-2023  润新知