zoukankan      html  css  js  c++  java
  • Redis持久化怎么选?成年人从来不做选择!

    前言

    面试官:你知道 redis 是的「怎么做持久化」的吗?

    我:我知道 redis 有两种方式,一种是 RDB,一种是 AOF。

    面试官:那这两种方式「具体是怎么做」的,它们的「区别」是什么,生产环境中到底应该怎么选择??

    我:嗯。。。。。。这个我不知道。

    面试官:出门左拐,不送。

    嗯。。。以上场景很真实,都说面试造火箭,入职拧螺丝,今天我们就让面试官再问到这个问题时,把他按在地上摩擦!

    redis的持久化有哪些?

    我们简单的说明一下什么是 redis 的持久化:

    用通俗的语言来说.redis 的持久化就是「将内存中的数据,保存磁盘当中,以便于数据恢复」.

    接下来我们进入正题,说说 redis 持久化的方式 redis 的持久化方式有「两种」:

    • RDB(Redis DataBase)
    • AOF(Append Only File)

    RDB

    RDB(redis database):

    把某个时间点redis内存中的数据以二进制的形式存储的一个.rdb为后缀的文件当中,也就是「周期性的备份redis中的整个数据」,这是redis默认的持久化方式,也就是我们说的快照(snapshot)

    RDB的两种工作方式

    • 「自动提交」

    这是redis中默认的配置,它的意思就是

    在900s内,有1个redis键有变化,就备份一次
    
    在300s内,有10个redis键有变化,就备份一次
    
    在600s内,有10000个redis键有变化,就备份一次
    

    这个参数是我们可以修改的,具体的数值可以根据我们的业务量进行匹配

    • 「手动提交」:手动触发Redis进行RDB持久化的命令有两种:
    • 「save」:该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止。所以说当save命令执行期间,如果有其他命令执行,那么只能阻塞,极大的影响了redis的性能
    • 「bgsave」:执行bgsave命令的时候,redis会自己「fork出一条子进程」,由这条子进程去执行,这样就不会影响到客户端对于redis 的正常操作

    有意思的是,我们都知道,进程与进程之间的内存不是共享的,那么「子进程是如何获取到主进程的内存数据呢?」

    • 真像是在主进程fork子进程的同时,会把「自己内存中的数据同时复制一份给子进程」,这样就相当于子进程可以读取到主进程的数据了,然后子进程就可以愉快的进行io操作了.(将内存中的数据写入磁盘中)

    用心的小伙伴可能已经发现了,既然主进程要把自己的数据复制一份给子进程,那么就是说,会有「双倍的内存占用」.

    简单点来讲,假如你的redis在未fork子进程时就占用了5G内存,那么你的服务器剩余可用内存「至少要达到」5G才可正常的进行fork操作.

    有的小伙伴可能会问,那么「这个复制内存的操作是立即执行的吗」?

    • 其实并不是的,正常情况下redis的服务大部分都是读操作,在fork子进程的时候,子进程其实并没有直接复制一份主进程的数据,而是给他「分配了一个虚拟的内存地址,指向了父进程的内存地址,直到父进程的内存数据发生改变的时候才会进行复制内存的操作」

    又有一个问题来了,「如果在fork期间客户端又发起了新的操作,redis会怎样做呢」

    • 答案相信大家都猜到了,当然是等着了,毕竟 redis 是单线程的, fork 这个过程结束后, redis 的子进程就会进行持久化操作了,所以 redis 主进程理所应当的就「创建了一份新的」 rdb 文件,直到子进程完成持久化操作后,才会删除掉之前的 rdb 文件,以这份新的 rdb 文件「代替」

    所以大家也发现了,fork的时间长短其实是跟当时redis中的数据量有很大关系的,在其他条件恒定的情况下,随着「数据量的增大,redis 的fork操作时间也会变」长.

    为了性能,基本上 R「edis 内部所有的 RDB 操作都是采用 bgsave 命令」,也就是自动提交。

    RDB的优缺点

    RDB的优点:

    • 1.它是将某一时间点redis内的所有数据保存下来,所以当我们做「大型的数据恢复时,RDB的恢复速度会很快」
    • 2.由于RDB的FROK子进程这种机制,队友给客户端提供读写服务的影响会非常小

    RDB的缺点:

    • 1:「有可能会产生长时间的数据丢失」
    • 举个例子假设我们定时5分钟备份一次,在10:00的时候 redis 备份了数据,但是如果在10:04的时候服务挂了,那么我们就会丢失在10:00到10:04的整个数据
    • 2:可能会有长时间停顿
    • 我们前面讲了,fork 子进程这个过程是和 redis 的数据量有很大关系的,如果「数据量很大,那么很有可能会使redis暂停几秒」

    AOF

    AOF(append only file): redis每次执行一个命令时,都会把这个「命令原本的语句记录到一个.aod的文件当中,然后通过fsync策略,将命令执行后的数据持久化到磁盘中」(不包括读命令)

    AOF 重写

    我们知道 AOF 是不断的将「写命令追加」到一个后缀叫 .aof 的文件当中的,那么问题来了,随着我们不断的写命令,aof文件越来越大,那么redis会做什么操作呢? 我们举个简单的例子.

     set a  10
    
     del  a
    
     set a 10
    
     del a
    

    我们执行了以上四条命令,正常来说,就会在.aof文件当中存在这四条命令的身影,但是我们发现,其实当我们执行完这四条命令,我们根本没有修改数据的内容,要知道,redis的本质就是存储数据的,「只要数据内容不发生改变,即使做再多的操作也是没有意义的」.

    redis自然也考虑到了这一点,所以它会自己对.aof文件进行优化,「重建.aof文件」.

    这个文件中包含了当前数据所需要的的最少的命令集 (如:a + 1,a + 1,a + 1 这三个命令会合成为a + 3 这一个命令).

    当然redis并「不会让主进程进行这个操作」,为了防止阻塞,在执行重写操作期间会设置一个「aof重写缓冲区」,仅仅用于在后台进程重写期间,将发生的数据库读写命令写入到重写缓冲区,之后当重写子进程完成重写后,向服务器主进程发送一个信号,此时服务器主进程将aof重写缓冲区中的命令追加到新的aof文件中去,用新的aof文件替换掉旧的aof文件。

    命令同步到磁盘的三种方式:

    • 1:「appendfsync no」
    • Redis不会主动调用fsync去将AOF日志内容同步到磁盘,具体的操作依赖于操作系统,对大多数Linux操作系统,是每30秒进行一次fsync,将缓冲区中的数据写到磁盘上。
    • 2:「appendfsync everysec」
    • Redis会默认每隔一秒进行一次fsync调用,也就是说一次将一秒内的所有命令同步到磁盘中
    • 3:「appednfsync always」
    • 每次写操作都会调用一次fsync

    AOF的优缺点

    • AOF 的「优点」:
    • 1.AOF可以「更好保护数据不丢失」,一般AOF会以每隔1秒,通过后台的一个线程去执行一次fsync操作,如果redis进程挂掉,最多丢失1秒的数据
    • 2.AOF是将命令直接追加在文件末尾的,「写入性能非常高」
    • 3.AOF日志文件的命令通过非常可读的方式进行记录,这个非常「适合做灾难性的误删除紧急恢复」,如果某人不小心用flushall命令清空了所有数据,只要这个时候还没有执行rewrite,那么就可以将日志文件中的flushall删除,进行恢复
    • AOF 的「缺点」:
    • 1.对于同一份数据源来说,一般情况下「AOF文件比RDB数据快照要大」
    • 2.由于.aof每次命令都会写入,那么相对于RDB来说「需要消耗的性能也就更多」
    • 3.「数据恢复比较慢」,不适合做冷备。

    总结

    redis有两种持久化的方式,一种是RDB,一种是AOP

    所谓RDB就是段时间的用快照的方式将redis中的所有数据存储下来,并且通过fork子进程的方式去持久化的,由于redis通常是用来读取数据的,所有中间有一层优化就是写时持久化(进程与进程之间内存不共享,再redis数据发生变动时再fork完成持久化)

    而AOF就是存储一条条执行的redis命令(不包括查询命令),通过命令的方式完成持久化,并且中间还会有AOF重写的操作,主要就是为了节省空间。

    所以「生产环境中到底应该怎么选择」

    「成年人的世界不做选择,我全都要!」

    你学会了吗?下期见~

  • 相关阅读:
    Unity 粒子系统 特效 移除屏幕外面后再移回来 不会显示问题
    同步读取各平台StreamingAssets文件
    cocos2d-x for android 环境搭建&交叉编译
    lua 热更新
    php连接mysql超时问题
    svn仓库自动同步(主库、从库自动同步)
    游戏开发进度、状况以及结果的关系(个人感言)
    centos 重启服务命令
    编译时,输出信息重定向到文件
    vs开发的程序内存错误
  • 原文地址:https://www.cnblogs.com/javadoudi/p/14293524.html
Copyright © 2011-2022 走看看