Redis-内存机制
前言
Redis的数据都存储在内存中,所以本篇文章将学习Redis的内存机制,以帮助定位Redis的内存相关问题。
正文
一. 查看Redis中的内存
Redis提供了info memory
指令来查看Redis的内存情况,但是在查看Redis中的内存之前,先通过一段代码来造成Redis的OOM,代码片段如下。
long count = 0L;
while (true) {
byte[] bytes = new byte[1024];
RBucket 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。 |
二. used_memory详解
info memory
指令的结果中的used_memory表示Redis实际使用的内存,通常由对象内存,缓冲内存和自身内存组成,可以由下图进行概括。文章图片
1. 对象内存 已知Redis中存储数据时使用的键和值均为对象,Redis中的对象共五种(详见Redis-对象类型),当存储数据时,会根据存储场景将数据存储为不同的对象,此时Redis的内存分配器会为这些对象分配内存空间。
2. 缓冲内存 Redis中的缓冲区主要有:客户端缓冲区,AOF缓冲区和复制积压缓冲区,这些缓冲区的内存消耗可以概括如下。
- 客户端缓冲区:又分为客户端输入缓冲区和客户端输出缓冲区。客户端输入缓冲区用于暂存客户端输入的指令,当一次性写入大量指令,或者服务端负载过高时,客户端输入缓冲区会持续增高,客户端输入缓冲区的最大容量为
1GB
。客户端输出缓冲区用于保存指令执行后的返回结果; - AOF缓冲区:Redis进行AOF持久化时会先将Redis指令写入缓冲区中,然后再根据AOF的写磁盘策略在合适的时间点将缓存内容刷到磁盘文件中;
- 复制积压缓冲区:用于主从同步。关于主从同步,会在后续的文章中介绍。
三. 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的速度就越慢。
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算法删除数据,如果没有数据可供删除且还未有足够空间分配给新数据,此时报错。 |
在第一节的例子中,可以看到Redis的淘汰策略是noeviction,所以在使用内存达到maxmemory后,Redis报了OOM的错误。
五. Redis的过期策略
上一节提到在Redis中可以使用
expire
指令为存储的数据设置过期时间,那么某条数据过期后,Redis会根据过期策略来删除这些过期数据,Redis中的过期策略如下所示。过期策略 | 说明 |
---|---|
定时删除 | 每个设置了过期时间的数据都有一个定时器,一旦数据过期,该数据会立即被删除。优点:过期数据可以及时被删除;缺点:过期数据多时定时器会占用较多CPU资源。 |
惰性删除 | 使用数据的时候才去判断该数据是否过期,如果过期就删除该数据。优点:过期数据被访问时才会被删除,删除过期数据不会占用过多CPU资源;缺点:有些已经过期但是没有被访问的数据会长期得不到删除。 |
定期删除 | 每隔一段时间扫描过期数据并删除。优点:通过控制扫描的间隔时间和执行时间,可以减少删除过期数据的操作对CPU资源的占用;缺点:扫描的间隔时间和执行时间难以确定一个合理值。 |
- 步骤1:从字典中随机选择20个key;
- 步骤2:删除这20个key中过期key对应的数据;
- 步骤3:如果20个key中过期key占比超过25%,则重复步骤1-3。
info memory
指令能够列出Redis当前的内存状况和内存策略,可以通过maxmemory配置项来配置Redis的最大内存,也可以通过maxmemory-policy配置项来配置当内存到达最大值时的数据淘汰策略。Redis提供了共8种淘汰策略,可以仅针对设置了过期时间的数据生效,也可以针对所有数据生效,如果不配置淘汰策略,那么Redis采用的默认淘汰策略为不淘汰,此时内存达到最大值时Redis会报OOM错误。推荐阅读
- 手机APP性能测试_内存CPU耗电量流量等测试
- WinXP系统支持多大内存?WinXP能支持多大内存?
- 简单聊聊AndroidIPC机制
- excel 内存或磁盘空间不足,本文教您如何3步内解
- Android 内存泄露
- 小记--------spark的Master的Application注册机制源码分析及Master的注册机制原理分析
- 一文详解Java中的类加载机制
- 虚拟内存不足,图文详细说明系统总是提示虚拟内存不足怎样办
- 内存不能为read,图文详细说明内存不能为read如何处理
- python|YOLOv5添加注意力机制