更多优质内容
请关注公众号

深入Redis之 redis中的内存管理(十三)-张柏沛IT博客

正文内容

深入Redis之 redis中的内存管理(十三)

栏目:其他内容 系列:深入Redis系列 发布时间:2020-10-10 22:50 浏览量:2126

一、redis的内存消耗在哪里
如何查看redis的内存消耗情况
info memory 
命令


每一项的具体意思可以看博客的图片,标红的要重点关注。

在我们的印象中,我们都会认为redis占用的内存就是我们写入kv占用的内存,但其实还包括bgsave fork出子进程要用的内存,各种缓冲区占用的内存等等。

redis使用的内存分为两大块:
used_memory: 又分为自身内存、对象内存、缓冲内存、lua内存
自身内存:很低,几百k,可以忽略
对象内存:就是我们存储的string、hash、set、list等内容
缓冲内存:包括复制缓冲区、aof缓冲区、客户端缓冲区等等
lua内存:略

used_memory_rss - used_memory: 即内存碎片,即操作系统分配给redis的内存和redis实际使用的内存的差值。比如我存一个字符串 hello, 一共5个字节,但实际操作系统分配给这个key的内存大小要大于5个字节。


内存碎片率 + used_memory(redis实际使用的内存量) = used_memory_rss(总的内存消耗量)


二、缓冲区内存
输入缓冲区:
客户端发送命令给redis,redis在执行这些命令前,这些命令会被临时存放到一个输入缓冲区,也就是之前说的命令队列(由于redis是单线程的所以命令要放在队列中排队顺序执行)。
命令存在于输入缓冲区的时间很短,因为redis处理命令很快,所以输入缓冲区占用的内存基本看到都是0

但是如果redis很忙,或者key是一个bigkey,那么输入缓冲区就会较长时间的被占用。

输入缓冲区不能占用超过1G(无法配置修改),否则就会断开和客户端的连接。

1.客户端缓冲区
分为
普通客户端(redis-cli,php或者python执行命令时的客户端等)
slave客户端(从节点同步主节点时需要一个slave客户端接收主节点同步过来的命令)
pubsub客户端(发布订阅时会生成客户端)

这三个是输出缓冲区(redis服务返回命令所经过的缓冲区)

如果客户端请求的是bigkey,那么普通客户端缓冲区就很容易占用大量内存。

2.复制缓冲区和AOF缓冲区
复制缓冲区是用于主从节点的部分复制而生的,master节点会将写命令写入到复制缓冲区,然后再从复制缓冲区发送给从节点以进行数据同步。为了防止网络抖动造成不必要的全量复制可以将这个缓冲区调大点。这个缓冲区大小默认是1Mb,可以调大到100M的样子。

AOF缓冲区分为aof缓冲区(用于aof日志的生成)和aof重写缓冲区(用于aof重写),这两个缓冲区是没有容量限制的。

3.内存碎片
内存碎片的处理最好解决方式是安全重启。

4.子进程内存消耗
像bgsave和bgrewriteaof的时候,就会fork生成一个子进程从而消耗内存。



三、内存管理


1.设置内存上限
假如一台linux专门用来做redis缓存,共有24G内存,则可以在这台机器上部署多个redis实例,分配2/3的内存给redis,预留1/3的内存给Linux系统和一部分空闲内存以备fork消耗。

可以通过设置在配置文件的 maxmemory 设置redis节点使用的最大内存(这里的最大内存不包含客户端缓冲区)

动态配置:
config set maxmemory 6GB
config rewrite        # 配置重启,写入配置文件中


2.内存回收策略

分两块说:

A.对于过期key的回收,redis会使用定期删除 + 惰性删除两种策略结合。

定期好理解,默认每隔一段时间(100秒)就随机抽一些设置了过期时间的key,去检查是否过期,过期了就删了。

为啥不扫描全部设置了过期时间的key呢?
假如Redis里面所有的key都有过期时间,都扫描一遍?那太恐怖了,而且我们线上基本上也都是会设置一定的过期时间的。全扫描跟你去查数据库不带where条件不走索引全表扫描一样,100s一次,Redis累都累死了。

如果一直没随机到很多key,里面不就存在大量的无效key了?

此时就用到了惰性删除:
即当用户查询到某个key的时候,redis会查看这个key是否过期。过期则不返回给用户,直接删掉。


B.当内存溢出的时候(redis中的kv超过了maxmemory内存上限)会触发5种相应的策略。

Noeviction:默认策略,不会删除任何数据,但会拒绝所有写入操作并返回错误信息“OOM command not allowed when used memory”。但是会相应读操作。

Volatile-lru:根据LRU算法(最近最少使用算法)删除设置了超时时间的key(设置了expire的key)直到腾出足够空间为止。如果没有可删除的key则退回到Noeviction默认策略

Volatile-ttl:根据key的ttl属性,删除最近即将过期的key,如果没有就回退到默认策略。

Allkeys-lru:根据LRU算法删除key,不管数据有没有设置超时,直到腾出足够空间为止。

Allkeys-random:随机删除所有key直到腾出足够空间为止

volatile-random:随机删除过期key直到腾出足够空间为止

可以在配置文件的设置 maxmemory-policy 来控制。


当内存溢出的时候,可以分析一下是什么原因造成的,这是要想到redis用到内存的地方(对象内存、缓冲区内存、内存碎片等)
=======================================
value设计

1.拒绝bigkey
string类型超过10k就可以看成bigkey
hash,list,set和zset元素个数超过5000个可以看成bigkey

这个要看不同公司有不同标准

bigkey的危害:
1.网络阻塞
举个例子,有一个key大小是5M,单机的带宽是千兆也就是128Mb,那么当对这1个key的QPS达到25的时候带宽就会占满从而影响了其他很多的业务。

2.慢查询
例如做一些hgetall,lrange,zrange等操作
因为redis是一个单线程,如果单个key的大小非常大,就会阻塞后面的命令的执行。

3.节点数据不均衡
这个问题是出现在分布式redis中的,如果key过大,那么在每个节点的key个数差不多的情况下出现数据倾斜的问题。

4.反序列化消耗
如果string类型存的是一个序列话的bigkey,那么获取的时候,你的应用程序就要对他进行反序列化,这是一个比较吃CPU的操作


如何发现bigkey
可以使用 redis-cli --bigkeys(他也是进行数据的扫描,建议在slave节点中进行)
或者通过网络流量监控,客户端监控(例如python去查redis发现超时)。




更多内容请关注微信公众号
zbpblog微信公众号

如果您需要转载,可以点击下方按钮可以进行复制粘贴;本站博客文章为原创,请转载时注明以下信息

张柏沛IT技术博客 > 深入Redis之 redis中的内存管理(十三)

热门推荐
推荐新闻