Redis Set中一个成员占用多少字节
我使用 Redis 作为内存中的哈希集。当我将1M 8字节键(二进制)插入Set后,我发现Redis USED_MEMORY大约为100M,这意味着单个成员需要100字节?为什么 ?
或者我如何配置 Redis 以节省内存使用量。
I am using Redis as a in-memory hashset. After I insert 1M 8-byte keys (in binary) into a Set, I find Redis USED_MEMORY comes to about 100M, which means a single member takes 100 bytes? why ?
Or how can I conf Redis to save it memory usage.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
首先,您应该始终详细说明此类问题的设置,因为内存布局取决于操作系统、内存分配器、平台和 Redis 版本。
在装有 Redis 2.4 的 64 位 Linux 机器上,8 字节密钥的 1M 项集占用 87 MB。
与键的大小相比,这似乎很多,但是任何支持对其项进行有效访问的动态数据结构都会产生开销。您的物品越小,开销就越大。
使用 Redis,大型集合是使用单独的链接哈希表来实现的。每个条目由以下结构表示:
由于内存分配器(jemalloc)不支持 24 字节类,因此使用 32 字节。在这个结构体中,val被设置为NULL(这是一个集合),key指向一个对象,定义如下:
这个结构体只占用16个字节。它指向密钥数据本身,由以下可变长度结构表示:
密钥为 8 个字节,加上一个 nul 字符,因此每个密钥的大小为 17 个字节。下一个分配类是 jemalloc 的 32 字节,因此这个结构将占用 32 字节。
总而言之,每个项目的成本为:32+16+32 = 80 字节。他们有1M。为哈希表本身添加一些空间(包含至少 1M 指向 dictEntry 结构的指针),您将获得非常接近我们在此平台上测量的 87 MB 的结果。
优化大型集合的内存占用并不是一件小事。当集合很小(默认小于 512 个项目)并且键实际上是整数时,Redis 会执行优化。请在此处查看更多信息。
一种可能的优化是增加 set-max-intset-entries 参数,并将集合拆分为多个部分。例如,可以对项目键进行散列以将项目分布在不同的集合上。您不仅有 myset,还有 myset:0、myset:1、myset:2 ... myset:n。要检查给定的项目是否是集合,需要对键计算哈希值以找到正确的 myset:X 条目,然后检查该特定条目。目的是将所有这些集合的大小保持在 set-max-intset-entries 参数以下,以便从内存优化中受益。当然,它使得在集合上完成的所有操作变得更加复杂,因此这实际上是复杂性和内存占用之间的权衡。
First, you should always detail your setup for this kind of question, since the memory layout is dependant on the OS, memory allocator, platform and Redis version.
On a 64 bits Linux box with Redis 2.4, a 1M items set of 8 bytes keys eats 87 MB.
It seems a lot compared to the size of the keys, but any dynamic data structure supporting efficient accesses to its items involve an overhead. The smaller your items, the larger the overhead.
With Redis, large sets are implemented using separate chaining hash tables. Each entry is represented by the following structure:
Because there is no 24 bytes class supported by the memory allocator (jemalloc), 32 bytes are used. In this structure, val is set to NULL (this is a set), and key points to an object defined as follows:
This structure takes only 16 bytes. It points to the key data itself, represented by this variable-length structure:
The keys are on 8 bytes, plus a nul char, so the size will be 17 bytes per keys. The next allocation class is 32 bytes with jemalloc, so this structure will take 32 bytes.
All in all, each items will cost: 32+16+32 = 80 bytes. There are 1M ot them. Add some space for the hash table itself (containing at least 1M pointers to dictEntry struct), and you get a result which is very close to the 87 MB we can measure on this platform.
Optimizing the memory footprint of a large set is not really trivial. Redis performs optimization when the sets are small (by default less than 512 items) and the keys are actually integers. See more information here.
One possible optimization is to increase the set-max-intset-entries parameter, and split the set in various pieces. For instance item keys can be hashed to distribute the items on various sets. Instead of just myset, you have myset:0, myset:1, myset:2 ... myset:n. To check a given item is is the set, a hash value is calculated on the key to find the correct myset:X entry, and then this specific entry is checked. Purpose is to keep the size of all those sets below the set-max-intset-entries parameter to benefit from the memory optimization. Of course, it makes all operations done on the set more complex, so it is really a tradeoff between complexity and memory footprint.
如果不知道集合中每个成员的底层结构,就不可能说出来。但是,如果您存储键/值,则每个成员都存储键和值(即使该值是空的,它仍然需要保存它的引用)。
为了快速查找键,底层结构很可能是一棵树,这意味着它需要为每个成员存储指向树中左右降序节点的左指针和右指针(或红/黑)。在 64 位系统中,这些指针每个都是 8 个字节。
为了有效地分配和释放键/值对,每个成员节点可以具有指示其大小和可用性(已分配、已删除)的数据成员,以便可以从内存池中分配每个成员节点并进行垃圾收集或标记删除并重新使用。每次填充前一个池时,典型的池分配都会使池大小加倍,以最大限度地减少堆争用,这对于多线程应用程序的性能非常重要。您的 100M 内存使用量可能包含 50M 未使用(但已分配)的密钥持有者。
为什么要节省内存使用量?您打算存储数十亿个哈希键吗?
Without knowing the underlying structure of each member of the set, it's impossible to say. However, if you are storing key/values, then each member is storing the key and the value (even if the value is empty, it still needs to hold a reference for it).
For fast finds on keys, the underlying structure is most probably a tree, which means it needs to store a left and a right (or red/black) pointer to the left and right descending nodes in the tree for each member. In a 64bit system, those pointers are 8 bytes each.
To efficiently allocate and deallocate key/value pairs, the each member node may have data members that indicate it's size and it's availability (allocated, deleted), so that each member node can be allocated from a pool of memory and either garbage collected or marked as deleted and reused. Typical pool allocation doubles the pool size each time the previous pool is filled to minimize the heap contention, which is very important for performance in multithreaded applications. Your 100M of memory usage may contain 50M of unused (but allocated) key holders.
Why do you want to save memory usage? Are you planning to store billions of hash keys?