redis从零开始学习系列(三)—— 数据持久化和性能保证

一、数据持久化选项

    redis提供两种不同的持久化方法来将数据存储到硬盘里面。一种方法叫快照,它可以将存在于某一时刻的所有数据都写入硬盘里面;另一种方法叫只追加文件(AOF),它会在执行写命令时,将被执行的写命令复制到硬盘里面。

    1.快照持久化

        redis可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。在创建快照之后,用户可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本,还可以将快照留在原地以便重启服务器时使用。(类似于关系数据库将数据库导出成xxx.sql文件相似

        创建快照的方法:

           (1)客户端可以通过向redis发送bgsave命令来创建一个快照。对于支持bgsave命令的平台来说,redis会调用fork来创建一个子进程,然后子进程负责将快照写入硬盘,而父进程则继续处理命令请求。

            (2)客户端还可以通过向redis发送save命令来创建一个快照,接到save命令的redis服务器在快照创建完毕之前将不再相应任何其他命令。

            (3)如果用户设置了save配置选项,比如save 60 10000,那么从redis最近一次创建快照之后开始算起,当“60秒之内有10000次写入”这个条件被满足时,redis就会自动触发bgsave命令。如果设置多个save选项,只要其中一条满足,redis就会触发一次bgsave命令。

            (4)当redis通过shutdown命令接收到关闭服务器的请求时,或者接收到标准term信号时,会执行一个save命令,阻塞所有客户端,不再执行客户端发送的任何命令,并在save命令执行完毕之后关闭服务。

            (5)当一个redis服务连接另一个redis服务器,并向对方发送sync命令来开始一次复制操作的时候,如果主服务器目前没有在执行bgsave操作,或者主服务器并非刚刚执行完bgsave操作,那么主服务器就会执行bgsave命令。

        快照持久化的缺点:在创建新的快照文件时,redis、系统或者硬件三者之中的任意一个崩溃了,那么redis将会丢失最近一次创建快照之后写入的所有数据。  

        如何恢复没有生成快照文件的数据:首先我们需要在处理日志的同时记录被处理日志的相关信息,一般包括3个参数,分别是一个reids连接;一个存储日志文件的路径;待处理日志文件中各个行的回调函数;通过将日志的处理进度记录到redis里面,程序可以在系统崩溃之后,根据进度记录继续执行之前未完成的处理工作。

    2.AOF持久化

        AOF持久化会将被执行的写命令写到AOF文件的末尾,以此来记录数据发生的变化。因此redis只要从头到尾执行一次AOF文件包含的所有写命令,就可以恢复AOF文件所记录的数据集。

appendfsync选项及同步频率
选项 同步频率
always 每个redis写命令都要同步写入硬盘。这样做会严重降低redis的速度
everysec 每秒执行一次同步,显示地将多个写命令同步到硬盘
no 让操作系统来决定应该何时进行同步

        AOF的缺点:AOF文件的体积大小。因为redis会不断的将被执行的写命令记录到AOF文件中,随着redis不断运行,AOF文件的体积不断增长,在极端的情况下,体积不断增大的AOF文件甚至可能会用完硬盘的所有可用空间

        解决方法:可以向redis发送bgrewriteaof命令,这个命令会通过移除AOF文件中的冗余命令来重写AOF文件,使AOF文件的体积变得尽可能的小。

二、复制(可理解为关系数据库的主从配置)

    复制可以让其他服务器拥有一个不断地更新的数据副本,从而使得拥有数据副本的服务器可以用于处理客户端发送的读请求。

从服务器连接主服务器时的步骤
步骤 主服务器操作 从服务器操作
1 (等待命令进入) 连接(或者重连接)主服务器,发送sync命令
2 开始执行bgsave,并使用缓冲区记录bgsave之后执行的所有写命令 根据配置选项来决定是继续使用现有的数据(如果有的话)来处理客户端的命令请求,还是向发送请求的客户端返回错误
3 bgsave执行完毕,向从服务器发送快照文件,并在发送期间继续使用缓冲区记录被执行的写命令 丢弃所有旧数据(如果有的话),开始载入主服务器发来的快照文件
4 快照文件发送完毕,开始向从服务器发送存储在缓冲区里面的写命令 完成对快照文件的解释操作,像往常一样开始接收命令请求
5 缓冲区存储的写命令发送完毕;从现在开始,每执行一个写命令,就向从服务器发送相同的写命令 执行主服务器发来的所有存储在缓冲区里面的写命令;并从现在开始,接收并执行主服务器传来的每个写命令
当一个从服务器连接一个已有的主服务器时,有时可以重用已有的快照文件
当有新的从服务器连接主服务器时 主服务器的操作
上述的步骤3尚未执行 所有从服务器都会接收到相同的快照文件和相同的缓冲区写命令
上述步骤3正在执行或者已经执行完毕 当主服务与较早的进行连接的从服务器执行完复制所需的5个步骤之后,主服务器会与新连接的从服务器执行一次新的步骤1至步骤5
        如果检测主服务器将数据写到从服务器:用户需要在向主服务器写入真正的数据之后,再向主服务器写入一个唯一的虚构值,然后通过检查虚构值是否存在于从服务器来判断写数据是否已经到达从服务器。

三、redis的事务和非事务型流水线

    redis的事务以特殊命令multi为开始,之后跟着用户传入的多个命令,最后以exec为结束。

    非事务型流水线可以让用户同时执行多个不同的命令,“一次性发送多个命令,然后等待所有回复出现”,它可以减少客户端于redis服务器之间的通信次数,来提高性能。使用就是 pipe = conn.pipeline();

代码实例:

public void updateTokenPipeline(Jedis conn, String token, String user, String item) {
        long timestamp = System.currentTimeMillis() / 1000;
        Pipeline pipe = conn.pipelined();  //设置流水线
        pipe.multi();
        pipe.hset("login:", token, user);
        pipe.zadd("recent:", timestamp, token);
        if (item != null){
            pipe.zadd("viewed:" + token, timestamp, item);
            pipe.zremrangeByRank("viewed:" + token, 0, -26);
            pipe.zincrby("viewed:", -1, item);
        }
        pipe.exec();  //执行那些被流水线包裹的命令
    }

        

猜你喜欢

转载自blog.csdn.net/huxiaodong1994/article/details/80834512