Go map[int64]int64 写入 redis 占用多少内存
我們在系統設計面試或者在實際工作中,免不了要進行一些估算。之前的文章里講過一些技巧,今天來個實戰。
這是我最近在做的一個工作,將內存中的一個超大的 map[int64]int64 寫入到 redis,map 里的元素個數是千萬級的。設計方案的時候,需要對 redis 的容量做一個估算。
如果不了解 redis 的話,可能你的答案是用元素個數直接乘以 16B(key 和 value 各占 8B)。我們假設元素個數是 5kw,那估算結果就是:5kw * 16B=50kk * 16B = 800MB。
答案是錯的。
為了解決這個問題,需要深入地研究一下 redis 的數據結構。
整個 redis 數據庫就是一個大的 map,它容納了所有的 key,我們都知道 key 都是 string 類型,而 value 則有 string, list, set, hashmap, zset……等類型。
Redis 中的一個 k-v 對用一個 entry 項表示,其中每個 entry 包含 key、value、next 三個指針,共 24 字節。由于 redis 使用 jemalloc 分配內存,因此一個 entry 需要申請 32 字節的內存。這里的 key, value 指針分別指向一個 RedisObject:
redis entrytypedef?struct?redisObject?{unsigned?type:4;unsigned?encoding:4;unsigned?lru:LRU_BITS;?int?refcount;void?*ptr; }?robj;RedisObject 對應前面提到的各種數據類型,其中最簡單的就是 redis 內部的字符串了。它有如下幾種編碼格式:
SDS 編碼(圖片來自極客時間-redis 專欄)圖中的元數據包括 type,encoding,lru, refcount,分別表示數據類型,編碼類型,最近一次訪問的時間戳,引用次數。
當字符串是一個整型時,直接放在 ptr 位置,不用再分配新的內存了,非常高效。
解析一下 44 字節的原因:元數據和 ptr 共占 16 字節,加上 44 字節,再加上字符串末尾的 '\0',共61 字節。因為字符串的長度只有 44,因此 len 和 alloc 各用 1 個字節就夠了。再加上 1 個字節的 flags,剛好是 64 字節。超過了這個值,SDS 就需要單獨再申請一塊內存,導致訪問的時候就多了一跳指針。
多提一句,redis 最大支持 512MB 大小的字符串。
回答本文的問題,恰好我們要寫入 redis 的 map 中的 key 和 value 都是整數,因此直接將值寫入 ptr 處即可。
于是 map 的一個 key 占用的內存大小為:32(entry)+16(value)+16(value)=64B。于是,5kw 個 key 占用的內存大小是 5kw*64B = 50 kk * 64B = 3200MB ≈ 3G。
假如我們在 key 前面加上了前綴,那就會生成 SDS,占用的內存會變大,訪問效率也會變差。
總之,我們根據要寫入 redis 中的字符串的長度可以很方便地估算占用內存的總大小。如果 key 和 value 恰好都是 int64 類型的,那么盡量不要在 key 前加前綴,這樣可以直接使用 key 的個數乘以 64B 就能算出占用內存的大小。
總結
以上是生活随笔為你收集整理的Go map[int64]int64 写入 redis 占用多少内存的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 读者吐槽:Go 面试总被问到 RPC
- 下一篇: 神奇的 Go init 函数