Redis生产实践中相关疑问记录
1. Redis相关疑问
1.1. redis内存使用率100% 就等同于redis不可用吗?
正常使用情况下,不是。
redis有【缓存淘汰机制】,Redis 在内存使用率达到 100% 时不会直接崩溃。相反,它依赖内存淘汰策略来释放内存,确保系统的稳定性。
详细参见:Redis maxmemory-policy: Eviction policy
默认配置的策略volatile_lru
volatile-lru行为: 使用 LRU(Least Recently Used,最近最少使用)算法驱逐键。volatile-lru 仅驱逐设有过期时间的键,allkeys-lru 则驱逐所有键。适用场景: 缓存场景,不介意丢失一些数据。
确保你根据实际需求配置适当的内存淘汰策略,以便在内存达到上限时,系统能够稳定地处理新请求,而不会出现写操作失败的情况(只要不是noeviction)
理论上SET GET都应该能正常操作才对(先不考虑其他复杂命令)。尽管 Redis 本身不会轻易崩溃,但如果内存耗尽且没有淘汰策略或者淘汰策略未能生效,Redis 可能拒绝新的写操作,并返回错误:OOM command not allowed when used memory > 'maxmemory’如果系统的配置或者操作系统的内存管理不当,可能会导致 Redis 进程被操作系统杀死。
1.2. redis如何删除过期的key?
过期 key 的自动删除机制。它是 Redis 用来回收内存空间的常用机制,应用广泛,本身就会引起 Redis 操作阻塞,导致性能变慢,所以,你必须要知道该机制对性能的影响。
Redis 键值对的 key 可以设置过期时间。默认情况下,Redis 每 100 毫秒会删除一些过期 key,具体的算法如下:
-
采样:ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 个数的 key,并将其中过期的 key 全部删除;
-
如果超过 25% 的 key 过期了,则重复删除的过程,直到过期 key 的比例降至 25% 以下。
ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 是 Redis 的一个参数,默认是 20,那么,一秒内基本有 200 个过期 key 会被删除。
这一策略对清除过期 key、释放内存空间很有帮助。如果每秒钟删除 200 个过期 key,并不会对 Redis 造成太大影响。但是,如果触发了上面这个算法的第二条,Redis 就会一直删除以释放内存空间。注意,删除操作是阻塞的(Redis 4.0 后可以用异步线
程机制来减少阻塞影响)。所以,一旦该条件触发,Redis 的线程就会一直执行删除,这样一来,就没办法正常服务其他的键值操作了,
就会进一步引起其他键值操作的延迟增加,Redis 就会变慢。那么,算法的第二条是怎么被触发的呢?其中一个重要来源,就是频繁使用带有相同时间参数的 EXPIREAT 命令设置过期 key,这就
会导致,在同一秒内有大量的 key 同时过期。可以类比JVM频繁GC造成的性能影响。
1.3. redis的内存组成?
使用info memory进行分析的示例
#Memoryused_memory:1072693248
used_memory_human:1023.99
Mused_memory_rss:1090519040
used_memory_rss_human:1.02
Gused_memory_peak:1072693248
used_memory_peak_human:1023.99
Mused_memory_peak_perc:100.00%
used_memory_overhead:1048576000
used_memory_startup:1024000
used_memory_dataset:23929848
used_memory_dataset_perc:2.23%
allocator_allocated:1072693248
allocator_active:1090519040
allocator_resident:1090519040
total_system_memory:16777216000
total_system_memory_human:16.00
Gused_memory_lua:37888
used_memory_lua_human:37.89
Kused_memory_scripts:1024000
used_memory_scripts_human:1.00
Mmaxmemory:1073741824
maxmemory_human:1.00
Gmaxmemory_policy:noeviction
allocator_frag_ratio:1.02
allocator_frag_bytes:17825792
allocator_rss_ratio:1.00
allocator_rss_bytes:0
rss_overhead_ratio:1.00
rss_overhead_bytes:0
mem_fragmentation_ratio:1.02
mem_fragmentation_bytes:17825792
mem_not_counted_for_evict:0
mem_replication_backlog:0
mem_clients_slaves:0
mem_clients_normal:1048576000
mem_aof_buffer:0
mem_allocator:jemalloc-5.1.0
active_defrag_running:0
lazyfree_pending_objects:0
分析和解释
从上面的 INFO memory 输出中,我们可以看到一些关键信息,这些信息表明大部分内存被缓冲区占用殆尽:
1.内存使用情况:
-
used_memory: 1072693248 (1.02 GB)
-
maxmemory: 1073741824 (1.00 GB)
上面的输出表明,当前内存使用几乎达到了配置的最大内存限制,内存已接近耗尽。
2.缓冲区占用:
-
used_memory_overhead: 1048576000 (1.00 GB)
这个值表示 Redis 开销的内存,包括缓冲区、连接和其他元数据。在这种情况下,大部分 used_memory (1.02 GB) 被
used_memory_overhead (1.00 GB) 占用,这意味着大部分内存都被缓冲区等开销占据。
3.数据集占用:
-
used_memory_dataset: 23929848 (23.93 MB)
-
used_memory_dataset_perc: 2.23%
这里显示,实际存储的数据只占了非常少的一部分内存(约 23.93 MB),而绝大部分内存被缓冲区占据。
4.客户端缓冲区:
-
mem_clients_normal: 1048576000 (1.00 GB)
这表明普通客户端连接占用了约 1.00 GB 内存,这通常意味着输出缓冲区可能已经接近或达到了设定的限制。
5.内存碎片:
-
allocator_frag_ratio: 1.02
-
mem_fragmentation_ratio: 1.02
碎片率不高,表明内存被合理使用但被缓冲区占用过多。
总结
从上面的例子可以看出,Redis 的内存几乎被缓冲区占用殆尽。以下是具体的结论:
- 当前内存使用 (used_memory) 已经接近最大内存限制 (maxmemory),即 1.02 GB 接近 1.00 GB 的限制。
- 内存开销 (used_memory_overhead) 很大,主要被客户端普通连接使用(可能是输出缓冲区),而实际的数据仅占用了很少的内存。
- 分配器和 RSS 碎片率 (allocator_frag_ratio 和 mem_fragmentation_ratio) 较低,表明碎片不是问题。
1.4. redis为什么要有缓冲区?
缓冲区的功能其实很简单,主要就是用一块内存空间来暂时存放命令数据,以免出现因为数据和命令的处理速度慢于发送速度而导致的数据丢失和性能问题。
Redis Server 的输出大小通常是不可控制的。存在bigkey的时候,就会产生体积庞大的返回数据。另外也有可能因为执行了太多命令,导致产生返回数据的速率超过了往客户端发送的速率,导致服务器堆积大量消息,从而导致输出缓冲区越来越大,占用过多内存,甚至导致系统崩溃。Redis 通过设置 client-output-buffer-limit来保护系统安全。
Redis工作原理-单客户端视角简单版:
Redis工作原理-单客户端视角复杂版:
Redis工作原理-多客户端视角简单版(含缓冲区)。
1.5. redis的性能边界
Redis的性能是有边界的,不能盲目相信所谓的高性能。真正理解性能须使用benchmark。
-
计算资源
使用通配符、Lua并发、1对多的PUBSUB、热点Key等会大量消耗计算资源,集群架构下还会导致访问倾斜,无法有效利用所有数据分片。 -
存储资源
Streaming慢消费、大Key等会占用大量存储资源,集群架构下还会导致数据倾斜,无法有效利用所有数据分片。 -
网络资源
扫描全库(KEYS命令)、大Value、大Key的范围查询(如HGETALL命令)等会消耗大量的网络资源,且极易引发线程阻塞。重要
Redis的高并发能力不等同于高吞吐能力,例如将大Value存在Redis里以期望提升访问性能,此类场景往往不会有特别大的收益,反而会影响Redis整体的服务能力。
附录
业务部署规范
Key设计规范
SDK使用规范
命令使用规范
运维管理规范