1

我正在使用 Go 和 Redis 开发一个 API。问题是内存使用不足,我找不到问题的根源。

TL;DR 版本

有成百上千的哈希对象。每个 1 KB 对象(键+值)占用约 0.5 MB 的 RAM。但是,没有内存碎片(INFO显示无)。

此外,dump.rdb 比 RAM 集小 70 倍(360KB dump.rdb 与 25MB RAM 用于 50 个对象,35.5MB 与 2.47GB 用于 5000 个对象)。

长版

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 个任务的 INFO 输出:

# 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 的 RAM!

我将不胜感激有关如何弄清楚发生了什么的任何想法。

4

1 回答 1

7

你有很多很多小的 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吗?如果是这样,也许您没有修剪它并且您正在编写更大的缓冲区或类似的东西?我和 redigo 一起工作过很多次,但从未见过你所描述的内容。

于 2015-05-25T14:15:46.127 回答