[Remote Dictionary Service],也就是「遠程字典服務」,Redis。算法
Redis咱們都知道有5種基礎數據結構:分別爲:string (字符串)、list (列表)、set (集合)、hash (哈希) 和 zset (有序集合)。數組
再說這些基礎數據結構的時候,咱們先說說Redis的Key:安全
Key:網絡
Redis的鍵是二進制安全的,也就是任何binary sequence均可以做爲鍵,好比「foo」到到JPEG文件的內容,空字符串也是有效的值,Redis的鍵根據自身業務去設計,但不要過於長和短,它最大大小爲512MB,固然關於鍵的設計,官網給出了hashing的處理方式。數據結構
string:異步
它的數據結構是Key-Value,經過key獲取相應的值。Redis 的字符串是動態字符串,是能夠修改的字符串,在內存中它是以字節數組的形式存在的。優化
Redis 的字符串叫着「SDS」,也就是 Simple Dynamic String。它的結構是一個帶長度信息的字節數組。以'\0'結尾。SDS結構使用的是範型,爲何不直接用 int 呢,這是由於當字符串比較短時,存入的值 和 分配的空間 可使用 byte 和 short 來表示,ui
struct SDS<T> { T capacity; // 數組容量 T len; // 數組長度 byte flags; // 特殊標識位 byte[] content; // 數組內容 }
Redis 爲了對內存作極致的優化,不一樣長度的字符串使用不一樣的結構體來表示。它採用預分配冗餘空間的方式來減小內存的頻繁分配,Redis內部當前字符串實際分配的空間通常高於字符串長度。當字符串長度小於 1M 時,擴容都是加倍現有的空間,若是超過 1M,擴容時一次只會多擴 1M 的空間,編碼
簡單操做,格式 {SET 鍵 值,GET 鍵}.當批量SETGET的時候,會節省網絡耗時開銷.(Redis管道機制)。spa
過時事件,格式{EXPIRE 鍵 時間}. {setex 鍵 時間 值}。
計數:若是 value 值是一個整數,還能夠對它進行自增操做。自增是有範圍的,它的範圍是signed long 的最大最小值,超過了這個值,Redis 會報錯。格式{incr 鍵}.
list:
它是鏈表而不是數組,插入和刪除操做都是O(1)級別,可是索引慢O(n)。當列表彈出了最後一個元素以後,該數據結構自動被刪除,內存被回收。
Redis 的列表結構經常使用來作異步隊列使用。將須要延後處理的任務結構體序列化成字符串塞進 Redis 的列表,另外一個線程從這個列表中輪詢數據進行處理。
右邊進左邊出:隊列 右邊進右邊出:棧
咱們會發現,Redis底層存儲是一個快速鏈表的結構(quicklist).首先在列表元素較少的狀況下會使用一塊連續的內存存儲,這個結構是 ziplist,也便是壓縮列表(支持雙向遍歷)。它將全部的元素緊挨着一塊兒存儲,分配的是一塊連續的內存。當數據量比較多的時候纔會改爲 quicklist。ziplist是列表鍵和哈希鍵的底層實現之一。當一個列表鍵只包含少許列表項,而且每一個列表項要麼是小整數值,要麼就是長度較短的字符串,那麼Redis就會使用壓縮列表。
這裏,注意觀察 debug object 輸出的 encoding 字段都是 ziplist,這就表示內部採用壓
縮列表結構進行存儲。
struct ziplist<T> { int32 zlbytes; // 整個壓縮列表佔用字節數 int32 zltail_offset; // 最後一個元素距離壓縮列表起始位置的偏移量,用於快速定位到最後一個 節點 int16 zllength; // 元素個數 T[] entries; // 元素內容列表,挨個挨個緊湊存儲 int8 zlend; // 標誌壓縮列表的結束,值恆爲 0xFF }
zltail_offset就是用來快速定位到最後一個元素。倒着遍歷。
entry 塊隨着容納的元素類型不一樣,也會有不同的結構。
struct entry { int<var> prevlen; // 前一個 entry 的字節長度 int<var> encoding; // 元素類型編碼 optional byte[] content; // 元素內容 }
encoding 字段存儲了元素內容的編碼類型信息,ziplist 經過這個字段來決定後面的content 內容的形式、經過前綴位來識別具體存儲的數據形式,content 字段在結構體中定義爲 optional 類型,表示這個字段是可選的,對於很小的整數而言,它的內容已經內聯到 encoding 字段的尾部了。
由於 ziplist 都是緊湊存儲,沒有冗餘空間 (對比一下 Redis 的字符串結構)。意味着插入一個新的元素就須要調用 realloc 擴展內存。取決於內存分配器算法和當前的 ziplist 內存大小,realloc 可能會從新分配新的內存空間,並將以前的內容一次性拷貝到新的地址,也可能在原有的地址上進行擴展,這時就不須要進行舊內容的內存拷貝。若是 ziplist 佔據內存太大,從新分配內存和拷貝內存就會有很大的消耗。因此 ziplist 不適合存儲大型字符串,存儲的元素也不宜過多
當 set 集合容納的元素都是整數而且元素個數較小時,Redis 會使用 intset 來存儲結合元素。intset 是緊湊的數組結構,同時支持 16 位、32 位和 64 位整數。
struct intset<T> { int32 encoding; // 決定整數位寬是 16 位、32 位仍是 64 位 int32 length; // 元素個數 int<T> contents; // 整數數組,能夠是 16 位、32 位和 64 位 }
hash
無序字典,數組+鏈表二位結構。第一維 hash 的數組位置碰撞時,就會將碰撞的元素使用鏈表串接起來。
Set集合
它內部的鍵值對是無序的惟一的。它的內部實現至關於一個特殊的字典,字典中全部的 value 都是一個值 NULL。set 結構能夠用來存儲活動中獎的用戶 ID,由於有去重功能,能夠保證同一個用戶不會中獎兩次。
zset (有序列表)
一方面它是一個 set,保證了內部value 的惟一性,另外一方面它能夠給每一個 value 賦予一個 score,表明這個 value 的排序權重。它的內部實現用的是一種叫着「跳躍列表」的數據結構。zset 能夠用來存粉絲列表,value 值是粉絲的用戶 ID,score 是關注時間。咱們能夠對粉絲列表按關注時間進行排序。zset 內部的排序功能是經過「跳躍列表」數據結構來實現的,它的結構很是特殊,也比較複雜。