1. 程式人生 > >Redis使用文檔一

Redis使用文檔一

typedef 部署 mark open 讀下 內存 type 數組 cover

1 Redis概述

1.1前言

Redis是一個開源、支持網絡、基於內存亦可持久化的日誌型、鍵值對存儲數據庫。使用ANSI C編寫。並提供多種語言的API。

其開發由VMware主持,是最流行的鍵值對存儲數據庫之中的一個。

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

1.2安裝和驗證

在Redis的官網上。我們能夠方便地下載Redis的各種版本號,其官網下載地址為:http://redis.io/download

我們下載了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:命令方式、client方式。

3.1 Redis命令

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

3.2 Java client

Jedis是Redis首推的javaclient開發包。

該項目在git的源碼在:https://github.com/xetorthio/jedis。

Jedis主要功能是對redis的全部命令操作進行封裝。供java開發者調用。Jedis處理我們的每一個命令調用步驟例如以下:

a. 依據提供的ip、port、password連接redisserver。並持有連接。

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協議內容發送到redisserver運行。

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的project代碼比較簡單,並且有非常多的範例和測試代碼,在此僅僅是簡單地說明一下project的包結構以及查找案例的方法。

redis.clients.jedis.tests.benchmark

正如包名一樣,此包以下的代碼為client的演示樣例代碼。假設剛接觸jedis和redis,則能夠直接改動當中的ip和port等,體驗一下redis。

redis.clients.jedis.tests.commands

該包以下是對jedis的全部commond的單元測試案例。其測試的代碼比較簡潔,假設大家對jedis的某個命令使用不太明白,在此處搜索其用法。應該是一個不錯的選擇。

很多其它關於jedis的細節。我們能夠直接看Jedis的源代碼。

3.3 和Redis通信

接下來,我們看看假設哪天我們認為jedis不好用了,我們想自己寫一套client。我們應該怎麽來和redis通信交互。

首先,redis和外部通信。僅僅支持tcp協議,port默覺得6379。

其次,假設想要redis能解析你發給它的命令和參數,我們的命令和參數必須符合redis協議。

另外,redis回復給我們的響應信息。也是依照redis協議來組裝。接下來,我們具體看看redis協議是怎麽回事。

3.3.1 Redis協議

在這個協議中。全部發送至Redis server的參數都是二進制安全(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

<argument data> CR LF

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

a. CR表示\r,LF表示\n;每一個Redis命令或者client和server之間傳輸的數據都以\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 21days"

QUEUED

redis> GET book-name

QUEUED

redis> SADD tag "C++""Programming" "Mastering Series"

QUEUED

redis> SMEMBERS tag

QUEUED

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命令用於開始一個事務。

該命令的效果,即是將服務端上記錄的client狀態改為事務狀態。

該命令返回“OK”。

4.3 加入要運行的命令

Redis服務端上的client狀態改動為事務狀態後。一個明顯的差別就是:全部發送給服務端運行的命令(除了用於事務處理的四個命令,MULTI、DISCARD、EXEC和WATCH),不是立即運行以及返回運行結果。取而代之的是。這些命令會被redis放到一個命令隊列中,並返回client“QUEUED”。

Redis上的事務命令隊列是一個數組,每一個數組項包括三個屬性:

1. 要運行的命令(cmd)。

2. 命令的參數(argv);

3. 參數的個數(argc)。

4.4 運行事務EXEC

假設client正處於事務狀態,當client再發送EXEC命令到服務端時,Redis服務端的運行步驟例如以下:

1. 服務端會從client所保存事務命令隊列中。以FIFO的順序取出命令運行,而且把每條命令運行的結果保存到一個FIFO的回復隊列中;

2.當事務命令隊列中的所有命令都被運行完了以後。EXEC命令會將回復隊列中的結果所有返回給client。

4.5 取消事務

DISCARD 命令用於取消一個事務,它清空client的整個事務隊列,然後將client從事務狀態調整回非事務狀態,最後返回字符串OK給client,說明事務已被取消。

之前已經提到過。在client輸入了EXEC命令開啟一個事務後。和事務相關的MULTI,EXEC,DISCARD和WATCH四個命令不會被放到client的事務命令隊列中。

那麽假設在client已經開啟一個事務的情況下。client輸入上述四個命令,redis會怎麽處理呢?我們下邊來看看。

MULTI

Redis 的事務是不可嵌套的, 當client已經處於事務狀態。 而client又再向server發送 MULTI 時, server僅僅是簡單地向client發送一個錯誤, 然後繼續等待其它命令的入隊。 MULTI 命令的發送不會造成整個事務失敗。 也不會改動事務隊列中已有的數據。

WATCH

僅僅能在client進入事務狀態之前運行。 在事務狀態下發送 WATCH 命令會引發一個錯誤, 但它不會造成整個事務失敗。 也不會改動事務隊列中已有的數據(和前面處理 MULTI 的情況一樣)。

當然,最後,EXEC會運行事務隊列中的所有命令;DISCARD會清空事務隊列中的命令,並改動client狀態。

4.6帶WATCH的事務

WATCH命令用於在事務開始之前監視隨意數量的鍵:當調用EXEC命令運行事務時,假設隨意一個被監視的鍵已經被其它client改動了,那麽整個事務不再運行,直接返回失敗。

下面演示樣例展示了一個運行失敗的事務樣例:

redis> WATCH name

OK

redis> MULTI

OK

redis> SET name peter

QUEUED

redis> EXEC

(nil)

註意最後EXEC命令返回的不再是SET命令運行的結果。

4.7 WATCH命令的實現和觸發

在每一個代表數據庫的redis.h/redisDb 結構類型中,都保存了一個 watched_keys 字典。字典的鍵是這個數據庫被監視的鍵,而字典的值則是一個鏈表。鏈表中保存了全部監視這個鍵的client。

比方說,下面字典就展示了一個 watched_keys 字典的樣例:

技術分享



當中。鍵 key1 正在被 client2 、 client5 和 client1 三個客戶端監視, 其它一些鍵也分別被其它別的客戶端監視著。

WATCH 命令的作用, 就是將當前client和要監視的鍵在 watched_keys 中進行關聯。

舉個樣例。假設當前client為client10086,那麽當client運行WATCH key1 key2時。前面展示的watched_keys 將被改動成這個樣子:


技術分享

通過watched_keys字典,假設程序想檢查某個鍵是否被監視,那麽它僅僅要檢查字典中是否存在這個鍵就可以。假設程序要獲取監視某個鍵的全部client。那麽僅僅要取出鍵的值(一個鏈表),然後對鏈表進行遍歷就可以。

在不論什麽對數據庫鍵空間(keyspace)進行改動的命令成功運行之後(比方FLUSHDB、SET、DEL、LPUSH、SADD、ZREM。諸如此類),multi.c/touchWatchedKey函數都會被調用——它檢查數據庫的watched_keys字典。看是否有client在監視已經被命令改動的鍵,假設有的話。程序將全部監視這個/這些被改動鍵的client的REDIS_DIRTY_CAS選項打開。

當client發送EXEC命令、觸發事務運行時,server會對client的狀態進行檢查:

假設client的REDIS_DIRTY_CAS選項已經被打開,那麽說明被client監視的鍵至少有一個已經被改動了,事務的安全性已經被破壞。server會放棄運行這個事務,直接向client返回空回復,表示事務運行失敗。

假設REDIS_DIRTY_CAS選項沒有被打開。那麽說明全部監視鍵都安全。server正式運行事務。

4.8 事務狀態和非事務狀態下運行命令

不管在事務狀態下, 還是在非事務狀態下。 Redis 命令都由同一個函數運行, 所以它們共享非常多server的一般設置, 比方 AOF 的配置、RDB 的配置,以及內存限制,等等。

只是事務中的命令和普通命令在運行上還是有一點差別的,當中最重要的兩點是:

1.非事務狀態下的命令以單個命令為單位運行,前一個命令和後一個命令的client不一定是同一個;而事務狀態則是以一個事務為單位,運行事務隊列中的全部命令:除非當前事務運行完成。否則server不會中斷事務。也不會運行其它client的其它命令。

2.在非事務狀態下,運行命令所得的結果會馬上被返回給client;而事務則是將全部命令的結果集合到回復隊列,再作為 EXEC 命令的結果返回給client。

4.9 很多其它的Redis事務說明

官方說明

依據Redis官方文檔,redis事務有下面兩個重要的保證:

1. 全部的事務命令隊列中的命令會被順序地運行;而且,在一個client的事務正在運行時,其它的client的請求都將不會運行。

2. 無論事務命令隊列中是否有命令。Redis都是具有原子性的。

也就是說。EXEC命令都將運行隊列中的全部命令。

基於此,發生下面錯誤時,redis的處理例如以下:

a.假設在MULTI命令運行之前。client的連接在斷開了。則什麽也不會運行。

b. 假設EXEC命令已經調用,而client的連接斷開了,則全部的命令都將運行;

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

內存模式:假設Redis沒有採取不論什麽持久化機制。那麽重新啟動之後的數據庫總是空白的,所以數據總是一致的。

RDB模式:在運行事務時,Redis不會中斷事務去運行保存RDB的工作,僅僅有在事務運行之後,保存RDB的工作才有可能開始。

所以當RDB模式下的Redisserver進程在事務中途被殺死時,事務內運行的命令,無論成功了多少,都不會被保存到RDB文件中。

恢復數據庫須要使用現有的RDB文件。而這個RDB文件的數據保存的是近期一次的數據庫快照(snapshot)。所以它的數據可能不是最新的,但僅僅要RDB文件本身沒有由於其它問題而出錯,那麽還原後的數據庫就是一致的。

AOF模式:由於保存AOF文件的工作在後臺線程進行,所以即使是在事務運行的中途。保存AOF文件的工作也能夠繼續進行,因此,依據事務語句是否被寫入並保存到AOF文件,有下面兩種情況發生:

假設事務語句未寫入到AOF文件,或AOF未被SYNC調用保存到磁盤,那麽當進程被殺死之後,Redis能夠依據近期一次成功保存到磁盤的AOF文件來還原數據庫,僅僅要AOF文件本身沒有由於其它問題而出錯,那麽還原後的數據庫總是一致的,但當中的數據不一定是最新的。

假設事務的部分語句被寫入到AOF文件,並且AOF文件被成功保存,那麽不完整的事務運行信息就會遺留在AOF文件中。當重新啟動Redis時。程序會檢測到AOF文件並不完整。Redis會退出,並報告錯誤。須要使用redis-check-aof工具將部分成功的事務命令移除之後。才幹再次啟動server。還原之後的數據總是一致的。並且數據也是最新的(直到事務運行之前為止)。

處理錯誤

在操作事務時。我們常常會發生下面兩種錯誤:

1.在運行了MULTI命令,再往事務命令隊列中加入命令時,可能會出現一些錯誤。比如加入的命令的參數個數不正確錯誤,甚至內存溢出等系統級錯誤;

2.在運行EXEC命令時,出現的一些錯誤。比如我們使用對Sting類型值的命令。但實際上對應key上存儲的值是List。

對於第一種錯誤,client會明顯地收到非“QUEUED”回復。此時,client最應該撤銷該事務。另外,在redis2.6.5以後,假設client不做不論什麽處理,服務端也記住了這樣的錯誤。而且在運行EXEC命令時,返回錯誤信息。而且不會運行事務命令隊列中的命令。

對於另外一種錯誤,剩下的其它命令會繼續運行;而且在EXEC命令的返回值中,我們能夠看到響應的錯誤信息。

持久性(Durability)補充

由於事務只是是用隊列包裹起了一組Redis命令,並沒有提供不論什麽額外的持久性功能。所以事務的持久性由Redis所使用的持久化模式決定:

在單純的內存模式下,事務肯定是不持久的。

在RDB模式下,server可能在事務運行之後、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的訂閱公布功能非常easy,我們這裏簡單地羅列一下各個命令的使用方法和說明,方便大家查閱。

PSUBSCRIBE pattern [pattern ...]

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

PUBLISH channel message

將信息message發送到指定的一個頻道channel。

PUBSUB <subcommand> [argument [argument ...]]

PUBSUB 是一個查看訂閱與公布系統狀態的內省命令。它由數個不同格式的子命令組成。

PUBSUB CHANNELS [pattern]

列出當前的活躍頻道。活躍頻道指的是那些至少有一個訂閱者的頻道。訂閱模式的client不計算在內。

pattern 參數是可選的:

假設不給出pattern 參數,那麽列出訂閱與公布系統中的全部活躍頻道。

假設給出pattern 參數,那麽僅僅列出和給定模式pattern 相匹配的那些活躍頻道。

PUBSUB NUMSUB [channel-1 ... channel-N]

返回給定頻道的訂閱者數量,訂閱模式的client不計算在內。

PUBSUB NUMPAT

返回訂閱模式的數量。註意,這個命令返回的不是訂閱模式的client的數量。而是client訂閱的全部模式的數量總和。

PUNSUBSCRIBE [pattern [pattern ...]]

指示client退訂全部給定模式。假設沒有模式被指定,也即是,一個無參數的PUNSUBSCRIBE調用被運行,那麽client使用PSUBSCRIBE

命令訂閱的全部模式都會被退訂。在這樣的情況下,命令會返回一個信息,告知client全部被退訂的模式。

SUBSCRIBE channel [channel ...]

訂閱給定的一個或多個頻道的信息。

UNSUBSCRIBE [channel [channel ...]]

指示client退訂給定的頻道。假設沒有頻道被指定,那麽client使用SUBSCRIBE命令訂閱的全部頻道都會被退訂。在這樣的情況下。命令會返回一個信息,告知client全部被退訂的頻道。

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;

一個數據庫,在內存中的數據結構例如以下圖所看到的:


技術分享

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配置文件:

port7000 //節點端口

cluster-enabledyes //集群開關

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

cluster-node-timeout 5000 //集群中節點超時時間

appendonlyyes //aof文件讀寫模式

當中。

nodes.conf會在創建集群的過程中自己主動生成,並存儲集群環境中各個node的id;該id是redis實例的唯一標示。在實例的整個生命周期內有效;

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

2. 通過命令redis-server./redis.conf啟動全部的節點實例;

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

a. server上須要ruby的執行環境;

b. 工具的使用命令為:./redis-trib.rbcreate --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003127.0.0.1:7004 127.0.0.1:7005。命令解釋為:將最後的6個server上的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自帶的client工具redis-cli。依次輸入下面命令:

$ redis-cli -c -p 7000

redis 127.0.0.1:7000> set foo bar

-> Redirected to slot [12182] located at127.0.0.1:7002

OK

redis 127.0.0.1:7002> set hello world

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

OK

redis 127.0.0.1:7000> get foo

-> Redirected to

redis-cli -c -p 7000命令表示:使用集群環境。且連接7000port的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號哈希槽。

這樣的將哈希槽分布到不同節點的做法使得用戶能夠非常easy地向集群中加入或者刪除節點。比方說:

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.client向主節點B發送一條寫命令。

b.主節點B運行寫命令,並向client返回命令回復。

c.主節點B將剛剛運行的寫命令復制給它的從節點B1、B2和B3。

Note:假設真的有必要的話,Redis集群可能會在將來提供同步地(synchronou)運行寫命令的方法。

Redis集群第二種可能會丟失命令的情況是:集群出現網絡分裂(network partition)。而且一個client與至少包含一個主節點在內的少數(minority)實例被孤立。

舉個樣例,如果集群包括A、B、C、A1、B1、C1六個節點,當中A、B、C為主節點,而A1、B1、C1分別為三個主節點的從節點。另外另一個clientZ1。

如果集群中發生網絡分裂,那麽集群可能會分裂為雙方,大多數(majority)的一方包括節點A、C、A1、B1和C1。而少數(minority)的一方則包括節點B和clientZ1。

在網絡分裂期間。主節點B仍然會接受Z1發送的寫命令:

a.假設網絡分裂出現的時間非常短,那麽集群會繼續正常執行;

b.可是,假設網絡分裂出現的時間足夠長,使得大多數一方將從節點B1設置為新的主節點,並使用B1來取代原來的主節點B,那麽Z1發送給主節點B的寫命令將丟失。

註意,在網絡分裂出現期間,clientZ1能夠向主節點B發送寫命令的最大時間是有限制的。這一時間限制稱為節點超時時間(nodetimeout),是Redis集群的一個重要的配置選項:

c.對於大多數一方來說,假設一個主節點未能在節點超時時間所設定的時限內又一次聯系上集群。那麽集群會將這個主節點視為下線,並使用從節點來取代這個主節點繼續工作。

d.對於少數一方。假設一個主節點未能在節點超時時間所設定的時限內又一次聯系上集群,那麽它將停止處理寫命令,並向client報告錯誤。

Redis使用文檔一