本筆記參考《Redis設計與實作》 P84~P88
記憶體回收
Redis在對象系統中使用reference counting技術實作了記憶體回收機制。程式可以通過跟蹤對象的引用計數資訊,在适當的時候自動釋放對象并進行記憶體回收。
typedef struct redisObject {
// ...
// 引用計數
int refcount;
// ...
} robj;
refcount會随着對象的使用狀态而不斷變化:
- 建立一個新對象時,refcount被初始化為1
- 當對象被一個新程式使用時,refcount++
- 當對象不再被一個程式使用時,refcount–
- 當對象引用計數為0時,對象所占記憶體被釋放
一些API:
函數 | 作用 |
---|---|
incrRefCount | 引用計數+1 |
decrRefCount | 引用計數-1 ,為0時釋放對象 |
resetRefCount | 引用計數設定為0,不釋放對象 |
舉例:
// 建立一個字元串對象s,對象引用計數為1
robj *s = createStringObject(...)
// 對象s執行各種操作
...
// 将對象s的引用計數-1,降為0,導緻對象被釋放
decrRefCount(s)
對象共享
引用計數還帶有對象共享的作用。
在Redis中,讓多個鍵共享同一個值對象需要執行兩個步驟:
1、将資料庫鍵的值指向一個現有的值對象
2、将被共享的值對象的引用計數+1
共享對象機制對于節約記憶體非常有幫助,資料庫中儲存的相同的值對象對越,對象共享機制就能節約越多的記憶體。
Redis會在初始化伺服器時,建立一萬個字元串對象,包含了0~9999的所有整數值。當伺服器有用到這些整數字元串對象,就利用的是共享對象,而非新建立對象。
可以使用
OBJECT REFCOUNT 對象Key
來檢視引用計數。
在資料結構中嵌套了字元串對象的獨享如(linkedlist編碼的清單對象,hashtable編碼的哈希對象,hashtable編碼的集合對象,zset編碼的有序集合對象)
需要注意下面一個問題:
隻有共享對象和目标都西昂完全相同時,才會将共享對象的作為鍵的值對象,是以需要先驗證是否相等。
一個共享對象儲存的值越複雜,驗證是否相等所需要的複雜度就越高:
1、如果是整數型字元串對象,O(1)
2、如果是字元串值的字元串對象,O(n)
3、如果是包含了多個對象的對象,O(n^2)
空轉時長
lru屬性記錄了對象最後一次被指令程式通路的時間
typedef struct redisObject {
// ...
// 引用計數
unsigned lur : 22;
// ...
} robj;
使用
OBJECT IDLETIME
可以列印出鍵的空轉時長(目前時間減去鍵的值對象的lru時間)。
如果伺服器打開了
maxmemory
選項,并且回收記憶體的算法為
volatile-lru
或者
allkeys-lru
,那麼伺服器占用的記憶體數超過
maxmemory
設定的上限時,空轉時長較搞的那部分鍵會被伺服器釋放。