前言
Redis的数据都存储在内存中,所以本篇文章将学习 Redis 的内存机制,以帮忙定位 Redis 的内存相干问题。
注释
一. 查看 Redis 中的内存
Redis提供了 info memory
指令来查看 Redis 的内存状况,然而在查看 Redis 中的内存之前,先通过一段代码来造成 Redis 的OOM,代码片段如下。
long count = 0L;
while (true) {byte[] bytes = new byte[1024];
RBucket<byte[]> curBucket = redissonClient.getBucket(String.valueOf(count++));
curBucket.set(bytes);
}
因为当时曾经将 Redis 的最大内存设置为了 10MB
,所以上述代码片段运行一小会儿就会造成Redis 产生 OOM,Redisson
抛出的 OOM 异样如下。
此时应用 info memory
指令查看 Redis 内存状况,如下所示。
下表是对上图的字段的阐明。
字段 | 阐明 |
---|---|
used_memory | Redis应用的内存总量。蕴含 对象内存 , 缓冲内存 , 虚拟内存 和本身内存 ,不蕴含 内存碎片。 |
used_memory_rss | Redis过程占用的内存总量。蕴含 对象内存 , 缓冲内存 , 本身内存 和内存碎片 ,不蕴含 虚拟内存。 |
used_memory_peak | used_memory达到过的峰值。 |
used_memory_lua | Lua引擎占用的内存。 |
maxmemory | 用户配置的 Redis 的最大内存。 |
maxmemory_policy | Redis达到最大内存时的淘汰策略。 |
mem_fragmentation_ratio | used_memory_rss / used_memory。代表 Redis 内存的碎片化率,值越大,示意 Redis 的内存碎片越多。 |
mem_allocator | Redis应用的内存分配器。默认为jemalloc。 |
注:以 _human 结尾的字段是增加了单位的可读形式显示。
二. used_memory 详解
info memory
指令的后果中的 used_memory 示意 Redis 理论应用的内存,通常由 对象内存 , 缓冲内存 和本身内存 组成,能够由下图进行概括。
1. 对象内存
已知 Redis 中存储数据时应用的键和值均为对象,Redis中的对象共五种(详见 Redis- 对象类型),当存储数据时,会依据存储场景将数据存储为不同的对象,此时 Redis 的内存分配器会为这些对象分配内存空间。
2. 缓冲内存
Redis中的缓冲区次要有:客户端缓冲区 ,AOF 缓冲区 和复制积压缓冲区,这些缓冲区的内存耗费能够概括如下。
- 客户端缓冲区:又分为 客户端输出缓冲区 和客户端输入缓冲区。客户端输出缓冲区用于暂存客户端输出的指令,当一次性写入大量指令,或者服务端负载过高时,客户端输出缓冲区会继续增高,客户端输出缓冲区的最大容量为
1GB
。客户端输入缓冲区用于保留指令执行后的返回后果; - AOF缓冲区:Redis进行 AOF 长久化时会先将 Redis 指令写入缓冲区中,而后再依据 AOF 的写磁盘策略在适合的工夫点将缓存内容刷到磁盘文件中;
- 复制积压缓冲区:用于主从同步。对于主从同步,会在后续的文章中介绍。
3. 本身内存
次要指进行 RDB 或者 AOF 长久化时,Redis创立子过程的内存耗费。
三. mem_fragmentation_ratio 详解
mem_fragmentation_ratio示意 Redis 内存的碎片化率,所谓内存碎片,就是 Redis 占用着然而又没有用于存储数据的内存。mem_fragmentation_ratio = used_memory_rss / used_memory
,对于 mem_fragmentation_ratio 的值,存在如下的含意。
- mem_fragmentation_ratio大于 1 时,示意 Redis 存在内存碎片,mem_fragmentation_ratio越大,内存碎片越多;
- mem_fragmentation_ratio小于 1 时,示意 Redis 的局部内存替换到了磁盘(应用了虚拟内存),值越小,Redis替换到磁盘的内存就越多,此时 Redis 的速度就越慢。
失常状况下,mem_fragmentation_ratio的值须要大于 1 并管制在 1.2 以内。
Redis产生内存碎片是因为 Redis 的默认内存分配器 jemalloc 的内存分配机制导致的,jemalloc分配内存时,会依照如下规定。
- 调配的内存空间满足 2 的幂次方;
- 调配的内存空间满足大于等于须要调配的内存空间。
所以如果须要调配 100byte
,那么jemalloc 会调配 128byte
的内存空间,此时 Redis 过程占用的这 128byte
的内存理论只有 100byte
被应用,未应用的 28byte
就是内存碎片。同时如果一个 128byte
的内存空间中只有局部数据被删除,那么这 128byte
的内存是不会被回收的,此时也产生了内存碎片。
四. Redis 的淘汰策略
如果 Redis 应用的内存将超过 maxmemory 时,Redis会依据 maxmemory_policy 即淘汰策略来决定将哪些数据淘汰掉。Redis反对的淘汰策略如下表所示。
淘汰策略 | 阐明 |
---|---|
noeviction | 默认策略。应用内存达到 maxmemory 时,如果增加新数据,不会删除任何旧数据,而是间接报错。 |
volatile-random | 对设置了过期工夫(expire )的数据失效。随机删除一部分数据,直到有足够的内存空间调配给新数据,如果将设置了过期工夫的数据全副删除完了都没有足够空间调配给新数据,此时报错。 |
volatile-ttl | 对设置了过期工夫(expire )的数据失效。优先删除过期工夫最小的数据,如果将设置了过期工夫的数据全副删除完了都没有足够空间调配给新数据,此时报错。 |
volatile-lru | 对设置了过期工夫(expire )的数据失效。应用 LRU 算法删除数据,如果将设置了过期工夫的数据全副删除完了都没有足够空间调配给新数据,此时报错。 |
volatile-lfu | 对设置了过期工夫(expire )的数据失效。应用 LFU 算法删除数据,如果将设置了过期工夫的数据全副删除完了都没有足够空间调配给新数据,此时报错。 |
allkeys-random | 对所有数据失效。随机删除一部分数据,直到有足够的内存空间调配给新数据,如果没有数据可供删除且还未有足够空间调配给新数据,此时报错。 |
allkeys-lru | 对所有数据失效。应用 LRU 算法删除数据,如果没有数据可供删除且还未有足够空间调配给新数据,此时报错。 |
allkeys-lfu | 对所有数据失效。应用 LFU 算法删除数据,如果没有数据可供删除且还未有足够空间调配给新数据,此时报错。 |
下面提到的 LRU(Least Recently Used) 算法其实就是将最近被拜访间隔以后最久的数据删除,Redis会记录每个数据最初一次被拜访的工夫,在须要删除数据开释空间时,会依据每个数据最初一次被拜访的工夫抉择出最“旧”的数据进行删除,所以 LRU(Least Recently Used) 算法有一个毛病,就是一个很少被拜访然而最近被拜访过的数据不会优先被删除,所以 Redis4.0 引入了LFU(Least Frequently Used) 算法,即须要删除数据开释空间时,依据数据的拜访频次筛选出起码被拜访的数据进行删除,如果两条数据的拜访频次雷同,此时再依据数据最初一次被拜访工夫来决定删除哪条数据。
在第一节的例子中,能够看到 Redis 的淘汰策略是 noeviction,所以在应用内存达到maxmemory 后,Redis报了 OOM 的谬误。
五. Redis 的过期策略
上一节提到在 Redis 中能够应用 expire
指令为存储的数据设置过期工夫,那么某条数据过期后,Redis会依据过期策略来删除这些过期数据,Redis中的过期策略如下所示。
过期策略 | 阐明 |
---|---|
定时删除 | 每个设置了过期工夫的数据都有一个定时器,一旦数据过期,该数据会立刻被删除。长处:过期数据能够及时被删除;毛病:过期数据多时定时器会占用较多 CPU 资源。 |
惰性删除 | 应用数据的时候才去判断该数据是否过期,如果过期就删除该数据。长处:过期数据被拜访时才会被删除,删除过期数据不会占用过多 CPU 资源;毛病:有些曾经过期然而没有被拜访的数据会长期得不到删除。 |
定期删除 | 每隔一段时间扫描过期数据并删除。长处:通过管制扫描的间隔时间和执行工夫,能够缩小删除过期数据的操作对 CPU 资源的占用;毛病:扫描的间隔时间和执行工夫难以确定一个正当值。 |
在 Redis 中是采纳 惰性删除 加定期删除 来解决过期数据的,同时 Redis 在进行定期删除时,是将设置了过期工夫的 key 寄存在字典中,默认状况下是每秒对字典进行 10 次扫描,每次扫描应用贪婪策略遍历局部 key 来判断对应的数据是否过期,贪婪策略如下。
- 步骤 1:从字典中随机抉择 20 个key;
- 步骤 2:删除这 20 个 key 中过期 key 对应的数据;
- 步骤 3:如果 20 个 key 中过期 key 占比超过 25%,则反复步骤 1 -3。
总结
Redis将数据寄存在内存中,所以理解 Redis 的内存机制对于应用 Redis 很有帮忙。info memory
指令可能列出 Redis 以后的内存情况和内存策略,能够通过 maxmemory 配置项来配置 Redis 的最大内存,也能够通过 maxmemory-policy 配置项来配置当内存达到最大值时的数据淘汰策略。Redis提供了共 8 种淘汰策略,能够仅针对设置了过期工夫的数据失效,也能够针对所有数据失效,如果不配置淘汰策略,那么 Redis 采纳的默认淘汰策略为不淘汰,此时内存达到最大值时 Redis 会报 OOM 谬误。