2012-01-05 49 views
3

我正在使用Redis作爲內存中的哈希集。在我將1M個8字節密鑰(二進制)插入一個Set之後,我發現Redis USED_MEMORY約爲100M,這意味着單個成員需要100個字節?爲什麼?Redis中成員佔用了多少字節集

或者我該如何配置Redis來節省內存使用量。

回答

7

首先,由於內存佈局取決於操作系統,內存分配器,平臺和Redis版本,因此首先應該詳細介紹此類問題的設置。

在帶有Redis 2.4的64位Linux機器上,一個1M字節的8字節鍵組可以吃87 MB。

與鍵的大小相比似乎有很多,但支持對其項目進行高效訪問的任何動態數據結構都涉及開銷。您的物品越小,開銷越大。

使用Redis,大集合使用單獨的鏈接哈希表來實現。每個條目由下面的結構表示:

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

因爲存在由內存分配器(jemalloc)不支持任何24個字節類,32個字節被使用。

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個字節:在該結構中,VAL是如下設置爲NULL(這是一組),和關鍵點中定義的對象。它指向密鑰數據本身,由該可變長結構表示:

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

鍵是8個字節,加上一個NUL炭,所以大小將每鍵17個字節。下一個分配類是帶有jemalloc的32個字節,所以這個結構將佔用32個字節。

總而言之,每件物品的費用爲:32 + 16 + 32 = 80字節。有1M ot他們。爲散列表本身(包含至少1M個指向dictEntry結構的指針)添加一些空間,您將得到一個非常接近我們可以在此平臺上測量的87 MB的結果。

優化大集合的內存佔用並不是微不足道的。Redis在集合很小時執行優化(默認情況下少於512個項目),而鍵實際上是整數。查看更多信息here

一個可能的優化是增加set-max-intset-entries參數,並將該集合拆分成各個部分。例如項目密鑰可以被散列來分配各種集合上的項目。而不僅僅是myset,你有myset:0,myset:1,myset:2 ... myset:n。要檢查給定的項目是集合,在鍵上計算散列值以查找正確的myset:X條目,然後檢查此特定條目。目的是將所有那些集合的大小保持在set-max-intset-entries參數之下,以從內存優化中受益。當然,它會使集合上的所有操作都變得更加複雜,因此它實際上是複雜性和內存佔用之間的折衷。

1

不知道每個成員的底層結構,這是不可能的。但是,如果要存儲鍵/值,則每個成員都存儲鍵和值(即使該值爲空,它仍需要爲其保留一個引用)。

對於鍵上的快速查找,底層結構最可能是一棵樹,這意味着它需要在樹中爲每個成員存儲左和右(或紅/黑)指針,以指向樹的左右降序節點。在64位系統中,這些指針每個都是8個字節。

爲了有效地分配和取消分配鍵/值對,每個成員節點可以有數據成員,表明它的大小和可用性(分配,刪除),以便每個成員節點都可以從內存池中分配,垃圾收集或標記爲刪除並重新使用。每次填充上一個池時,典型的池分配會使池大小加倍,以最大限度地減少堆爭用,這對於多線程應用程序中的性能非常重要。您的100M內存使用量可能包含50M未使用(但已分配)的密鑰持有者。

爲什麼要節省內存使用量?你打算存儲數十億個散列鍵嗎?

+0

這是非常錯誤的。 Redis中的集合存儲爲哈希表,而不是樹。 Redis不彙集分配:它主要依賴卓越的jemalloc通用分配器(從2.4版開始)。 Redis中沒有垃圾回收:使用引用計數。 – 2012-01-05 11:33:01

+0

感謝您的糾正。在我的辯護中,我只是在猜測Redis使用的內部結構,而我發現你的詳細答案在上面很有趣並且有啓發性。 – 2012-01-06 06:57:29