如何停止 redis 内存使用量随着连接数的增加而增加

Posted

技术标签:

【中文标题】如何停止 redis 内存使用量随着连接数的增加而增加【英文标题】:How can I stop redis memory usage increasing with connection count 【发布时间】:2020-11-03 20:40:22 【问题描述】:

我们在 AWS 上通过 elasticache 运行 redis,并且在运行大量刚读取的 lambda 函数时发现内存使用量激增。这是redis-cli --stat的一些示例输出

------- data ------ --------------------- load -------------------- - child -
keys       mem      clients blocked requests            connections
1002       28.11M   15      0       2751795 (+11)       53877
1002       28.07M   15      0       2751797 (+2)        53877
1002       28.07M   15      0       2751799 (+2)        53877
1002       28.11M   15      0       2751803 (+4)        53877
1002       28.07M   15      0       2751806 (+3)        53877
1001       28.11M   15      0       2751808 (+2)        53877
1007       28.08M   15      0       2751837 (+29)       53877
1007       28.08M   15      0       2751839 (+2)        53877
1005       28.10M   16      0       2751841 (+2)        53878
1007       171.68M  94      0       2752012 (+171)      53957
1006       545.93M  316     0       2752683 (+671)      54179
1006       1.07G    483     0       2753508 (+825)      54346
1006       1.54G    677     0       2754251 (+743)      54540
1006       1.98G    882     0       2755024 (+773)      54745
1006       2.35G    1010    0       2755776 (+752)      54873
1005       2.78G    1014    0       2756548 (+772)      54877
1005       2.80G    1014    0       2756649 (+101)      54877
1004       2.79G    1014    0       2756652 (+3)        54877
1008       2.79G    1014    0       2756682 (+30)       54877
1007       2.79G    1014    0       2756685 (+3)        54877

正如您所见,键的数量几乎是恒定的,但随着客户端数量的增加,内存使用量会增加到 2.8GB。这种内存模式是预期的吗?如果是,除了增加进程可用的 RAM 量之外,是否有其他方法可以缓解它?

lambda 客户端是用 Java 编写的,使用 lettuce 5.2.1.RELEASE 和 spring-data-redis 2.2.1.RELEASE

除非spring-data-redis内部有一些额外的redis交互,否则客户端代码基本如下

public <T> T get(final String label, final RedisTemplate<String, ?> redisTemplate) 
    final BoundHashOperations<String, String, T> cache = redisTemplate.boundHashOps(REDIS_KEY);
    return cache.get(label);

在我的代码库中没有RedisTemplate#keys 的用法,与redis 的唯一交互是通过RedisTemplate#boundHashOps

以下是峰值前后redis-cli info memory 的输出:

之前

# Memory
used_memory:31558400
used_memory_human:30.10M
used_memory_rss:50384896
used_memory_rss_human:48.05M
used_memory_peak:6498905008
used_memory_peak_human:6.05G
used_memory_peak_perc:0.49%
used_memory_overhead:4593040
used_memory_startup:4203584
used_memory_dataset:26965360
used_memory_dataset_perc:98.58%
allocator_allocated:32930040
allocator_active:34332672
allocator_resident:50593792
used_memory_lua:37888
used_memory_lua_human:37.00K
used_memory_scripts:0
used_memory_scripts_human:0B
number_of_cached_scripts:0
maxmemory:5140907060
maxmemory_human:4.79G
maxmemory_policy:volatile-lru
allocator_frag_ratio:1.04
allocator_frag_bytes:1402632
allocator_rss_ratio:1.47
allocator_rss_bytes:16261120
rss_overhead_ratio:1.00
rss_overhead_bytes:-208896
mem_fragmentation_ratio:1.60
mem_fragmentation_bytes:18826560
mem_not_counted_for_evict:0
mem_replication_backlog:0
mem_clients_slaves:0
mem_clients_normal:269952
mem_aof_buffer:0
mem_allocator:jemalloc-5.1.0
active_defrag_running:0
lazyfree_pending_objects:0

之后

# Memory
used_memory:4939687896
used_memory_human:4.60G
used_memory_rss:4754452480
used_memory_rss_human:4.43G
used_memory_peak:6498905008
used_memory_peak_human:6.05G
used_memory_peak_perc:76.01%
used_memory_overhead:4908463998
used_memory_startup:4203584
used_memory_dataset:31223898
used_memory_dataset_perc:0.63%
allocator_allocated:5017947040
allocator_active:5043314688
allocator_resident:5161398272
used_memory_lua:37888
used_memory_lua_human:37.00K
used_memory_scripts:0
used_memory_scripts_human:0B
number_of_cached_scripts:0
maxmemory:5140907060
maxmemory_human:4.79G
maxmemory_policy:volatile-lru
allocator_frag_ratio:1.01
allocator_frag_bytes:25367648
allocator_rss_ratio:1.02
allocator_rss_bytes:118083584
rss_overhead_ratio:0.92
rss_overhead_bytes:-406945792
mem_fragmentation_ratio:0.96
mem_fragmentation_bytes:-185235352
mem_not_counted_for_evict:0
mem_replication_backlog:0
mem_clients_slaves:0
mem_clients_normal:4904133550
mem_aof_buffer:0
mem_allocator:jemalloc-5.1.0
active_defrag_running:0
lazyfree_pending_objects:0

【问题讨论】:

你的redis服务器是什么版本的?此外,在此处显示 redis-cli info memory 的输出可能非常有帮助。你在运行keys 命令吗?否则我无法弄清楚仅仅阅读是如何导致内存使用量激增的。 根据 AWS 控制台,我们正在运行 Engine Version Compatibility = 5.0.3 您的客户是否使用大量管道从 Redis 获取数据? redis-cli info memory 在添加到问题之前/之后的详细信息以及有关我们如何在代码中与 redis 交互的一些信息 【参考方案1】:

已与 AWS 支持人员讨论过这个内存峰值的原因是 1000 个 lambda 客户端中的每一个都在用大约 5mb 的数据填充读取缓冲区,因为我们存储在 redis 中的数据是大型序列化 json 对象。

他们的建议是:

在集群中添加 2-3 个副本,并使用副本节点进行读取请求。您可以使用读取器端点对请求进行负载平衡

或使用参数控制客户端输出缓冲区,但注意客户端达到缓冲区限制后将断开连接。

client-output-buffer-limit-normal-hard-limit >> 如果客户端的输出缓冲区达到指定的字节数,客户端将断开连接。默认为零(无硬限制)。默认为 0,这意味着客户端可以使用尽可能多的内存。 client-output-buffer-limit-normal-soft-limit >> 如果客户端的输出缓冲区达到指定的字节数,客户端将断开连接,但前提是此条件持续到 client-output-buffer-limit -正常软秒。默认为零(无软限制)。 client-output-buffer-limit-normal-soft-seconds >> 对于 Redis 发布/订阅客户端:如果客户端的输出缓冲区达到指定的字节数,客户端将断开连接。默认值为 0

鉴于这些限制和我们的使用情况,我们实际上将在本例中改用 S3。

【讨论】:

以上是关于如何停止 redis 内存使用量随着连接数的增加而增加的主要内容,如果未能解决你的问题,请参考以下文章

从连接池到内存池

性能问题:查看时间负载随着时间的推移而增加

我的应用程序中的内存增加

Lepus 天兔 监控-增加redis 内存使用报警

新线程内存使用量不会停止增加

redis单线程如何支持高并发