天天看點

Redis使用文檔一

1 redis概述

1.1前言

redis是一個開源、支援網絡、基于記憶體亦可持久化的日志型、鍵值對存儲資料庫。使用ansi c編寫,并提供多種語言的api。其開發由vmware主持,是最流行的鍵值對存儲資料庫之一。

redis的一大特點就是速度異常快,官方公布的性能測試結果顯示,每秒鐘可以達到10萬次的操作。

1.2安裝和驗證

我們下載下傳了redis的穩定版redis-2.8.9.tar.gz。

我們依次執行以下指令:

     $ tar xzf redis-2.8.9.tar.gz

     $ cd redis-2.8.9

     $ make

 在執行完以上指令後,會在同級目錄下生成src目錄。再執行指令:

      $ src/redis-server

 就啟動好了redis。

  快速驗證服務是否啟動成功可以執行以下指令:

        $ src/redis-cli

        redis> set foo bar

        ok

        redis> get foo

        "bar"

2 redis資料結構 

redis以鍵值的形式存儲我們的資料。

redis key值是二進制安全的,這意味着可以用任何二進制序列作為key值,例如字元串、或者一個jpeg的檔案。特殊地,空字元串是一個有效的key值。

另外,對于我們的系統應用,如果多個系統公用一個redis執行個體,為了避免鍵值沖突,一個解決的辦法,就是在key中包含系統名稱。例如有兩個使用者賬号資訊的key,分别為:system1.account_info.123456,system2.account_info.123456。當然,後邊我們會提到,通過database來區分key的空間,也是一個不錯的方案。 

redis提供了五種資料類型:string(字元串)、list(雙向連結清單)、set(集合)、zset(有序集合)和hash(哈希類型)。

string(字元串):最常見的值,例如“aaa”、“{no:'1234',name:'張三'}”等等。redis支援對其包括set、get、incr、append、strlen等操作。

list(雙向連結清單):數組,多用于1個key對應多個value的場景。redis支援對其進行lpop、lset、rpush等操作。

set(集合):也是一個key對應多個valude的場景。但對其的操作,主要是和集合相關的,例如sadd、smove、sdiff、sunion等。

zset(有序集合):存儲元素和set相近,但是内部以一個score來排序我們放進去的資料。redis對其支援的操作有zadd、zrange、zrevrange等操作。

hash(哈希):值又是一個key-value鍵值對的集合。如果整個redis相當于一個java裡的hashmap的話,類型為hash的redis存儲值又是一個hashmap。對其的操作包括hget、hdel、hset、hkeys等。

3 如何使用redis

現階段,我們可以通過兩種方式來使用redis:指令方式、用戶端方式。

3.1 redis指令

參考下列網址的說明 https://redis.readthedocs.org/en/latest/

3.2 java 用戶端

 jedis是redis首推的java用戶端開發包。該項目在git的源代碼在:https://github.com/xetorthio/jedis。

 jedis主要功能是對redis的所有指令操作進行封裝,供java開發人員調用。jedis處理我們的每個指令調用過程如下:

     a.   根據提供的ip、port、password連接配接redis伺服器,并持有連接配接;

     b.   接收各個指令及其參數;

     c.   對參數按utf8格式編碼成byte[];

     d.   将byte[]組裝成符合redis協定格式的順序,并添加redis格式要求的一些分隔符;例如:将byte[]形式的參數1{ 0x01, 0x02, 0x03, 0x04 }和參數2{ 0x05, 0x06, 0x07, 0x08 }之間用“\r”和“\n”分開。(更多地關于redis協定的内容,請關注4.3.1redis協定)

     e.   通過發送tcp請求(socket),将組裝後的redis協定内容發送到redis伺服器執行。

     f.    接收redis傳回的符合redis協定的指令執行結果,通過utf8格式将byte[]轉成str,解析出響應字元串,作為指令的執行結果傳回給使用者;

     g.   如果需要,關閉連接配接。

 在jedis中,jedis.class是提供給開發人員使用的api類。jedis.class繼承自binaryjedis.class。前者接收明文的參數,例如“aa”,後者接收byte[]形式的參數。如果我們調用jedis.class中的api,根據上述過程,我們可以看到,明碼的參數會被轉成byte[]形式的參數,最終調用binaryjedis.class中的api完成我們的指令執行。

 面向redis管理的操作封裝類包括:client,connection,protocol。 

 鑒于jedis的工程代碼比較簡單,而且有很多的範例和測試代碼,在此隻是簡單地說明一下工程的包結構以及查找案例的方法。

 redis.clients.jedis.tests.benchmark

 正如包名一樣,此包下面的代碼為用戶端的示例代碼。如果剛接觸jedis和redis,則可以直接修改其中的ip和端口等,體驗一下redis。

 redis.clients.jedis.tests.commands

 該包下面是對jedis的所有commond的單元測試案例。其測試的代碼比較簡潔,如果大家對jedis的某個指令使用不太明确,在此處搜尋其使用方法,應該是一個不錯的選擇。

 更多關于jedis的細節,我們可以直接看jedis的源碼。

3.3 和redis通信

接下來,我們看看如果哪天我們覺得jedis不好用了,我們想自己寫一套用戶端,我們應該怎麼來和redis通信互動。

首先,redis和外部通信,隻支援tcp協定,端口預設為6379。

其次,如果想要redis能解析你發給它的指令和參數,我們的指令和參數必須符合redis協定。另外,redis回複給我們的響應資訊,也是按照redis協定來組裝。接下來,我們詳細看看redis協定是怎麼回事。

3.3.1 redis協定

在這個協定中,所有發送至redis 伺服器的參數都是二進制安全(binary safe)的。

我們先看一個實際的例子:

我們想發送一個set(“mykey”, “myvalue”)的指令給redis,那麼這條指令最終會被轉換成符合redis協定的形式(真正傳給redis的,是byte[]型資料,這裡隻是為了便于說明和看清問題,因而用沒有轉換),其内容如下:

"*3\r\n$3\r\nset\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"

我們可以對照協定的說明來解讀下上述指令。

協定的格式為:

*<number of arguments> cr lf

$<number of bytes of argument 1> cr lf

<argument data> cr lf

...

$<number of bytes of argument n> cr lf

我們結合協定的格式解讀一下上邊實際的例子:

 a.   cr表示\r,lf表示\n;每個redis指令或者用戶端和伺服器之間傳輸的資料都以\r\n (crlf)結束;

 b.   第一個以*号開始的數字表示參數的個數;其中,我們的指令名稱set是第一個參數;

 c.   各個參數以$開始來表示;

 d.   第一個$後緊接着的内容為第一個參數的長度;

 e.   第一個$後的crlf後的内容(”set”)為第一個參數的内容;

 f.    以此内推,第二個參數的長度為5,值為mykey

 g.   第三個參數的長度為7,值為myvalue

 h.   不要忘了,最後一個參數後還要緊跟一個crlf作為該資料結束的标志

另外,對于回複,redis也有一些規定,我們可以參照附屬資料《redis大全》通信協定.回複、狀态回複等章節的相關說明。

4 redis事務

redis通過multi、discard、exec和watch四個指令來實作事務功能。

4.1hello world

一個簡單地使用事務的例子如下所示:

redis> multi

ok

redis> set book-name "mastering c++ in 21 days"

queued

redis> get book-name

redis> sadd tag "c++" "programming" "mastering series"

redis> smembers tag

redis> exec

1) ok

2) "mastering c++ in 21 days"

3) (integer) 3

4) 1) "mastering series"

   2) "c++"

   3) "programming"

4.2 開始事務multi

multi指令用于開始一個事務。該指令的效果,即是将服務端上記錄的用戶端狀态改為事務狀态。

該指令傳回“ok”。

4.3 添加要執行的指令

redis服務端上的用戶端狀态修改為事務狀态後,一個明顯的差別就是:所有發送給服務端執行的指令(除了用于事務處理的四個指令,multi、discard、exec和watch),不是馬上執行以及傳回執行結果;取而代之的是,這些指令會被redis放到一個指令隊列中,并傳回用戶端“queued”。 

 redis上的事務指令隊列是一個數組,每個數組項包含三個屬性:

 1. 要執行的指令(cmd);

 2. 指令的參數(argv);

 3. 參數的個數(argc)。

4.4 執行事務exec

 如果用戶端正處于事務狀态,當用戶端再發送exec指令到服務端時,redis服務端的執行過程如下:

 1. 服務端會從用戶端所儲存事務指令隊列中,以fifo的順序取出指令執行,并且把每條指令執行的結果儲存到一個fifo的回複隊列中;

 2.當事務指令隊列中的所有指令都被執行完了以後,exec指令會将回複隊列中的結果全部傳回給用戶端。

4.5 取消事務

 discard 指令用于取消一個事務,它清空用戶端的整個事務隊列,然後将用戶端從事務狀态調整回非事務狀态,最後傳回字元串ok給用戶端,說明事務已被取消。

 之前已經提到過,在用戶端輸入了exec指令開啟一個事務後,和事務相關的multi,exec,discard和watch四個指令不會被放到用戶端的事務指令隊列中。

 那麼如果在用戶端已經開啟一個事務的情況下,用戶端輸入上述四個指令,redis會怎麼處理呢?我們下邊來看看。

multi

 redis 的事務是不可嵌套的, 當用戶端已經處于事務狀态, 而用戶端又再向伺服器發送 multi 時, 伺服器隻是簡單地向用戶端發送一個錯誤, 然後繼續等待其他指令的入隊。 multi 指令的發送不會造成整個事務失敗, 也不會修改事務隊列中已有的資料。

watch

隻能在用戶端進入事務狀态之前執行, 在事務狀态下發送 watch 指令會引發一個錯誤, 但它不會造成整個事務失敗, 也不會修改事務隊列中已有的資料(和前面處理 multi 的情況一樣)。

當然,最後,exec會執行事務隊列中的全部指令;discard會清空事務隊列中的指令,并修改用戶端狀态。

4.6帶watch的事務

watch指令用于在事務開始之前監視任意數量的鍵:當調用exec指令執行事務時,如果任意一個被監視的鍵已經被其他用戶端修改了,那麼整個事務不再執行,直接傳回失敗。

以下示例展示了一個執行失敗的事務例子:

redis> watch name

redis> set name peter

(nil)

        注意最後exec指令傳回的不再是set指令執行的結果。

4.7 watch指令的實作和觸發

在每個代表資料庫的redis.h/redisdb 結構類型中,都儲存了一個 watched_keys 字典,字典的鍵是這個資料庫被監視的鍵,而字典的值則是一個連結清單,連結清單中儲存了所有監視這個鍵的用戶端。

比如說,以下字典就展示了一個 watched_keys 字典的例子:

Redis使用文檔一

<!--[if !supportlinebreaknewline]-->

<!--[endif]-->

其中,鍵 key1 正在被 client2 、 client5 和 client1 三個用戶端監視, 其他一些鍵也分别被其他别的用戶端監視着。

watch 指令的作用, 就是将目前用戶端和要監視的鍵在 watched_keys 中進行關聯。

舉個例子,如果目前用戶端為client10086,那麼當用戶端執行watch key1 key2時,前面展示的watched_keys 将被修改成這個樣子:

Redis使用文檔一

通過watched_keys字典,如果程式想檢查某個鍵是否被監視,那麼它隻要檢查字典中是否存在這個鍵即可;如果程式要擷取監視某個鍵的所有用戶端,那麼隻要取出鍵的值(一個連結清單),然後對連結清單進行周遊即可。

在任何對資料庫鍵空間(keyspace)進行修改的指令成功執行之後(比如flushdb、set、del、lpush、sadd、zrem,諸如此類),multi.c/touchwatchedkey函數都會被調用——它檢查資料庫的watched_keys字典,看是否有用戶端在監視已經被指令修改的鍵,如果有的話,程式将所有監視這個/這些被修改鍵的用戶端的redis_dirty_cas選項打開。

當用戶端發送exec指令、觸發事務執行時,伺服器會對用戶端的狀态進行檢查:

 如果用戶端的redis_dirty_cas選項已經被打開,那麼說明被用戶端監視的鍵至少有一個已經被修改了,事務的安全性已經被破壞。伺服器會放棄執行這個事務,直接向用戶端傳回空回複,表示事務執行失敗。

 如果redis_dirty_cas選項沒有被打開,那麼說明所有監視鍵都安全,伺服器正式執行事務。

4.8 事務狀态和非事務狀态下執行指令

無論在事務狀态下, 還是在非事務狀态下, redis 指令都由同一個函數執行, 是以它們共享很多伺服器的一般設定, 比如 aof 的配置、rdb 的配置,以及記憶體限制,等等。

不過事務中的指令和普通指令在執行上還是有一點差別的,其中最重要的兩點是:

     1.非事務狀态下的指令以單個指令為機關執行,前一個指令和後一個指令的用戶端不一定是同一個;而事務狀态則是以一個事務為機關,執行事務隊列中的所有指令:除非目前事務執行完畢,否則伺服器不會中斷事務,也不會執行其他用戶端的其他指令。

      2.在非事務狀态下,執行指令所得的結果會立即被傳回給用戶端;而事務則是将所有指令的結果集合到回複隊列,再作為 exec 指令的結果傳回給用戶端。

4.9 更多的redis事務說明

官方說明

根據redis官方文檔,redis事務有以下兩個重要的保證:

 1. 所有的事務指令隊列中的指令會被順序地執行;并且,在一個用戶端的事務正在執行時,其他的用戶端的請求都将不會執行。

 2. 不管事務指令隊列中是否有指令,redis都是具有原子性的。也就是說,exec指令都将執行隊列中的所有指令。基于此,發生以下錯誤時,redis的處理如下:

     a.如果在multi指令執行之前,用戶端的連接配接在斷開了,則什麼也不會執行;

      b. 如果exec指令已經調用,而用戶端的連接配接斷開了,則所有的指令都将執行;

       c.  如果事務指令隊列中已經存在了一些待執行的指令,此時發生一些停機、斷電等操作,則redis會按情況處理:

           記憶體模式:如果redis沒有采取任何持久化機制,那麼重新開機之後的資料庫總是空白的,是以資料總是一緻的。

            rdb模式:在執行事務時,redis不會中斷事務去執行儲存rdb的工作,隻有在事務執行之後,儲存rdb的工作才有可能開始。是以當rdb模式下的redis伺服器程序在事務中途被殺死時,事務内執行的指令,不管成功了多少,都不會被儲存到rdb檔案裡。恢複資料庫需要使用現有的rdb檔案,而這個rdb檔案的資料儲存的是最近一次的資料庫快照(snapshot),是以它的資料可能不是最新的,但隻要rdb檔案本身沒有因為其他問題而出錯,那麼還原後的資料庫就是一緻的。

             aof模式:因為儲存aof檔案的工作在背景線程進行,是以即使是在事務執行的中途,儲存aof檔案的工作也可以繼續進行,是以,根據事務語句是否被寫入并儲存到aof檔案,有以下兩種情況發生:

如果事務語句未寫入到aof檔案,或aof未被sync調用儲存到磁盤,那麼當程序被殺死之後,redis可以根據最近一次成功儲存到磁盤的aof檔案來還原資料庫,隻要aof檔案本身沒有因為其他問題而出錯,那麼還原後的資料庫總是一緻的,但其中的資料不一定是最新的。

如果事務的部分語句被寫入到aof檔案,并且aof檔案被成功儲存,那麼不完整的事務執行資訊就會遺留在aof檔案裡,當重新開機redis時,程式會檢測到aof檔案并不完整,redis會退出,并報告錯誤。需要使用redis-check-aof工具将部分成功的事務指令移除之後,才能再次啟動伺服器。還原之後的資料總是一緻的,而且資料也是最新的(直到事務執行之前為止)。 

處理錯誤

在操作事務時,我們經常會發生以下兩種錯誤:

1.在執行了multi指令,再往事務指令隊列中添加指令時,可能會出現一些錯誤。例如添加的指令的參數個數不對錯誤,甚至記憶體溢出等系統級錯誤;

2.在執行exec指令時,出現的一些錯誤。例如我們使用對sting類型值的指令,但實際上相應key上存儲的值是list。

對于第一種錯誤,用戶端會明顯地收到非“queued”回複;此時,用戶端最應該撤銷該事務;另外,在redis2.6.5以後,如果用戶端不做任何處理,服務端也記住了這種錯誤,并且在執行exec指令時,傳回錯誤資訊,并且不會執行事務指令隊列中的指令。

對于第二種錯誤,剩下的其他指令會繼續執行;并且在exec指令的傳回值中,我們可以看到響應的錯誤資訊。 

持久性(durability)補充

因為事務不過是用隊列包裹起了一組redis指令,并沒有提供任何額外的持久性功能,是以事務的持久性由redis所使用的持久化模式決定:

在單純的記憶體模式下,事務肯定是不持久的。

在rdb模式下,伺服器可能在事務執行之後、rdb檔案更新之前的這段時間失敗,是以rdb模式下的redis事務也是不持久的。

在aof的“總是sync”模式下,事務的每條指令在執行成功之後,都會立即調用fsync或fdatasync将事務資料寫入到aof檔案。但是,這種儲存是由背景線程進行的,主線程不會阻塞直到儲存成功,是以從指令執行成功到資料儲存到硬碟之間,還是有一段非常小的間隔,是以這種模式下的事務也是不持久的。

其他aof模式也和“總是sync”模式類似,是以它們都是不持久的。

5  訂閱與釋出

 redis通過publish、subscribe等指令實作了訂閱與釋出模式,這個功能提供兩種資訊機制,分别是訂閱/釋出到頻道和訂閱/釋出到模式。

 頻道,指具體要釋出或訂閱的消息的标示,例如news .sports;

 模式,指可以比對多個頻道的表達式,例如news.*比對頻道news.sports,news.education等。

 redis目前隻支援線上訂閱,不支援durable式訂閱。

5.1hello world

先看一個簡單訂閱者的例子:

redis> psubscribe news.* tweet.*

reading messages... (press ctrl-c to quit)

1) "psubscribe" # 傳回值的類型:顯示訂閱成功

2) "news.*" # 訂閱的模式

3) (integer) 1 # 目前已訂閱的模式的數量

1) "psubscribe"

2) "tweet.*"

3) (integer) 2

1) "pmessage" # 傳回值的類型:資訊

2) "news.*" # 資訊比對的模式

3) "news.it" # 資訊本身的目标頻道

4) "google buy motorola" # 資訊的内容

當然,在使用者a訂閱後,還要等使用者b發送消息。我們再看一個釋出者的例子:

redis> publish msg "good morning"

(integer) 1

釋出消息後,将傳回訂閱者的數量。

5.2redis的指令

 redis的訂閱釋出功能很簡單,我們這裡簡單地羅列一下各個指令的用法和說明,友善大家查閱。

psubscribe pattern [pattern ...]

 訂閱一個或多個模式或頻道。多個頻道或模式之間用空格隔開。

publish channel message

 将資訊message發送到指定的一個頻道channel。

pubsub <subcommand> [argument [argument ...]]

 pubsub 是一個檢視訂閱與釋出系統狀态的内省指令,它由數個不同格式的子指令組成。

pubsub channels [pattern]

 列出目前的活躍頻道。活躍頻道指的是那些至少有一個訂閱者的頻道,訂閱模式的用戶端不計算在内。

 pattern 參數是可選的:

 如果不給出pattern 參數,那麼列出訂閱與釋出系統中的所有活躍頻道。

 如果給出pattern 參數,那麼隻列出和給定模式pattern 相比對的那些活躍頻道。

pubsub numsub [channel-1 ... channel-n]

 傳回給定頻道的訂閱者數量,訂閱模式的用戶端不計算在内。

pubsub numpat

 傳回訂閱模式的數量。注意,這個指令傳回的不是訂閱模式的用戶端的數量,而是用戶端訂閱的所有模式的數量總和。

punsubscribe [pattern [pattern ...]]

 訓示用戶端退訂所有給定模式。如果沒有模式被指定,也即是,一個無參數的punsubscribe調用被執行,那麼用戶端使用psubscribe

 指令訂閱的所有模式都會被退訂。在這種情況下,指令會傳回一個資訊,告知用戶端所有被退訂的模式。

subscribe channel [channel ...]

 訂閱給定的一個或多個頻道的資訊。

unsubscribe [channel [channel ...]]

 訓示用戶端退訂給定的頻道。如果沒有頻道被指定,那麼用戶端使用subscribe指令訂閱的所有頻道都會被退訂。在這種情況下,指令會傳回一個資訊,告知用戶端所有被退訂的頻道。

5.3訂閱與釋出使用補充說明

在實際使用redis-cli測試訂閱與釋出過程中,發現有些問題:

作為訂閱者者(subscribe)的視窗,在訂閱了模式或頻道後,面臨一個尴尬的問題:不能退出(或者說,沒找到)訂閱收聽模式。如下圖所示:

也就是說,這個時候,我想取消訂閱(unsubscribe)時,沒有了輸入指令的地方。

 在jedis(redis的java用戶端)中,找到了有關訂閱與釋出的測試代碼redis.clients.jedis.tests.commands.publishsubscribecommandstest。其邏輯還是比較簡單,終于松了一口氣,看到了怎麼實際玩這個功能。 

6 redis的記憶體結構(database)

 redis資料庫是真正存儲資料的地方。當然,資料庫本身也是存儲在記憶體中的。

 databased的資料結構僞代碼如下:

typedef struct redisdb {

    // 儲存着資料庫以整數表示的号碼

    int id; 

    // 儲存着資料庫中的所有鍵值對資料

    // 這個屬性也被稱為鍵空間(key space)

    dict *dict; 

    // 儲存着鍵的過期資訊

    dict *expires; 

    // 實作清單阻塞原語,如 blpop

    // 在清單類型一章有詳細的讨論

    dict *blocking_keys;

    dict *ready_keys; 

    // 用于實作 watch 指令

    // 在事務章節有詳細的讨論

    dict *watched_keys;

} redisdb;

一個資料庫,在記憶體中的資料結構如下圖所示:

Redis使用文檔一

database的内容要點包括:

 1.資料庫主要由 dict 和 expires 兩個字典構成,其中 dict 儲存鍵值對,而 expires 則儲存鍵的過期時間。

 2.資料庫的鍵總是一個字元串對象,而值可以是任意一種 redis 資料類型,包括字元串、哈希、集合、清單和有序集。

 3.expires 的某個鍵和 dict 的某個鍵共同指向同一個字元串對象,而 expires 鍵的值則是該鍵以毫秒計算的 unix 過期時間戳。

 4.redis 使用惰性删除和定期删除兩種政策來删除過期的鍵。

   a.更新後的 rdb 檔案和重寫後的 aof 檔案都不會保留已經過期的鍵。

   b.當一個過期鍵被删除之後,程式會追加一條新的 del 指令到現有 aof 檔案末尾。

   c.當主節點删除一個過期鍵之後,它會顯式地發送一條 del 指令到所有附屬節點。

d.附屬節點即使發現過期鍵,也不會自作主張地删除它,而是等待主節點發來 del 指令,這樣可以保證主節點和附屬節點的資料總是一緻的。

資料庫的 dict 字典和 expires 字典的擴充政策和普通字典一樣。它們的收縮政策是:當節點的填充百分比不足 10% 時,将可用節點數量減少至大于等于目前已用節點數量。

7 叢集簡介

redis叢集可以實作在多個redis節點之間進行資料共享。

對于多個鍵的redis指令,不支援在叢集環境裡運作。(稍後說明為什麼多個鍵的不行)

redis叢集帶來的好處:

将資料自動切分到了多個節點上。

     當叢集中的一部分節點無法進行通訊時,仍然可以繼續處理指令請求。

7.1 叢集部署和驗證

redis叢集部署大緻分為以下步驟:

1.為每個redis節點修改生成redis.conf配置檔案:

port 7000                     //節點端口

cluster-enabled yes            //叢集開關

cluster-config-file nodes.conf    //指定節點id的存儲檔案

cluster-node-timeout 5000      //叢集中節點逾時時間

appendonly yes               //aof檔案讀寫模式

其中,

nodes.conf會在建立叢集的過程中自動生成,并存儲叢集環境中各個node的id;該id是redis執行個體的唯一标示,在執行個體的整個生命周期内有效;

cluster-node-timeout會在後邊一節提到其功能,主要用于節點間發現自己或别人有沒有已經“不在”了。

 2. 通過指令redis-server ./redis.conf啟動所有的節點執行個體;

 3. 通過一個ruby的redis-trib工具腳本建立叢集。

      a. 伺服器上需要ruby的運作環境;

      b. 工具的使用指令為:./redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005。指令解釋為:将最後的6個伺服器上的redis執行個體,建立成每個master有1個從節點的叢集環境。

      c. redis-trib會盡量地配置設定主從節點在不同的ip機器上。 

 4. 正常情況下,此時會得到一個redis-trib工具為我們配置設定好的叢集環境方案,如果我們認同,則輸入“yes”。

 5. 最後,出現如下日志資訊時,表示叢集環境搭建成功:

>>> check for open slots...

>>> check slots coverage...

[ok] all 16384 slots covered.

叢集環境的驗證:

使用redis自帶的用戶端工具redis-cli,依次輸入以下指令:

$ redis-cli -c -p 7000

redis 127.0.0.1:7000> set foo bar

-> redirected to slot [12182] located at 127.0.0.1:7002

redis 127.0.0.1:7002> set hello world

-> redirected to slot [866] located at 127.0.0.1:7000

redis 127.0.0.1:7000> get foo

-> redirected to 

redis-cli -c -p 7000指令表示:使用叢集環境,且連接配接7000端口的redis執行個體。

通過執行每個指令的提示資訊,我們可以看到叢集環境已經生效了。

7.2redis叢集内部原理

資料共享

新的redis版本,采用資料分片(sharding)而非一緻性哈希來實作:一個redis叢集包含16384個哈希槽(hash slot),資料庫中的每個鍵都屬于這16384個哈希槽的其中一個,叢集使用公式crc16(key)%16384來計算鍵key屬于哪個槽,其中crc16(key)語句用于計算鍵key的crc16校驗和。

叢集中的每個節點負責處理一部分哈希槽。舉個例子,一個叢集可以有三個哈希槽,其中:

     a.節點a負責處理0号至5500号哈希槽。

     b.節點b負責處理5501号至11000号哈希槽。

     c.節點c負責處理11001号至16384号哈希槽。

     這種将哈希槽分布到不同節點的做法使得使用者可以很容易地向叢集中添加或者删除節點。比如說:

     d.如果使用者将新節點d添加到叢集中,那麼叢集隻需要将節點a、b、c中的某些槽移動到節點d就可以了。

     e.與此類似,如果使用者要從叢集中移除節點a,那麼叢集隻需要将節點a中的所有哈希槽移動到節點b和節點c,然後再移除空白(不包含任何哈希槽)的節點a就可以了。

因為将一個哈希槽從一個節點移動到另一個節點不會造成節點阻塞,是以無論是添加新節點還是移除已存在節點,又或者改變某個節點包含的哈希槽數量,都不會造成叢集下線。 

主從複制

為了使得叢集在一部分節點下線或者無法與叢集的大多數節點進行通訊的情況下,仍然可以正常運作,redis叢集對節點使用了主從複制功能:叢集中的每個節點都有1個至n個複制品(replica),其中一個複制品為主節點(master),而其餘的n-1個複制品為從節點(slave)。

     在之前列舉的節點a、b、c的例子中,如果節點b下線了,那麼叢集将無法正常運作,因為叢集找不到節點來處理5501号至11000号的哈希槽。

     另一方面,假如在建立叢集的時候(或者至少在節點b下線之前),我們為主節點b添加了從節點b1,那麼當主節點b下線的時候,叢集就會将b1設定為新的主節點,并讓它代替下線的主節點b,繼續處理5501号至11000号的哈希槽,這樣叢集就不會因為主節點b的下線而無法正常運作了。

不過如果節點b和b1都下線的話,redis叢集還是會停止運作。 

一緻性保證

redis叢集不保證資料的強一緻性(strong consistency):在特定條件下,redis叢集可能會丢失已經被執行過的寫指令。

使用異步複制(asynchronous replication)是redis叢集可能會丢失寫指令的其中一個原因。考慮以下這個寫指令的例子:

     a.用戶端向主節點b發送一條寫指令。

     b.主節點b執行寫指令,并向用戶端傳回指令回複。

     c.主節點b将剛剛執行的寫指令複制給它的從節點b1、b2和b3。

     note:如果真的有必要的話,redis叢集可能會在将來提供同步地(synchronou)執行寫指令的方法。

     redis叢集另外一種可能會丢失指令的情況是:叢集出現網絡分裂(network partition),并且一個用戶端與至少包括一個主節點在内的少數(minority)執行個體被孤立。

     舉個例子,假設叢集包含a、b、c、a1、b1、c1六個節點,其中a、b、c為主節點,而a1、b1、c1分别為三個主節點的從節點,另外還有一個用戶端z1。

     假設叢集中發生網絡分裂,那麼叢集可能會分裂為兩方,大多數(majority)的一方包含節點a、c、a1、b1和c1,而少數(minority)的一方則包含節點b和用戶端z1。

     在網絡分裂期間,主節點b仍然會接受z1發送的寫指令:

     a.如果網絡分裂出現的時間很短,那麼叢集會繼續正常運作;

     b.但是,如果網絡分裂出現的時間足夠長,使得大多數一方将從節點b1設定為新的主節點,并使用b1來代替原來的主節點b,那麼z1發送給主節點b的寫指令将丢失。

     注意,在網絡分裂出現期間,用戶端z1可以向主節點b發送寫指令的最大時間是有限制的,這一時間限制稱為節點逾時時間(nodetimeout),是redis叢集的一個重要的配置選項:

     c.對于大多數一方來說,如果一個主節點未能在節點逾時時間所設定的時限内重新聯系上叢集,那麼叢集會将這個主節點視為下線,并使用從節點來代替這個主節點繼續工作。 

     d.對于少數一方,如果一個主節點未能在節點逾時時間所設定的時限内重新聯系上叢集,那麼它将停止處理寫指令,并向用戶端報告錯誤。

Redis使用文檔一

大小: 12.5 kb

Redis使用文檔一

大小: 15.3 kb

Redis使用文檔一

大小: 46.2 kb