Redis 使用的 RAM 不足
Inadequate RAM usage by Redis
我正在使用 Go 和 Redis 开发 API。问题是RAM使用不足,我找不到问题的根源。
TL;DR 版本
有 hundreds/thousands 个散列对象。每个 1 KB 的对象(键+值)占用大约 0.5 MB 的 RAM。但是,没有内存碎片(INFO
显示none)。
此外,dump.rdb 比 RAM 集小 70 倍(360KB dump.rdb 对 50 个对象的 25MB RAM,以及 5000 个对象的 35.5MB 对 2.47GB)。
长版
Redis 实例主要由 task:123
以下类型的哈希填充:
"task_id" : int
"client_id" : int
"worker_id" : int
"text" : string (0..255 chars)
"is_processed" : boolean
"timestamp" : int
"image" : byte array (1 kbyte)
此外,还有几个整数计数器、一个列表和一个排序集(均由 task_id 组成)。
RAM 使用与任务对象的数量呈线性相关。
50 个任务的信息输出:
# Memory
used_memory:27405872
used_memory_human:26.14M
used_memory_rss:45215744
used_memory_peak:31541400
used_memory_peak_human:30.08M
used_memory_lua:35840
mem_fragmentation_ratio:1.65
mem_allocator:jemalloc-3.6.0
和 5000 个任务:
# Memory
used_memory:2647515776
used_memory_human:2.47G
used_memory_rss:3379187712
used_memory_peak:2651672840
used_memory_peak_human:2.47G
used_memory_lua:35840
mem_fragmentation_ratio:1.28
mem_allocator:jemalloc-3.6.0
50 个任务的 dump.rdb
大小为 360kB,5000 个任务的大小为 35553kB。
每个任务对象的序列化长度约为 7KB:
127.0.0.1:6379> DEBUG OBJECT task:2000
Value at:0x7fcb403f5880 refcount:1 encoding:hashtable serializedlength:7096 lru:6497592 lru_seconds_idle:180
我写了一个 Python 脚本试图重现这个问题:
import redis
import time
import os
from random import randint
img_size = 1024 * 1 # 1 kb
r = redis.StrictRedis(host='localhost', port=6379, db=0)
for i in range(0, 5000):
values = {
"task_id" : randint(0, 65536),
"client_id" : randint(0, 65536),
"worker_id" : randint(0, 65536),
"text" : "",
"is_processed" : False,
"timestamp" : int(time.time()),
"image" : bytearray(os.urandom(img_size)),
}
key = "task:" + str(i)
r.hmset(key, values)
if i % 500 == 0: print(i)
而且它只占用 80MB 内存!
如果有任何关于如何弄清楚发生了什么的想法,我将不胜感激。
您有很多很多小的 HASH 对象,这很好。但是它们中的每一个在redis内存中都有很多开销,因为它有一个单独的字典。对此有一个小的优化,通常可以显着改善事情,它将哈希保存在内存优化但稍慢的数据结构中,在这些对象大小下这应该无关紧要。来自配置:
# Hashes are encoded using a memory efficient data structure when they have a
# small number of entries, and the biggest entry does not exceed a given
# threshold. These thresholds can be configured using the following directives.
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
现在,您的值很大,导致此优化不起作用。
我将 hash-max-ziplist-value
设置为几 kbs(取决于最大对象的大小),它应该会改善这一点(您应该不会看到此 HASH 大小的任何性能下降)。
此外,请记住,redis 会根据内存中的内容压缩其 RDB 文件,因此无论如何预计内存会减少约 50%。
[编辑] 在重新阅读你的问题并看到它只是一个问题,并考虑到压缩的 rdb 很小的事实,有些东西告诉我你正在写比您预期的图像尺寸更大。你有没有机会从 []byte
切片中注销它?如果是这样,也许您没有 trim 它并且您正在编写一个更大的缓冲区或类似的东西?我已经像这样使用 redigo 工作了很多次,但从未见过你所描述的内容。
我正在使用 Go 和 Redis 开发 API。问题是RAM使用不足,我找不到问题的根源。
TL;DR 版本
有 hundreds/thousands 个散列对象。每个 1 KB 的对象(键+值)占用大约 0.5 MB 的 RAM。但是,没有内存碎片(INFO
显示none)。
此外,dump.rdb 比 RAM 集小 70 倍(360KB dump.rdb 对 50 个对象的 25MB RAM,以及 5000 个对象的 35.5MB 对 2.47GB)。
长版
Redis 实例主要由 task:123
以下类型的哈希填充:
"task_id" : int
"client_id" : int
"worker_id" : int
"text" : string (0..255 chars)
"is_processed" : boolean
"timestamp" : int
"image" : byte array (1 kbyte)
此外,还有几个整数计数器、一个列表和一个排序集(均由 task_id 组成)。
RAM 使用与任务对象的数量呈线性相关。
50 个任务的信息输出:
# Memory
used_memory:27405872
used_memory_human:26.14M
used_memory_rss:45215744
used_memory_peak:31541400
used_memory_peak_human:30.08M
used_memory_lua:35840
mem_fragmentation_ratio:1.65
mem_allocator:jemalloc-3.6.0
和 5000 个任务:
# Memory
used_memory:2647515776
used_memory_human:2.47G
used_memory_rss:3379187712
used_memory_peak:2651672840
used_memory_peak_human:2.47G
used_memory_lua:35840
mem_fragmentation_ratio:1.28
mem_allocator:jemalloc-3.6.0
50 个任务的 dump.rdb
大小为 360kB,5000 个任务的大小为 35553kB。
每个任务对象的序列化长度约为 7KB:
127.0.0.1:6379> DEBUG OBJECT task:2000
Value at:0x7fcb403f5880 refcount:1 encoding:hashtable serializedlength:7096 lru:6497592 lru_seconds_idle:180
我写了一个 Python 脚本试图重现这个问题:
import redis
import time
import os
from random import randint
img_size = 1024 * 1 # 1 kb
r = redis.StrictRedis(host='localhost', port=6379, db=0)
for i in range(0, 5000):
values = {
"task_id" : randint(0, 65536),
"client_id" : randint(0, 65536),
"worker_id" : randint(0, 65536),
"text" : "",
"is_processed" : False,
"timestamp" : int(time.time()),
"image" : bytearray(os.urandom(img_size)),
}
key = "task:" + str(i)
r.hmset(key, values)
if i % 500 == 0: print(i)
而且它只占用 80MB 内存!
如果有任何关于如何弄清楚发生了什么的想法,我将不胜感激。
您有很多很多小的 HASH 对象,这很好。但是它们中的每一个在redis内存中都有很多开销,因为它有一个单独的字典。对此有一个小的优化,通常可以显着改善事情,它将哈希保存在内存优化但稍慢的数据结构中,在这些对象大小下这应该无关紧要。来自配置:
# Hashes are encoded using a memory efficient data structure when they have a
# small number of entries, and the biggest entry does not exceed a given
# threshold. These thresholds can be configured using the following directives.
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
现在,您的值很大,导致此优化不起作用。
我将 hash-max-ziplist-value
设置为几 kbs(取决于最大对象的大小),它应该会改善这一点(您应该不会看到此 HASH 大小的任何性能下降)。
此外,请记住,redis 会根据内存中的内容压缩其 RDB 文件,因此无论如何预计内存会减少约 50%。
[编辑] 在重新阅读你的问题并看到它只是一个问题,并考虑到压缩的 rdb 很小的事实,有些东西告诉我你正在写比您预期的图像尺寸更大。你有没有机会从 []byte
切片中注销它?如果是这样,也许您没有 trim 它并且您正在编写一个更大的缓冲区或类似的东西?我已经像这样使用 redigo 工作了很多次,但从未见过你所描述的内容。