要估算redis中的資料佔據的記憶體大小,需要對redis的記憶體模型有比較全面的了解,包括前面介紹的hashtable、sds、redisobject、各種物件型別的編碼方式等。
下面以最簡單的字串型別來進行說明。
假設有90000個鍵值對,每個key的長度是7個位元組,每個value的長度也是7個位元組(且key和value都不是整數);下面來估算這90000個鍵值對所占用的空間。在估算佔據空間之前,首先可以判定字串型別使用的編碼方式:embstr。
90000個鍵值對佔據的記憶體空間主要可以分為兩部分:
一部分是90000個dictentry佔據的空間;一部分是鍵值對所需要的bucket空間。
每個dictentry佔據的空間包括:
1) 乙個dictentry,24位元組,jemalloc會分配32位元組的記憶體塊
2) 乙個key,7位元組,所以sds(key)需要7+9=16個位元組,jemalloc會分配16位元組的記憶體塊
3) 乙個redisobject,16位元組,jemalloc會分配16位元組的記憶體塊
4) 乙個value,7位元組,所以sds(value)需要7+9=16個位元組,jemalloc會分配16位元組的記憶體塊
5) 綜上,乙個dictentry需要32+16+16+16=80個位元組。
bucket空間:bucket陣列的大小為大於90000的最小的2^n,是131072;每個bucket元素為8位元組(因為64位系統中指標大小為8位元組)。
因此,可以估算出這90000個鍵值對佔據的記憶體大小為:90000*80 + 131072*8 = 8248576。
public
class redistest
public
static
void
insertdata()
} public
static string getmemory()
}
預估答案8248576。
執行結果:8247552
理論值與結果值誤差在萬分之1.2,對於計算需要多少記憶體來說,這個精度已經足夠了。之所以會存在誤差,是因為在我們插入90000條資料之前redis已分配了一定的bucket空間,而這些bucket空間尚未使用。
作為對比將key和value的長度由7位元組增加到8位元組,則對應的sds變為17個位元組,jemalloc會分配32個位元組,因此每個dictentry占用的位元組數也由80位元組變為112位元組。此時估算這90000個鍵值對佔據記憶體大小為:90000*112 + 131072*8 = 11128576。
在redis中驗證**如下(只修改插入資料的**):
public
static
void
insertdata()
}
執行結果:11128576;估算準確。
對於字串型別之外的其他型別,對記憶體占用的估算方法是類似的,需要結合具體型別的編碼方式來確定。
了解redis的記憶體模型,對優化redis記憶體占用有很大幫助。下面介紹幾種優化場景。
上一小節所講述的90000個鍵值便是乙個例子。由於jemalloc分配記憶體時數值是不連續的,因此key/value字串變化乙個位元組,可能會引起占用記憶體很大的變動;在設計時可以利用這一點。
例如,如果key的長度如果是8個位元組,則sds為17位元組,jemalloc分配32位元組;此時將key長度縮減為7個位元組,則sds為16位元組,jemalloc分配16位元組;則每個key所占用的空間都可以縮小一半。
如果是整型/長整型,redis會使用int型別(8位元組)儲存來代替字串,可以節省更多空間。因此在可以使用長整型/整型代替字串的場景下,盡量使用長整型/整型。
利用共享物件,可以減少物件的建立(同時減少了redisobject的建立),節省記憶體空間。目前redis中的共享物件只包括10000個整數(0-9999);可以通過調整redis_shared_integers引數提高共享物件的個數;例如將redis_shared_integers調整到20000,則0-19999之間的物件都可以共享。
考慮這樣一種場景:論壇**在redis中儲存了每個帖子的瀏覽數,而這些瀏覽數絕大多數分布在0-20000之間,這時候通過適當增大redis_shared_integers引數,便可以利用共享物件節省記憶體空間。
然而需要注意的是,不論是哪種優化場景,都要考慮記憶體空間與設計複雜度的權衡;而設計複雜度會影響到**的複雜度、可維護性。
如果資料量較小,那麼為了節省記憶體而使得**的開發、維護變得更加困難並不划算;還是以前面講到的90000個鍵值對為例,實際上節省的記憶體空間只有幾mb。但是如果資料量有幾千萬甚至上億,考慮記憶體的優化就比較必要了。
記憶體碎片率是乙個重要的引數,對redis 記憶體的優化有重要意義。
如果記憶體碎片率過高(jemalloc在1.03左右比較正常),說明記憶體碎片多,記憶體浪費嚴重;這時便可以考慮重啟redis服務,在記憶體中對資料進行重排,減少記憶體碎片。
如果記憶體碎片率小於1,說明redis記憶體不足,部分資料使用了虛擬記憶體(即swap);由於虛擬記憶體的訪問速度比物理記憶體差很多(2-3個數量級),此時redis的訪問速度可能會變得很慢。因此必須設法增大物理記憶體(可以增加伺服器節點數量,或提高單機記憶體),或減少redis中的資料。
要減少redis中的資料,除了選用合適的資料型別、利用共享物件等,還有一點是要設定合理的資料**策略(maxmemory-policy),當記憶體達到一定量後,根據不同的優先順序對記憶體進行**。
redis樂觀鎖應用案例實踐
專車系統給司機派單時,需要過濾掉已經派單的司機,防止出現將不同訂單派單給同個司機的情況發生。以下讀寫操作均是指redis進行讀寫。目前系統裡以城市維度儲存了每個城市已經處於服務中 已派過訂單 的司機列表,但讀取城市服務中司機列表和寫服務中司機列表時,未進行同步,可能由於高併發,會導致讀的司機列表在執...
redis使用案例
1.計數器 string 單執行緒,避免併發問題,保證不會出錯,毫秒級效能 命令 incrby incrby 2.佇列 list 簡單訊息佇列 使用者第幾個訪問 新聞列表排序 由於redis把資料新增到佇列是返回新增元素在佇列的第幾位,所以可以做判斷使用者是第幾個訪問這種業務 新聞列表頁面最新的新聞...
linux eventfd 應用案例
include include include include include include include include int evfd void f void p printf pid d exit n pthread self int main int argc,char argv ev...