Redis 的字符串就是 SET 和 GET 操做對應的類型,算是 Redis 裏最經常使用的類型了。redis
Redis 內部的字符串表示,沒有直接使用 C 語言字符串,而是對其進行了必定的改造,改造後的字符串在內存管理和長度計算方面的性能都有所提高。緩存
舉個例子,假設要存儲的是字符串」redis「。性能
+--------+--------+-------------+ | len | alloc | |r|e|d|i|s| | +--------+--------++-----------++ | | v v flag '\0'
這個圖就是 sds 的內存結構。sdshdr 分四個部分,從左往右一次是字符串長度、開闢的內存空間、sdshdr 的類型以及字符串自己。在字符串初始化好以後,會返回一個指針,指向字符串自己的首地址,也就是 r
的內存地址。這樣,既能方便地享受 C 語言字符串帶來的兼容性,又能夠對內存管理了如指掌。優化
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_RAW
、OBJ_ENCODING_INT
和 OBJ_ENCODING_EMBSTR
,針對這三種編碼,儘量地進行去優化內存空間。編碼
最後一個成員變量則是指向具體的內容,如字符串類型的對象就直接指向對應的 sds 字符串的地址。指針
Redis 在拿到了用戶 SET 命令的字符串後,若是用戶 SET 了一個純數字字符串,就會作這種優化。這種優化有一個好處,能夠看到 redisObject
中的 ptr
變量,這是一個指針,也就意味着,這個變量佔據 8 個字節的內存空間。8 個字節的內存空間,恰好能夠存儲一個 64 位的整數。Redis 會判斷字符串的長度是否是小於 20,若是小於 20 則會嘗試將字符串整數化。這裏的 20 是由於 64 位整型能夠表示的範圍是 [-9223372036854775808,9223372036854775807]。code
這樣作了以後,就不須要額外的空間爲這個「字符串」生成一個動態字符串,直接存在 ptr
就能夠了。對象
Redis 在轉換成整型失敗後,就會嘗試這種編碼。這種編碼實際上是把 robj 和 sds 字符串放在了同一塊內存空間中。這種編碼對內存空間優化不大,可是它們的空間是連續的。這樣作,我認爲有幾個好處。內存
目前,是對長度小於 44 的小字符串進行這種編碼。字符串
在前面的幾種嘗試都失敗以後,就只能存儲成最原始的動態字符串了。可是 Redis 在這裏依然仍是會作一點事情,就是會把動態字符串的多餘的空間給釋放掉,目前(5.0)是會釋放掉 10% 長度的冗餘空間,若是不足 10% 就不會釋放。
Redis 對內存的優化真是作到了極致,這裏還只是冰山一角,我瞭解到的還只是冰山一角。
本文爲做者本身讀書總結的文章,因爲做者的水平限制,不免會有錯誤,歡迎你們指正,感激涕零。