1. 内存消耗
1.1 内存使用统计
重点关注的指标:
(1)used_memory_rss:从操作系统角度显示Redis进程占用的物理内存的总量。
(2)used_memory:Redis分配器分配的内存总量,所有数据内存的占用量。
(3)mem_fragmentation_ratio= used_memory_rss/used_memory。
如果ratio>1: 表示部分内存没有用于数据存储,而是被内存碎片所消耗。
如果ratio<1:表示操作系统把redis内存sweap到硬盘导致。
1.2 内存消耗划分
Redis 内存消耗主要包括: 自身内存+对象内存+缓冲内存+内存碎片。
(1)对象内存是Redis内存占用最大的一块。
(2)缓冲内存包括客户端缓冲,复制积压缓冲区,AOF缓冲区。
(3)Redis默认的内存分配器采用jemalloc。
2. 内存管理
2.1 设置内存上限
(1)使用maxmemory参数限制最大可用内存。由于内存碎片的存在,实际消耗的内存可能比maxmemory大。
2.3 内存回收策略
包括两个策略:
(1)删除过期的对象
a. 惰性删除
当客户端读取带有超时属性的键时,如果已经超过键的过期时间,会自动执行删除操作并返回空。但是这种方式存在内存泄漏问题,如果一个键过期了一直没有被访问将无法得到及时删除,从而导致内存不能及时释放。
b.定时任务删除
Redis内部维护一个定时任务,默认每秒运行10次,流程如下:
- 定时任务随机检查20个键,发现过期时删除键。
- 如果检查的键种有25%的键过期了,循环执行回收逻辑直到不足25%。
(2)内存溢出达到maxmemory上限,触发内存溢出控制策略。6种策略如下:
- noeviction: 默认策略。不删除任何数据,拒绝所有写入操作,返回给客户段:OOM command not allow when used memory。
- volatile-lru:根据LRU算法删除了有超时属性的键,知道腾出足够空间为止。
- allkeys-lru:根据LRU算法删除键,不管数据有没有设置超时属性。
- allkey-random:随机删除所有键,知道腾出足够的空间。
- volatile-random:随机删除过期键
- voaltile-ttl:根据ttl属性,删除最近将要过期的数据。
3. 内存优化
3.1 redisObject 对象
(1)Redis 存储的所有值对象在内部定义为redisObject 结构体,redisObject 包括以下字段:
- type:当前数据使用的数据类型。e.g. string, hash, set, zset, list.
- encoding 字段:Redis内部编码类型。
- lru:字段:记录对象最后一次被访问的时间。
- refcount 字段:记录当前对象被引用次数。
- ptr 字段:指向数据的指针。
3.2 缩减键值对象
3.3. 共享对象池
3.4 字符串优化
3.4.1 字符串结构
(1)内部使用简单动态字符串(simple dynamic string, SDS)。int len:已用字节长度。int free : 未用字节长度。 char buf[]:字节数组。
(2)注意:尽量减少字符串频繁修改操作如append,setrange,改为直接使用set修改字符串,降低预分配带来的内存浪费。
3.5 编码优化
(1)Redis 为什么对一种数据结构实现多种编码方式?
通过不同编码方式实现空间和实现效率上的平衡。
(2)转化规则只能从小内存编码向大内存编码转换。
3.5.3 ziplist 编码
(1)ziplist编码的主要目的是节省内存。
(2)内部结构:zlbytes,zltail,zllen,entry-1,entry-2…zlend。
(3)每个entry: pre_entry_bytes_length,encoding,contents。
(4)zlbytes:记录整个压缩列表所占字节长度。
(5)zltail:记录距离尾节点的偏移量。
(6)zllen:记录压缩节点数量。
(7)entry:记录具体的节点。
(8)pre_entry_bytes_len:记录前一个节点所占空间。
(9)encoding
(10)content:保存节点的值
(11)zlend:记录列表结尾,占用一个字节。