redis-内存管理及优化

一、redis的内存消耗到底在哪些地方?

  • 自身内存(redis空进程消耗的内存,只有几M,可以忽略)
  • 对象内存(主要的内存消耗,存储数据对象,包括key、value占的内存)
  • 缓冲区内存(客户端输入缓冲区、服务端输出缓存区、AOF缓冲区)
  • 内存碎片(对象内存不规则导致的内存碎片)
  • 子进程内存消耗(RDB/AOF  fork子进程)

查看内存占用命令:info memory  主要关注used_memory(数据占用内存大小) 和 used_memory_rss(操作系统算出来redis进程占用内存大小),内存碎片率mem_fragmentation_ratio =used_memory_rss / used_memory,如果>1,说明有内存碎片,如果<1,说明有内存被操作系统弄到硬盘swap区去了。

二、redis如何管理内存?

  • 可以设置内存上限值(不设置就是操作系统最大内存)
  • 内存回收策略

可以为每个redis实例设置一个最大内存限制,命令:config set maxmemory  6G

redis有自己的内存回收策略:

  • 删除达到过期时间的键值
  • 达到内存上限触发内存回收

a、对于设置了过期时间的键值,redis采用了惰性删除+定时删除双策略。惰性删除就是当获取带有过期时间的键值时,先判断是否过期,过期则回收内存,返回空。如果某个过期的键值一直不被访问,那么就会造成内存泄漏,所以又加上定时删除机制。定时删除就是:redis内部运行一个定时任务,默认100ms跑一次,采用自适应删除逻辑对过期键删除,具体参考:Redis-内存淘汰策略 - hugeQAQ - 博客园 (cnblogs.com)

b、当内存达到限制时,触发内存回收,具体回收策略由maxmemory-policy决定。参考Redis-内存淘汰策略 - hugeQAQ - 博客园 (cnblogs.com)

三、如何优化redis的内存?(减小内存占用)

鱼和熊掌不可兼得,要想减小内存使用,就得牺牲效率,用时间换空间。

  • 缩减键和值的长度(最直接的方式)
  • 共享对象池
  • 字符串优化
  • 编码优化
  • 控制键的数量

1、缩减键值长度:业务上key-value字段能精简就精简

2、共享对象池:redis内部维护了一个[0-9999] 的整数对象池,当string类型的值或者list、hash、set、zset的内部的值是[0-9999] 的整数时,都可以引用这个对象池里面的redisObject,增加refCount即可。这样就可以减少很多内存分配了。但是需要注意的是:如果设置了maxmemory并且内存淘汰策略用的是LRU相关的,共享内存池就失效了,原因就是LRU策略需要记录对象的最近使用时间,如果共享对象,就没办法确定对象的使用时间了。还有一点如果用ziplist编码,也不会使用共享内存,因为结构的原因,判断成本太高了。

为什么只有整数类型的共享对象池呢?因为整数的判等操作是O(1),而字符串是O(n),其它结构需要O(n²),得不偿失。

3、字符串优化:redis的字符串是自己实现的结构SDS,结构上多了两个字段 len-已用字节长度 free-未用字节长度。特点是可以O(1)获取字符串长度、已用长度、未用长度,内部空间预分配、惰性删除(字符串删短了也不释放空间,留着预分配)。字符串内存的优化思路就是减少预分配占用的内存,比如一个字符串第一次set 大小为60byte,此时len =60 free=0,再执行append 60byte操作,此时会触发字符串的预分配机制(如果free不够,数据小于1M,预分配数据大小相同的内存,数据大于1M,预分配1M),变成了 len=120 free=120,如果没有后续操m作,这120就浪费了,如果是直接set 120 的话,就是len=120 free=0 所以要减少字符串的修改操作,直接用删了重新set。

json类的字符串,可以改为用hash存储,但是一定要注意长度,要用ziplist编码,不然内存会变得更大。

4、编码优化:redis的每种数据类型都有几种编码方式,具体参考:Redis基本数据结构及底层实现 - hugeQAQ - 博客园 (cnblogs.com),根据实际情况,尽量使用节省内存的编码方式,例如ziplist、intset

ziplist为什么节省内存?ziplist是一块连续的内存空间,类似于数组一样,不会有链表一样的前驱后继节点指针,它是根据起始位置和偏移来确定元素,用时间换空间,适合有限个数的小对象,元素太多了遍历成本太大,新增和删除元素也很麻烦(类比数组删除和新增)。

5、减少键的数量:键太多了,内存自然也涨了,可以利用hash,把键做一遍hash映射,比如有100万个键,hash到1000个键上,这1000个键又用hash结构存值,filed就是原来的键,value就是值,每个hash键存1000个值就完事了,但是要确保hash用ziplist编码,不然反而更大了。

 

posted @ 2022-03-12 16:50  hugeQAQ  阅读(442)  评论(0)    收藏  举报