Redis 的 string

Redis 的 string

Redis 的字符串就是 SET 和 GET 操做對應的類型,算是 Redis 裏最經常使用的類型了。redis

0x00 動態字符串 sds

Redis 內部的字符串表示,沒有直接使用 C 語言字符串,而是對其進行了必定的改造,改造後的字符串在內存管理和長度計算方面的性能都有所提高。緩存

舉個例子,假設要存儲的是字符串」redis「。性能

+--------+--------+-------------+
|  len   | alloc  | |r|e|d|i|s| |
+--------+--------++-----------++
                   |           |
                   v           v
                  flag        '\0'

這個圖就是 sds 的內存結構。sdshdr 分四個部分,從左往右一次是字符串長度、開闢的內存空間、sdshdr 的類型以及字符串自己。在字符串初始化好以後,會返回一個指針,指向字符串自己的首地址,也就是 r 的內存地址。這樣,既能方便地享受 C 語言字符串帶來的兼容性,又能夠對內存管理了如指掌。優化

0x01 redisObj

typedef struct redisObject {
    unsigned type:4;
    unsigned encoding:4;
    unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
                            * LFU data (least significant 8 bits frequency
                            * and most significant 16 bits access time). */
    int refcount;
    void *ptr;
} robj;

這是 Redis 的對象的結構,每個 key 或者 value 都會存儲成這樣一個結構體。能夠看一下第二個成員變量 encoding。這是 Redis 對這個對象作的編碼操做。,Redis string 在存儲的時候,會努力進行三種方式的編碼,分別是 OBJ_ENCODING_RAWOBJ_ENCODING_INTOBJ_ENCODING_EMBSTR,針對這三種編碼,儘量地進行去優化內存空間。編碼

最後一個成員變量則是指向具體的內容,如字符串類型的對象就直接指向對應的 sds 字符串的地址。指針

0x02 OBJ_ENCODING_INT

Redis 在拿到了用戶 SET 命令的字符串後,若是用戶 SET 了一個純數字字符串,就會作這種優化。這種優化有一個好處,能夠看到 redisObject 中的 ptr 變量,這是一個指針,也就意味着,這個變量佔據 8 個字節的內存空間。8 個字節的內存空間,恰好能夠存儲一個 64 位的整數。Redis 會判斷字符串的長度是否是小於 20,若是小於 20 則會嘗試將字符串整數化。這裏的 20 是由於 64 位整型能夠表示的範圍是 [-9223372036854775808,9223372036854775807]。code

這樣作了以後,就不須要額外的空間爲這個「字符串」生成一個動態字符串,直接存在 ptr 就能夠了。對象

0x03 OBJ_ENCODING_EMBSTR

Redis 在轉換成整型失敗後,就會嘗試這種編碼。這種編碼實際上是把 robj 和 sds 字符串放在了同一塊內存空間中。這種編碼對內存空間優化不大,可是它們的空間是連續的。這樣作,我認爲有幾個好處。內存

  1. 開闢和回收空間的時候,只須要進行一次操做就能夠了,這樣作減小了 malloc 和 free 的次數。
  2. 連續的空間,對系統緩存更加友好。

目前,是對長度小於 44 的小字符串進行這種編碼。字符串

0x04 OBJ_ENCODING_RAW

在前面的幾種嘗試都失敗以後,就只能存儲成最原始的動態字符串了。可是 Redis 在這裏依然仍是會作一點事情,就是會把動態字符串的多餘的空間給釋放掉,目前(5.0)是會釋放掉 10% 長度的冗餘空間,若是不足 10% 就不會釋放。

0x05 後記

Redis 對內存的優化真是作到了極致,這裏還只是冰山一角,我瞭解到的還只是冰山一角。

本文爲做者本身讀書總結的文章,因爲做者的水平限制,不免會有錯誤,歡迎你們指正,感激涕零。
相關文章
相關標籤/搜索