美文网首页
redis原理分析

redis原理分析

作者: 不给起这个名字 | 来源:发表于2019-07-17 15:23 被阅读0次

    过期时间设置

    在Redis中提供了Expire命令设置一个键的过期时间,到期以后Redis会自动删除它。
    EXPIRE key seconds 其中seconds 参数表示键的过期时间,单位为秒。
    EXPIRE 返回值为1表示设置成功,0表示设置失败或者键不存在
    如果想知道一个键还有多久时间被删除,可以使用TTL命令
    TTL key
    当键不存在时,TTL命令会返回-2
    而对于没有给指定键设置过期时间的,通过TTL命令会返回-1

    过期删除的原理

    Redis 中的主键失效是如何实现的,即失效的主键是如何删除的?实际上,Redis 删除失效主键的方法主要有两种:
    消极方法(passive way)
    在主键被访问时如果发现它已经失效,那么就删除它
    积极方法(active way)
    周期性地从设置了失效时间的主键中选择一部分失效的主键删除
    对于那些从未被查询的key,即便它们已经过期,被动方式也无法清除。因此Redis会周期性地随机测试一些key,已过期的key将会被删掉。Redis每秒会进行10次操作,具体的流程:

    1. 随机抽取20个带有timeout信息的key;
    2. 删除其中已经过期的key;
    3. 如果超过25%的key被删除,则重复执行步骤1;

    Redis数据持久化

    Redis支持两种方式的持久化,一种是RDB方式、另一种是AOF(append-only-file)方式。前者会根据指定的规则“定时”将内存中的数据存储在硬盘上,而后者在每次执行命令后将命令本身记录下来。两种持久化方式可以单独使用其中一种,也可以将这两种方式结合使用。两种开启时,会进行RDB和AOF的持久化,但第一次初始化时会读取AOF的数据

    RDB方式

    当符合一定条件时,Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件(快照)中,等到持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。
    Redis会在以下几种情况下对数据进行快照

    1. 根据配置规则进行自动快照
      Redis允许用户自定义快照条件,当符合快照条件时,Redis会自动执行快照操作。快照的条件可以由用户在配置文件中配置。配置格式如下
      save [seconds] [changes]
      第一个参数seconds是时间窗口,第二个changes是键的更改个数,也就是说,在第一个时间参数配置范围内被更改的键的个数大于changes时,即符合快照条件,会执行一次RDB持久化操作。
      redis默认配置了三个规则
      save 900 1
      save 300 10
      save 60 10000
      每条快照规则占一行,每条规则之间是“或”的关系(只要满足一个条件就会进行快照操作)。 在900秒(15分)内有一个以上的键被更改则进行快照。

    2. 用户执行SAVE或者GBSAVE命令
      除了让Redis自动进行快照以外,当我们对服务进行重启或者服务器迁移我们需要人工去干预备份。redis提供了两条命令来完成这个任务
      \1. save命令
      当执行save命令时,Redis同步做快照操作,在快照执行过程中会阻塞所有来自客户端的请求。当redis内存中的数据较多时,通过该命令将导致Redis较长时间的不响应。所以不建议在生产环境上使用这个命令,而是推荐使用
      bgsave命令
      \2. bgsave命令
      bgsave命令可以在后台异步地进行快照操作,快照的同时服务器还可以继续响应来自客户端的请求。执行BGSAVE后,Redis会立即返回ok表示开始执行快照操作。
      通过LASTSAVE命令可以获取最近一次成功执行快照的时间; (自动快照采用的是异步快照操作)

    3. 执行FLUSHALL命令
      FLUSHALL命令会清除redis在内存中的所有数据。执行该命令后,只要redis中配置的快照规则不为空,也就
      是save 的规则存在。redis就会执行一次快照操作。不管规则是什么样的都会执行。如果没有定义快照规则,就不会执行快照操作

    4. 执行复制(replication)时
      该操作主要是在主从模式下,redis会在复制初始化时进行自动快照

    RDB的优点:
    Redis在保存RDB快照时会fork出子进程进行(异步),几乎不影响Redis处理客户端请求的效率,使用RDB文件进行数据恢复比使用AOF要快很多。
    每次快照会生成一个完整的数据快照文件,所以可以辅以其他手段保存多个时间点的快照(例如把每天0点的快照备份至其他存储媒介中),作为非常可靠的灾难恢复手段。
    RDB的缺点:
    快照是定期生成的,所以在Redis crash时或多或少会丢失一部分数据。
    如果数据集非常大且CPU不够强(比如单核CPU),Redis在fork子进程时可能会消耗相对较长的时间,影响Redis对外提供服务的能力。

    AOF方式

    AOF可以将Redis执行的每一条写命令以日志方式追加到硬盘中,这一过程会降低Redis的性能,但大部分情况下这个影响是能够接受的,另外使用较快的硬盘可以提高AOF的性能。
    开启AOF
    默认情况下Redis没有开启AOF(append only file)方式的持久化,可以通过appendonly参数启用,在redis.conf中找到 appendonly yes
    开启AOF持久化后每执行一条会更改Redis中的数据的命令后,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是apendonly.aof. 可以在redis.conf中的属性 appendfilename appendonlyh.aof修改

    AOF的日志文件重写
    AOF文件以纯文本的形式记录Redis执行的写命令,每次写操作都会记录到AOF文件,会产生冗余。
    比如对相同key执行3次set命令
    set foo 1
    set foo 2
    set foo 3
    AOF会把这三次命令都记录到AOF文件中,但是实际只需要记录最后一次set命令就可以了

    随着执行的命令越来越多,AOF文件的大小也会越来越大,其实内存中实际的数据可能没有多少,那这样就会造成磁盘空间以及redis数据还原的过程比较长的问题。因此我们希望Redis可以自动优化AOF文件,就上面这个例子来说,前面两条是可以被删除的。 而实际上Redis也考虑到了,可以配置一个条件,每当达到一定条件时Redis就会自动重写AOF文件,这个条件的配置 auto-aof-rewritepercentage 100 auto-aof-rewrite-min-size 64mb
    auto-aof-rewrite-percentage 表示的是当目前的AOF文件大小超过上一次重写时的AOF文件大小的百分之多少时会再次进行重写,如果之前没有重写过,则以启动时AOF文件大小为依据auto-aof-rewrite-min-size 表示限制了允许重写的最小AOF文件大小,通常在AOF文件很小的情况下即使其中有很
    多冗余的命令我们也并不太关心。另外,还可以通过BGREWRITEAOF 命令手动执行AOF,执行完以后冗余的命令已经被删除了在启动时,Redis会逐个执行AOF文件中的命令来将硬盘中的数据载入到内存中,载入的速度相对于RDB会慢一些

    AOF的重写原理

    Redis 可以在 AOF 文件体积变得过大时,自动地在后台对AOF 进行重写:重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。
    重写的流程是这样,主进程会fork一个子进程出来进行AOF重写(异步),这个重写过程并不是基于原有的aof文件来做的,而是有点类似于快照的方式,全量遍历内存中的数据,然后逐个序列到aof文件中。在fork子进程这个过程中,服务端仍然可以对外提供服务,那这个时候重写的aof文件的数据和redis内存数据不一致了怎么办?不用担心,这个过程中,主进程的数据更新操作,会缓存到aof_rewrite_buf中,也就是单独开辟一块缓存来存储重写期间收到的命令,当子进程重写完以后再把缓存中的数据追加到新的aof文件。
    当所有的数据全部追加到新的aof文件中后,把新的aof文件重命名为,此后所有的操作都会被写入新的aof文件。如果在rewrite过程中出现故障,不会影响原来aof文件的正常工作,只有当rewrite完成后才会切换文件。因此这个rewrite过程是比较可靠的

    Redis内存回收策略

    Redis中提供了多种内存回收策略,当内存容量不足时,为了保证程序的运行,这时就不得不淘汰内存中的一些对象,释放这些对象占用的空间,那么选择淘汰哪些对象呢?
    noeviction(默认的策略),当内存使用达到阈值的时候,所有引起申请内存的命令会报错。
    allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
    适合的场景: 如果我们的应用对缓存的访问都是相对热点数据,那么可以选择这个策略
    allkeys-random:随机移除某个key。
    适合的场景:如果我们的应用对于缓存key的访问概率相等,则可以使用这个策略。
    volatile-random:从已设置过期时间的数据集中任意选择数据淘汰。
    volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。
    volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰
    适合场景:这种策略使得我们可以向Redis提示哪些key更适合被淘汰,我们可以自己控制

    Redis是单进程单线程,性能为什么这么快?

    官方的解释是,CPU并不是Redis的瓶颈所在,Redis的瓶颈主要在机器的内存和网络的带宽。
    那么Redis本身是如何处理并发请求的?
    Redis是单线程,所有的操作都是按照顺序线性执行的,但是由于读写操作等待用户输入或输出都是阻塞的,所以I/O操作在一般情况下往往不能直接返回,这会导致某一文件的I/O阻塞导致整个进程无法对其它客户提供服务,而I/O多路复用就是为了解决这个问题而出现的。

    IO多路复用

    即经典的Reactor设计模式,也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型。
    就像在Java中使用多线程做异步处理的概念,通过多线程去执行一个流程,主线程可以不用等待。而阻塞和非阻塞我们可以理解为假如在同步流程或者异步流程中做IO操作,如果缓冲区数据还没准备好,IO的这个过程会阻塞。
    IO多路复用主要步骤:
    1.当进程调用select,进程就会被阻塞
    2.此时内核会监视所有select负责的socket,当socket的数据准备好后,select就会通知进程。
    3.进程再调用read操作,数据就会从内核拷贝到进程。
    IO操作会阻塞线程,直到IO处理完成,IO多路复用技术通过把多个IO的阻塞复用到同一个select的阻塞上,select会监听所有的IO处理,当IO处理完成后,select会通知给线程,这样使用select来同时处理多个客户端请求,就减少了线程的开销。

    相关文章

      网友评论

          本文标题:redis原理分析

          本文链接:https://www.haomeiwen.com/subject/wpmnkctx.html