Redis集中一个成员占用的字节数

How many bytes occupied by a member in Redis Set

本文关键字:成员 字节数 一个 集中 Redis      更新时间:2023-10-16

我使用Redis作为内存中的哈希集。在我将1M个8字节的密钥(二进制)插入一个Set后,我发现Redis USED_MEMORY大约达到100M,这意味着单个成员需要100个字节?为什么?

或者如何配置Redis以节省内存使用。

首先,您应该始终详细说明此类问题的设置,因为内存布局取决于操作系统、内存分配器、平台和Redis版本。

在带有Redis 2.4的64位Linux盒子上,一个1M项目的8字节密钥集可以消耗87MB。

与键的大小相比,这似乎很大,但任何支持有效访问其项的动态数据结构都会带来开销。你的物品越小,开销就越大。

使用Redis,可以使用单独的链接哈希表来实现大型集合。每个条目由以下结构表示:

typedef struct dictEntry {
    void *key;
    void *val;
    struct dictEntry *next;
} dictEntry;

因为内存分配器(jemalloc)不支持24字节类,所以使用了32字节。在这个结构中,val被设置为NULL(这是一个集合),关键点指向如下定义的对象:

typedef struct redisObject {
    unsigned type:4;
    unsigned storage:2;     /* REDIS_VM_MEMORY or REDIS_VM_SWAPPING */
    unsigned encoding:4;
    unsigned lru:22;        /* lru time (relative to server.lruclock) */
    int refcount;
    void *ptr;
} robj;

这种结构只占用16个字节。它指向关键数据本身,由这种可变长度结构表示:

struct sdshdr {
    int len;
    int free;
   char buf[];
};

密钥为8个字节,加上一个nul字符,因此每个密钥的大小为17个字节。下一个分配类是32字节的jemalloc,所以这个结构需要32字节。

总而言之,每个项目的成本为:32+16+32=80字节。他们中有100多人。为哈希表本身添加一些空间(包含至少1M个指向dictEntry结构的指针),您会得到一个非常接近我们在这个平台上可以测量到的87MB的结果。

优化大型集合的内存占用并不是一件小事。Redis在集合较小(默认小于512项)并且键实际上是整数时执行优化。点击此处查看更多信息。

一种可能的优化是增加set max-intset entries参数,并将集合拆分为多个部分。例如,可以对项目键进行散列,以将项目分布在不同的集合上。您有myset:0、myset:1、myset:2。。。myset。要检查给定项是否为集合,则在键上计算哈希值以找到正确的myset:X条目,然后检查该特定条目。目的是将所有这些集合的大小保持在set max intset entries参数以下,以从内存优化中受益。当然,这会使在集合上完成的所有操作更加复杂,因此这实际上是复杂性和内存占用之间的权衡。

如果不知道集合中每个成员的底层结构,就不可能说出来。但是,如果存储键/值,则每个成员都存储键和值(即使值为空,它仍然需要为其保留一个引用)。

对于键的快速查找,底层结构很可能是一棵树,这意味着它需要为每个成员存储一个左指针和一个右指针(或红/黑),指向树中的左节点和右节点。在64位系统中,这些指针各为8个字节。

为了有效地分配和解除分配键/值对,每个成员节点可以具有指示其大小和可用性(已分配、已删除)的数据成员,以便可以从内存池中分配每个成员节点,并将其垃圾收集或标记为已删除和重用。典型的池分配会在每次填充前一个池时使池大小加倍,以最大限度地减少堆争用,这对多线程应用程序的性能非常重要。您使用的100M内存可能包含50M未使用(但已分配)的密钥持有者。

为什么要节省内存使用量?您是否计划存储数十亿个哈希密钥?