一 内存消耗
1.1 内存使用统计
used_memory: Redis分配的内存总量,即存储的所有数据占用的内存
used_memory_human: 以可读格式返回使用的内存量
used_memory_rss:从系统角度,显示Redis进程占用的物理内存总量
used_memory_rss_human:以可读格式返回Redis进程占用的物理内存总量
used_memory_peak:内存使用的最大值,表示used_memory峰值
used_memory_peak_human:以可读格式返回内存使用的最大值
total_system_memory:系统总内存
total_system_memory_human:以可读格式返回系统总内存
used_memory_lua:Lua进程使用内存
used_memory_lua_human:以可读格式返回Lua进程使用内存
mem_fragmentation_ratio:内存碎片率,等价于(used_memory_rss /used_memory)
mem_allocator:redis使用的内存分配器
重点关注used_memory以及used_memory_rss和碎片率mem_fragmentation_ratio
mem_fragmentation_ratio > 1: used_memory_rss -used_memory 多出部分没有存储数据,而是被内存碎片消耗了,如果差的很大,说明碎片很严重
mem_fragmentation_ratio < 1: 一般出现在操作系统把Redis内存交换到硬盘导致。
1.2 内存消耗划分
Redis进程内的内存主要包括:
自身内存 + 对象内存 + 缓冲内存+ 内存碎片,其中Redis自身内存消耗的很少,通常used_memory_rss在3MB左右,used_memory在800k左右。
1.2.1 对象内存
对象内存是Redis中最占内存的一块,存储着用户所有数据。Redis所有数据都采用key-value,每次你创建key-value,都是创建2个对象,即key对象和value对象,即 key的size + value的size. Key对象都是字符串,我们应当避免使用过长的key. Value对象根据使用情况不同占用内存也不尽相同,注意监控。
1.2.2 缓冲内存
缓冲内存涉及到客户端缓冲区,复制积压缓冲区和AOF缓冲区
客户端缓冲:指的是所有连接到Redis的服务器tcp连接输入输出缓冲,输入缓冲无法控制,最大空间1G;输出缓冲可通过client-output-buffer-limit控制。
# 普通客户端:client-output-buffer-limit normal 0 0 0
普通客户端默认并没有对输出缓冲区做限制。但是如果当有大量的慢连接客户端接入时,这部分消耗就不能忽略了,因为消费的很慢,在成输出缓冲区数据积压。所以可以设置maxclients做限制。
# 从客户端:client-output-buffer-limit slave 256mb 64mb 60
主节点会每一个从节点单独建立一条连接用于命令复制。当主节点网络延迟较高或主节点挂载大量的从节点时,这部分内存消耗将占用很大一部分,建议主节点挂载从节点最好不要超过2个。
# 订阅客户端:client-output-buffer-limit pubsub 32mb 8mb 60 当生产消息的速度快于消费的速度时,输出缓冲区容易积压消息
复制积压缓冲区:一个可重用的固定大小缓冲区用于实现部分复制功能,根据repl-backlog-size参数控制,默认1MB. 对于复制积压缓区,主节点有一个,所有从节点啊共享这个缓冲区,因此可以设置较大的值,比如100MB,这部分投入是有价值的,可以有效避免全量复制。
AOF缓冲区:用于AOF重写期间保存最近写入的命令,等待被刷到磁盘
1.2.3 内存碎片
Redis默认的内存分配器是jemalloc,内存分配器的作用就是为了更好的管理和重复利用内存。但是当存储的数据长短差异较大的时候,以下场景容易出现高内存碎片问题:
# 频繁更新,对已经存在的key进行append setrange操作
# 大量过期键删除,键对象过期删除后,释放的 空间无法得到拆分利用
解决办法:
# 尽量数据对齐,视业务情况而定
# 安全重启:重启可以做到内存碎片重新整理
1.3 子进程内存消耗
子进程内存主要是指AOF/RDB重写时Redis创建的子进程内存消耗。Redis执行fork操作产生子进程内存占用对外表现为与父进程相同,理论上需要一倍的相同物理内存来完成重写操作。但是Linux的copy-on-write机制使得父子进程共享相同的物理内存页,当父进程处理写请求时会对需要修改的页复制出一份副本完成写操作,而子进程依旧读取fork时整个父进程内存快照。
# Redis子进程并不需要消耗1倍 的父进程内存,但是依然要预留一些内存防止内存溢出
# 需要设置sysctl vm.overcommit_memory=1允许内核可以分配所有的物理内存,防止Redis进程执行fork时因系统剩余内存不足而失败。
# 排查当前系统是否支持并开启THP,如果开启,建议关闭。
二 内存管理
2.1 设置内存上限
Redis通过maxmemory参数限制最大可用内存。限制内存目的主要有:
# 用于缓存场景,当超出内存上限maxmemory时候使用LRU等删除策略释放空间
# 防止所用内存超过服务器物理内存
需要注意的是,maxmeory限制的是Redis实际使用的内存量,也就是used_memory统计项对应的内存。通过设置上限,可以方便实现一台服务器部署多个Redis进程的内存控制。比如一台32G的内存的服务器,预留4GB内存给系统,预留4GB给Redis fork进程,留给Redis24GB内存,这样就可以部署3个maxmemory=8GB的redis进程。
2.2 动态调整内存上限
可以通过命令config set maxmemory8GB
2.3 内存回收策略
内存回收机制主要体现在以下两个方面:
2.3.1删除过期的key
Redis采用惰性删除和定时任务删除机制实现过期key的内存回收
# 惰性删除
用于当客户端读取带有超时属性的key的时候,如果已经超过设置的过期时间,会执行删除操作并返回空。但是有一个问题,当过期键一直没有访问将无法得到及时删除,从而导致内存 不能及时释放
# 定时任务删除
Redis内部维护一个定时任务,默认每秒运行10次,通过配置hz属性控制。
2.3.2 内存使用达到maxmeory上限时触发内存溢出的控制策略
当Redis所用内存达到maxmeory上限时,会触发相应的溢出控制策略。具体策略受maxmeory-policy参数控制,Redis支持6种策略:
# volatile-lru -> 根据LRU算法删除设置了超时属性的键,直到腾出足够空间为止
# allkeys-lru -> 根据LRU算法删除键,不管有没有设置超时属性,直到腾出足够空间为止
# volatile-random -> 随即删除过期键,直到腾出足够空间为止
# allkeys-random -> 随即删除所有键,直到腾出足够空间为止
# volatile-ttl -> 根据ttl属性,删除最近将要过期的数据,如果没有回退到noeviction策略
# noeviction -> 不会删除任何数据,拒绝所有写入操作,并返回错误信息,此时只是响应读
三 内存优化
3.1 redisObject对象
3.2 缩减键值对象
降低Redis内存使用最直接的方式就是缩减key和value的长度
# key的设计:越短越好,如user:{userid}:friends:notify:{fid},可以简化为u:{uid}:fs:nt:{fid}
# value: 值对象缩减比较复杂,常见的需求是把业务对象序列化放入Redis。
3.3 共享对象池
3.4 字符串优化
3.5 编码优化
3.6 控制键的数量
比如通过hash数据结构就可以减少key的数量,从而减少了RTT的时间,还可以减少key,这样也节约了内存