Redis設計與實現:讀書筆記之一


第一部分:數據結構與對象

  1. Redis支持的數據類型
    • 字符串對象
    • 列表對象
    •           Hash對象
    • 集合對象
    • 有序集合對象

2.數據結構

  • Redis的所有數據類型都是:
    • key-value pair
    • 對象
  • Redis的Key是字符串對象

3.公共命令

DEL key [key ...]

刪除一個key

MIGRATE host port key destination-db timeout

原子性的將key從redis的一個實例移到另一個實例

PTTL key

獲取key的有效毫秒數

SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...

對隊列、集合、有序集合排序

DUMP key

導出key的值

MOVE key db

移動一個key到另一個數據庫

RANDOMKEY

返回一個隨機的key

TTL key

獲取key的有效時間(單位:秒)

EXISTS key

查詢一個key是否存在

OBJECT subcommand [arguments [arguments ...]]

檢查內部的再分配對象

RENAME key newkey

將一個key重命名

TYPE key

獲取key的存儲類型

EXPIRE key seconds

設置一個key的過期的秒數

PERSIST key

移除key的過期時間

RENAMENX key newkey

重命名一個key,新的key必須是不存在的key

EXPIREAT key timestamp

設置一個UNIX時間戳的過期時間

PEXPIRE key milliseconds

設置一個key的過期的毫秒數

RESTORE key ttl serialized-value

Create a key using the provided serialized value, previously obtained using DUMP.

KEYS pattern

查找所有匹配給定的模式的鍵

PEXPIREAT key milliseconds-timestamp

設置一個帶毫秒的UNIX時間戳的過期時間

SCAN cursor [MATCH pattern] [COUNT count]

增量迭代key

第二部分:簡單動態字符串(Simple dynamic string,SDS,sdshdr)

     1.SDS的定義

clip_image001

     2.SDS優點

  • Len計算的復雜度為O(1),傳統的C語言求Length的復雜度為O(N)
  • 通過空間預分配,減少了字符串修改時帶來的內存重新分配的性能問題。
  • 通惰性過釋放,提升字符串縮短操作的性能。
  • 通過len判斷字符串是否結束,而不是'\0',確保數據是二進制安全的。

clip_image002

第三部分:鏈表(list)

     1.鏈表定義

作為redis列表鍵的底層實現之一(當元素較多 或者包含的元素都是較長的字符串時)。

clip_image003

clip_image004

  • dup函數用於復制鏈表節點所保存的值。
  • free函數用於釋放鏈表節點所保存的值。
  • match函數用於對比鏈表中的值與輸入的比對峙是否相等。

第三部分:字典(dict)

     1.字典的實現與定義

字典是通過Hashtable實現的。是redis中哈希鍵的實現之一(當元素數量較多,或者元素值都是 比較長的字符串時)。

clip_image005

clip_image006

clip_image007

大小為4的空hashtable的結構如下:

clip_image008

  • dictht.table屬性是一個數組,記錄了dictEntry。
  • 每個dictEntry結構保存一個鍵值對
  • dictht.size記錄了哈希表的大小,即table數組的大小
  • dictht.used記錄了哈希表中已有的節點數量
  • dictht.sizemask=size-1
  • dictEntry.next屬性指向另外一個哈希表節點的指針,可以將多個hast值相同的鍵值對連接在一起,以解決鍵沖突的問題

clip_image009

  • dict.type和dict.privdata是針對不同類型的鍵值對,為創建多態字典而創建的。
  • dict.ht是一個包含了兩個元素的數組,一般 情況下只使用ht[0],ht[1]只在對ht[0]進行rehash時使用。
  • rehashindex記錄了rehash的進度。

     2.Hash算法

  • 當將一個新的鍵值對添加到字典中時,程序需要現對key值進行hash計算和索引值計算。然后根據索引值,將包含新鍵值對的dictEntry放到dictht.table的指定索引上。具體邏輯如下:

clip_image010

  • 鍵沖突:當兩個或以上的鍵被分配到dictht.table中的同一個索引上時,就會發出沖突。Redis通過dictEntry.Next創建鏈表解決此問題。程序默認總是把新節點放入到dictEntry.next的表頭位置。

clip_image011

     3.Rehash

  • 隨着操作的不斷執行,哈希表保存的鍵值會逐漸增多或者減少,為了讓哈希表的負載因子(used/size)保存在一個合理的范圍內,當哈希表中的鍵值對數量數量太多和太少時,程度對哈希表的大小進行擴容或收縮。
  • rehash步驟如下:
    • 為dict.ht[1]分配表空間,其大小取決於dict.ht[0].used.
    • 將保存在dict.ht[0]中的所有鍵值hash到dict.ht[1]中。Rehash是指重新計算哈希值和索引值。
    • 釋放dict.ht[0]
    • 將dict.ht[1]設置為dict.ht[0]
  • rehash時機
    • 服務沒有執行BGSave或BGRewirteAOF命令,並且負載因子>1
    • 服務器只在執行BGSave或BGRewirteAOF命令,並且負載因子>5
  • 漸進式rehash,如果ht數量巨大,一次性的rehash會阻塞服務。所以rehash都是漸進式的,步驟如下:
    • 為dict.ht[1]分配表空間,讓字典同時持有兩個哈希表
    • 將rehashindex設置為0(默認為-1),開始漸進式rehash。
    • 在rehash進行時,每次對字典進行CRUD操作,程序除了執行指定操作外,還會順帶將ht[0]在rehashindex索引上的所有鍵值對rehash到ht[1]上。完成后將rehashindex+1.
    • 隨着字典操作的不斷增加,ht[0]所有的值rehash到ht[1]中,rehashindex設置為-1,rehash完成。

第四部分:跳躍表(zskiplist)

     1.定義

一種有序數據結構,通過在每個節點中維持多個指向其他節點的指針,達到快速訪問節點的目的。是有序集合鍵的底層實現之一。

clip_image012

     zskiplist結構:

  • header:指向跳躍表的表頭節點
  • tail:指向跳躍表的表尾節點
  • level:記錄當前跳躍表內,層次最大的那個節點的層次(header不參與計算)。

     zskiplistnode結構:

  • level:

第五部分:整數集合(intset)

     1.定義

用於保存整數值的集合數據結構。集合鍵的底層實現之一(當集合鍵都是數值,並且集合元素數量 不多時,才采用)。

clip_image013

  • contents:contents中的項按照值大小從小到大有序排列,數組中不包含任何重復項。
  • length:contents的長度。
  • encoding:
    • =INTSET_ENC_INT16:contents為int16_t數組,數組中的每個項目也是int16_t類型,取值范圍:-32768~32767
    • =INTSET_ENC_INT32:contents為int32_t數組,數組中的每個項目也是int32_t類型,取值范圍:-2147483648~2147483647
    • =INTSET_ENC_INT64:contents為int64_t數組,數組中的每個項目也是int64_t類型,取值范圍:-9223372036854775808~9223372036854775807

     2.升級

當新加入的元素值比現有類型要長時,集合要先進行upgrade。步驟為:

  • 根據新元素類型,擴展整數集合底層數組空間大小,並未新元素分配空間
  • 將數據組中原有數值轉換為與新元素相同的數據類型,並將轉換后的數值放置到正確位置上
  • 將新元素加入到新數組中

第七部分:壓縮列表(ziplist)

     1.定義

列表鍵和哈希鍵底層實現之一。當列表鍵值包含少量列表項目,並且要么每個列表項都是小值,要不是長度比較短的字符串,redis就使用壓縮列表做列表鍵的底層實現。

是為節約內存而開發。

由一系列特殊編碼的連續內存快組成的順序型數據結構

clip_image014

clip_image015

     2.連鎖更新

          entryX中的對象結構如下,

clip_image016

clip_image017

考慮一種情況,在一個壓縮列表中,有多個連續的、長度介於250~253字節之間的節點,因為所有節點的長度都小於254,所以previous_entry_length屬性都是一個字節長。如果在壓縮表頭加入一個長度大於254的新節點,后續節點的previous_entry_length都需要變為5個字節長度,從而引發連鎖的結構變更。

實際應用中,這種情況較少。

第八部分:對象

     1.定義

          SDS、鏈表、字典、壓縮列表、整數集合是Redis中的主要數據結構,但是Redis並沒有直接使用這些數據結構實現鍵值對數據庫。而是通過這些結構創建了一個對象系統,這些對象包括字符串對象、列表對象、哈希對象、集合對象、有序集合對象五類對象。

           Redis使用對象表示數據庫中的鍵與值。每當我們在數據庫中創建一個鍵值對時,都會創建兩個對象:鍵對象、值對象,每個對象都有redisObject結構表示。

clip_image018

說明:redisobject還包含了refcount、lru屬性,分別用於計算引用次數、對空時長

  • type:對象類型

clip_image019

clip_image020

  • encoding:記錄了對象的使用數據結構

clip_image021

clip_image022

     2.字符串對象

字符串對象的編碼可以是int、embstr、raw。

clip_image023

clip_image024

如果字符串的長度<=32,字符串對象使用embstr編碼存儲。 embstr比raw性能更高。

差異化:embstr需要一次構造、一次釋放,二raw不是。

          命令:  

APPEND key value

追加一個值到key上

GETBIT key offset

返回位的值存儲在關鍵的字符串值的偏移量

MGET key [key ...]

獲得所有key的值

SETEX key seconds value

設置key-value並設置過期時間(單位:秒)。seconds是過期時間。

BITCOUNT key [start] [end]

統計字符串指定起始位置的字節數

GETRANGE key start end

獲取存儲在key上的值的一個子字符串

MSET key value [key value ...]

設置多個key value

SETNX key value

SETNX是"SET if Not eXists

MSETNX key value [key value ...]

設置多個key value,僅當key存在時

GETSET key value

設置一個key的value,並獲取設置前的值

SETRANGE key offset value

覆蓋key對應的string的一部分,從指定的offset處開始,覆蓋value的長度

DECR key

整數原子減1。對key對應的數字做減1操作。如果key不存在,那么在操作之前,這個key對應的值會被置為0。如果key有一個錯誤類型的value或者是一個不能表示成數字的字符串,就返回錯誤。這個操作最大支持在64位有符號的整型數字

INCR key

執行原子加1操作

PSETEX key milliseconds value

Set the value and expiration in milliseconds of a key

STRLEN key

獲取指定key值的長度

DECRBY key decrement

原子減指定的整數

INCRBY key increment

執行原子增加一個整數

SET key value

設置一個key的value值

GET key

獲取key的值

INCRBYFLOAT key increment

執行原子增加一個浮點數

SETBIT key offset value

設置或者清空key的value(字符串)在offset處的bit值

          3.列表對象

               列表對象的編碼可以是:ziplist或linkedlist.

clip_image025

clip_image026

clip_image027

clip_image028

clip_image029

命令:

BLPOP key [key ...] timeout

刪除,並獲得該列表中的第一元素,或阻塞,直到有一個可用

LLEN key

獲得隊列(List)的長度

LREM key count value

從列表中刪除元素

RPUSH key value [value ...]

從隊列的右邊入隊一個元素

BRPOP key [key ...] timeout

刪除,並獲得該列表中的最后一個元素,或阻塞,直到有一個可用

LPOP key

從隊列的左邊出隊一個元素

LSET key index value

設置隊列里面一個元素的值

RPUSHX key value

設置隊列里面一個元素的值

BRPOPLPUSH source destination timeout

彈出一個列表的值,將它推到另一個列表,並返回它;或阻塞,直到有一個可用

LPUSH key value [value ...]

從隊列的左邊入隊一個或多個元素

LTRIM key start stop

修剪到指定范圍內的清單

LINDEX key index

獲取一個元素,通過其索引列表

LPUSHX key value

當隊列存在時,從隊到左邊入隊一個元素

RPOP key

從隊列的右邊出隊一個元素

LINSERT key BEFORE|AFTER pivot value

在列表中的另一個元素之前或之后插入一個元素

LRANGE key start stop

從列表中獲取指定返回的元素

RPOPLPUSH source destination

刪除列表中的最后一個元素,將其追加到另一個列表

4.哈希對象

哈希對象的編碼可以是ziplist或hashtable

clip_image030

clip_image031

clip_image032

clip_image033

   命令:

HDEL key field [field ...]

刪除一個或多個哈希域

HINCRBY key field increment

將哈希集中指定域的值增加給定的數字

HMGET key field [field ...]

獲取hash里面指定字段的值

HSETNX key field value

設置hash的一個字段,只有當這個字段不存在時有效

HEXISTS key field

判斷給定域是否存在於哈希集中

HINCRBYFLOAT key field increment

將哈希集中指定域的值增加給定的浮點數

HMSET key field value [field value ...]

設置hash字段值

HVALS key

獲得hash的所有值

HGET key field

讀取哈希域的的值

HKEYS key

獲取hash的所有字段

HSCAN key cursor [MATCH pattern] [COUNT count]

迭代hash里面的元素

HGETALL key

HGETALL key

HLEN key

獲取hash里所有字段的數量

HSET key field value

設置hash里面一個字段的值

          5.集合對象

集合對象的編碼可以是:intset或hashtable

clip_image034

clip_image035

clip_image036

     命令

SADD key member [member ...]

添加一個或者多個元素到集合(set)里

SINTER key [key ...]

獲得兩個集合的交集

SMOVE source destination member

移動集合里面的一個key到另一個集合

SSCAN key cursor [MATCH pattern] [COUNT count]

迭代set里面的元素

SCARD key

獲取集合里面的元素數量

SINTERSTORE destination key [key ...]

獲得兩個集合的交集,並存儲在一個關鍵的結果集

SPOP key

刪除並獲取一個集合里面的元素

SUNION key [key ...]

添加多個set元素

SDIFF key [key ...]

獲得隊列不存在的元素

SISMEMBER key member

確定一個給定的值是一個集合的成員

SRANDMEMBER key [count]

從集合里面隨機獲取一個key

SUNIONSTORE destination key [key ...]

合並set元素,並將結果存入新的set里面

SDIFFSTORE destination key [key ...]

獲得隊列不存在的元素,並存儲在一個關鍵的結果集

SMEMBERS key

獲取集合里面的所有key

SREM key member [member ...]

從集合里刪除一個或多個key

6.有序集合對象

有序集合對象的編碼可以是ziplist、skiplist

clip_image037

clip_image038

skiplist編碼的有序集合對象使用zset結構最為底層實現,其結構為:

Typeof struct zset

{

Zskiplist *zsl;

Dict *dict;

}

clip_image039

clip_image040

命令:

ZADD key score member [score member ...]

添加到有序set的一個或多個成員,或更新的分數,如果它已經存在

ZRANGE key start stop [WITHSCORES]

返回的成員在排序設置的范圍,由指數

ZREMRANGEBYSCORE key min max

刪除一個排序的設置在給定的分數所有成員

ZSCORE key member

獲取成員在排序設置相關的比分

ZCARD key

獲取一個排序的集合中的成員數量

ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

返回的成員在排序設置的范圍,由得分

ZREVRANGE key start stop [WITHSCORES]

在排序的設置返回的成員范圍,通過索引,下令從分數高到低

ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ..

添加多個排序集和導致排序的設置存儲在一個新的關鍵

ZCOUNT key min max

給定值范圍內的成員數與分數排序

ZRANK key member

確定在排序集合成員的索引

ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]

返回的成員在排序設置的范圍,由得分,下令從分數高到低

ZINCRBY key increment member

增量的一名成員在排序設置的評分

ZREM key member [member ...]

從排序的集合中刪除一個或多個成員

ZREVRANK key member

確定指數在排序集的成員,下令從分數高到低

ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...

相交多個排序集,導致排序的設置存儲在一個新的關鍵

ZREMRANGEBYRANK key start stop

在排序設置的所有成員在給定的索引中刪除

ZSCAN key cursor [MATCH pattern] [COUNT count]

迭代sorted sets里面的元素

7.內存回收

Redis通過構建了一個引用計數器,來跟蹤對象的引用情況。在適當的時候進行回收。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM