Redis淘汰机制

Redis内存淘汰web

指的是用户存储的一些键被能够被Redis主动地从实例中删除,从而产生读miss的状况redis

那么Redis为何要有这种功能?这就是咱们须要探究的设计初衷。算法

Redis最多见的两种应用场景为缓存和持久存储缓存

首先要明确的一个问题是内存淘汰策略更适合于那种场景?是持久存储仍是缓存?安全

内存的淘汰机制的初衷是为了更好地使用内存,网络

用必定的缓存miss来换取内存的使用效率。数据结构

做为Redis用户,我如何使用Redis提供的这个特性呢?看看下面配置dom

maxmemory

525行svg

咱们能够经过配置redis.conf中的maxmemory这个值来开启内存淘汰功能,性能

至于这个值有什么意义,咱们能够经过了解内存淘汰的过程来理解它的意义:

  1. 客户端发起了须要申请更多内存的命令(如set)。

  2. Redis检查内存使用状况,若是已使用的内存大于maxmemory
    则开始根据用户配置的不一样淘汰策略来淘汰内存(key),从而换取必定的内存。

  3. 若是上面都没问题,则这个命令执行成功。

maxmemory为0的时候表示咱们对Redis的内存使用没有限制。

Redis提供了下面几种淘汰策略供用户选择,其中默认的策略为noeviction策略:

· noeviction:当内存使用达到阈值的时候,全部引发申请内存的命令会报错。

· allkeys-lru:在主键空间中,优先移除最近未使用的key。

· volatile-lru:在设置了过时时间的键空间中,优先移除最近未使用的key。

· allkeys-random:在主键空间中,随机移除某个key。

· volatile-random:在设置了过时时间的键空间中,随机移除某个key。

· volatile-ttl:在设置了过时时间的键空间中,具备更早过时时间的key优先移除。

这里补充一下主键空间和设置了过时时间的键空间,举个例子,

假设咱们有一批键存储在Redis中,则有那么一个哈希表用于存储这批键及其值,

若是这批键中有一部分设置了过时时间,那么这批键还会被存储到另一个哈希表中,

这个哈希表中的值对应的是键被设置的过时时间。

设置了过时时间的键空间为主键空间的子集。

咱们了解了Redis大概提供了这么几种淘汰策略,那么如何选择呢?淘汰策略的选择能够经过下面的配置指定:

maxmemory-policy noeviction

可是这个值填什么呢?为解决这个问题,咱们须要了解咱们的应用请求对于Redis中存储的数据集的访问方式以及咱们的诉求是什么。

同时Redis也支持Runtime修改淘汰策略,这使得咱们不须要重启Redis实例而实时的调整内存淘汰策略。

下面看看几种策略的适用场景:

· allkeys-lru:若是咱们的应用对缓存的访问符合幂律分布(也就是存在相对热点数据),或者咱们不太清楚咱们应用的缓存访问分布情况,咱们能够选择allkeys-lru策略。

· allkeys-random:若是咱们的应用对于缓存key的访问几率相等,则可使用这个策略。

· volatile-ttl:这种策略使得咱们能够向Redis提示哪些key更适合被eviction。

另外,volatile-lru策略和volatile-random策略

适合咱们将一个Redis实例既应用于缓存和又应用于持久化存储的时候

然而咱们也能够经过使用两个Redis实例来达到相同的效果,值得一提的是将key设置过时时间实际上会消耗更多的内存,所以咱们建议使用allkeys-lru策略从而更有效率的使用内存。

Redis的存储机制有两种AOF Snapshot

不管是那种机制,Redis都是将数据存储在内存中。

Snapshot工做原理: 是将数据先存储在内存,而后当数据累计达到某些设定的伐值的时候,就会触发一次DUMP操做,将变化的数据一次性写入数据文件(RDB文件)。

AOF 工做原理: 是将数据也是先存在内存,可是在存储的时候会使用调用fsync来完成对本次写操做的日志记录,这个日志揭露文件实际上是一个基于Redis网络交互协议的文本文件。AOF调用fsync也不是说所有都是无阻塞的,在某些系统上可能出现fsync阻塞进程的状况,对于这种状况能够经过配置修改,但默认状况不要修改。AOF最关键的配置就是关于调用fsync追加日志文件的平率,有两种预设频率,。两个配置各有所长后面分析。因为是采用日志追加的方式来持久话数据,因此引出了第二个日志的概念:rewrite. 后面介绍它的由来。

存储模式性能和安全比较:

1.性能:Snapshot方式的性能是要明显高于AOF方式的,缘由有两点:

采用2进制方式存储数据,数据文件比较小,加载快速.

存储的时候是按照配置中的save策略来存储,每次都是聚合不少数据批量存储,写入的效率很好,而AOF则通常都是工做在实时存储或者准实时模式下。相对来讲存储的频率高,效率却偏低。

2.数据安全:AOF数据安全性高于Snapshot存储,缘由:

Snapshot存储是基于累计批量的思想,
也就是说在容许的状况下,累计的数据越多那么写入效率也就越高,
但数据的累计是靠时间的积累完成的,
那么若是在长时间数据不写入RDB,但Redis又遇到了崩溃,那么没有写入的数据就没法恢复了

可是AOF方式恰恰相反,根据AOF配置的存储频率的策略能够作到最少的数据丢失和较高的数据恢复能力。

说完了性能和安全,这里不得不提的就是在Redis中的Rewrite的功能,

AOF的存储是按照记录日志的方式去工做的,那么成千上万的数据插入必然致使日志文件的扩大,

Redis这个时候会根据配置合理触发Rewrite操做,

所谓Rewrite就是将日志文件中的全部数据都从新写到另一个新的日志文件中,

可是不一样的是,对于老日志文件中对于Key的屡次操做,

只保留最终的值的那次操做记录到日志文件中,从而缩小日志文件的大小。

这里有两个配置须要注意:

auto-aof-rewrite-percentage 100 (当前写入日志文件的大小占到初始日志文件大小的某个百分比时触发Rewrite)

auto-aof-rewrite-min-size 64mb (本次Rewrite最小的写入数据良)

两个条件须要同时知足。

2.Redis内存优化理解

Redis内部有不少的数据类型,这些在官方文档上均可以看到,下面是其内部优化的一些细节点:

  1. String 和 数字,

在Redis中若是存储的是“123”

Redis是可以识别出来这是一个数字而且按照数字来存储,节省存储空间,固然除了这个优化以外

Redis内部会构建一个数字池,默认是10000

那么若是是在这个池子的数字就只须要用一个简单的索引来引用进来就能够,而不须要把重复的数字都分开存储。

这个数值能够调整源代码的宏:REDIS_SHARED_INTEGERS来扩大和缩小池子的大小。

2.复杂类型的存储优化,好比Map,List,Set等,这些集合都有一个特色可大可小

,根据实际场景来定,通常状况下若是这些集合所包含的Entry很少,而且每一个Entry所包含的Value不是很长的状况下,Redis内部使用紧凑格式来存储数据,紧凑格式存储数据在查询场景的算法复杂度是O(N),而相似Map或者Set他们的查询算法复杂度都是O(1)那为何要这么作呢 ?为了可以节省内存空间,在N很小的时候其实和O(1)没什么区别。因此这里不的不介绍紧凑格式的表明ZIPMap,他的数据结构是这样:
这里写图片描述

能够看出,这个结构中初始状况只有2个字节,随着操做的增长它会变长,其中最关键的是一个关于Free这个字段的理解,以Map为例,若是新插入一个Key,那么对应ZipMap就会多出来一长串数据:。从图中能够看到插入key1的时候只有绿色的一串,当key2插入的时候就会又出来一个相似的黄色结构串。free的功能是在插入的时候用来冗余空间的,当key所对应的数值发生变化的时候,若是数据变的比以前短了,那么free的长度就变大,这个时候不须要作ZipMap的resize操做,若是数据长度变长了,而且在free可以足以支持新数据的范围以内,那么free就被利用起来,而且也不须要作Resize。这个时候会有空间的浪费或者说碎片。空间换时间,没什么好说的。固然Redis的代码中还有另一个参数ZIPMAP_VALUE_MAX_FREE,这个参数能够用来设置若是Free的大小超过了这个值,那么ZipMap会发生Resize(收缩),从而节约空间