本文根据 Redis 的 info 命令查看 Redis 的内存使用情况以及 state 状态, 来观察 Redis 的运行情况以及需要作出的相应优化.
- info
- 1.memory
- used_memory:13409011624 #used_memory = 实际缓存占用的内存 + Redis 自身运行所占用的内存(如元数据, lua).
- #这个值是由 Redis 使用内存分配器分配的内存, 不包括内存碎片浪费的内存.
- used_memory_rss:13740019719 #从操作系统上显示已经分配的内存总量.
- used_memory_peak:13409011624 #内存使用的峰值大小
- total_system_memory:33567678464 #系统总内存
- used_memory_lua:37888 #Lua 脚本引擎所使用的内存大小.
- maxmemory:0 #最大可用内存(可配置, 默认为 total_system_memory)
- maxmemory_policy:noeviction #淘汰机制, noneviction 为禁止淘汰数据
- mem_fragmentation_ratio:1.02; #内存碎片率
- mem_allocator:jemalloc-4.0.3; #编译时指定的 Redis 内存分配器, 可以是 libc,jemalloc,tcmalloc.
- 2.stats
- total_commands_processed:3500 #自启动起 Redis 服务处理命令的总数
1.used_memory 过大导致的问题
1.1. 引发内存交换
当 Redis 内存使用率超过可用内存 (maxmemory 可配置) 的 95% 时, 操作系统会进行内存与 swap 空间数据交换. 把内存中旧的或不再使用的内容写入硬盘上即 Swap 分区, 以便腾出新的物理内存给新页或活动页 (page) 使用. 在硬盘上进行读写操作要比在内存上进行读写操作, 时间上慢了近 5 个数量级, 内存是 0.1μs 单位, 而硬盘是 10ms. 如果 Redis 进程上发生内存交换, 那么 Redis 和依赖 Redis 上数据的应用会受到严重的性能影响.
1.2.rdb 持久化风险
在没有开启持久化的情况下, Redis 宕机或者内存使用率超过 95% 会有丢数据的风险. 若使用快照 (rdb) 持久化, Redis 会 fork 一个子进程把当前内存中的数据完全复制一份写入到硬盘上(fork 使用的内存和 Redis 当前使用的内存会一样多). 因此若是当前使用内存超过可用内存的 45% 时触发快照功能, 那么此时进行的内存交换会变的非常危险(可能会丢失数据). 倘若在这个时候实例上有大量频繁的更新操作, 问题会变得更加严重.
2. 避免 used_memory 过大
尽可能的使用 Hash 数据结构.
因为 Redis 在储存小于 100 个字段的 Hash 结构上, 其存储效率是非常高的. 所以在不需要集合 (set) 操作或 list 的 push/pop 操作的时候, 尽可能的使用 Hash 结构. 比如, 在一个 web 应用程序中, 需要存储一个对象表示用户信息, 使用单个 key 表示一个用户, 其每个属性存储在 Hash 的字段里, 这样要比给每个属性单独设置一个 key-value 要高效的多. 通常情况下倘若有数据使用 string 结构, 用多个 key 存储时, 那么应该转换成单 key 多字段的 Hash 结构. 如上述例子中介绍的 Hash 结构应包含, 单个对象的属性或者单个用户各种各样的资料. Hash 结构的操作命令是 HSET(key, fields, value)和 HGET(key, field), 使用它可以存储或从 Hash 中取出指定的字段.
设置 key 的过期时间.
一个减少内存使用率的简单方法就是, 每当存储对象时确保设置 key 的过期时间. 倘若 key 在明确的时间周期内使用或者旧 key 不大可能被使用时, 就可以用 Redis 过期时间命令 (expire,expireat, pexpire, pexpireat) 去设置过期时间, 这样 Redis 会在 key 过期时自动删除 key. 假如你知道每秒钟有多少个新 key-value 被创建, 那可以调整 key 的存活时间, 并指定阀值去限制 Redis 使用的最大内存.
回收 key.
在 Redis 配置文件 Redis.conf 中, 通过设置 "maxmemory" 属性的值可以限制 Redis 最大使用的内存, 修改后重启实例生效. 也可以使用客户端命令 config set maxmemory 去修改值, 这个命令是立即生效的, 但会在重启后会失效, 需要使用 config rewrite 命令去刷新配置文件.
若是启用了 Redis 快照功能, 应该设置 "maxmemory" 值为系统可使用内存的 45%, 因为快照时需要一倍的内存来复制整个数据集, 也就是说如果当前已使用 45%, 在快照期间会变成 95%(45% 45% 5%), 其中 5% 是预留给其他的开销.
如果没开启快照功能, maxmemory 最高能设置为系统可用内存的 95%.
淘汰策略
当内存使用达到设置的最大阀值时, 需要选择一种 key 的回收策略, 可在 Redis.conf 配置文件中修改 "maxmemory-policy" 属性值. 若是 Redis 数据集中的 key 都设置了过期时间, 那么 "volatile-ttl" 策略是比较好的选择. 但如果 key 在达到最大内存限制时没能够迅速过期, 或者根本没有设置过期时间. 那么设置为 "allkeys-lru" 值比较合适, 它允许 Redis 从整个数据集中挑选最近最少使用的 key 进行删除(LRU 淘汰算法).
Redis 还提供了一些其他淘汰策略, 如下:
volatile-lru: 使用 LRU 算法从已设置过期时间的数据集合中淘汰数据.
volatile-ttl: 从已设置过期时间的数据集合中挑选即将过期的数据淘汰.
volatile-random: 从已设置过期时间的数据集合中随机挑选数据淘汰.
allkeys-lru: 使用 LRU 算法从所有数据集合中淘汰数据.
allkeys-random: 从数据集合中任意选择数据淘汰
no-enviction: 禁止淘汰数据.
通过设置 maxmemory 为系统可用内存的 45% 或 95%(取决于持久化策略)和设置 "maxmemory-policy" 为 "volatile-ttl" 或 "allkeys-lru"(取决于过期设置), 可以比较准确的限制 Redis 最大内存使用率, 在绝大多数场景下使用这 2 种方式可确保 Redis 不会进行内存交换. 倘若你担心由于限制了内存使用率导致丢失数据的话, 可以设置 noneviction 值禁止淘汰数据.
3. used_memory_rss 过大解决办法
当 mem_fragmentation_ratio 远大于 1 时即 used_memory_rss/used_memory(稍大于 1 正常), 说明 Redis 中存在大量的内存碎片, 一个比较好的解决办法就是重启 Redis, 这里需要注意的是如果用的是 aof 持久化, 那么重启之前要进行 rewriteaof 操作, 否则会无效. 还有可以指定 Redis 使用的内存分配器, 一般管理员不推荐, 麻烦而且要重新编译.
来源: https://www.cnblogs.com/uncleData/p/9689322.html