Redis 核心概念

發表於2017-07-26

概述

Redis 與其他 key – value 快取產品有以下三個特點:

  1. Redis支援資料的持久化,可以將記憶體中的資料保持在磁碟中,重啟的時候可以再次載入進行使用。
  2. Redis不僅僅支援簡單的key-value型別的資料,同時還提供list,set,zset,hash等資料結構的儲存。
  3. Redis支援資料的備份,即master-slave模式的資料備份。
概念 說明
Redis 優勢 1. 效能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。

2. 豐富的資料型別 – Redis支援二進位制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 資料型別操作。

3. 原子 – Redis的所有操作都是原子性的,同時Redis還支援對幾個操作全並後的原子性執行。

4. 豐富的特性 – Redis還支援 publish/subscribe, 通知, key 過期等等特性。

Redis與其他key-value儲存有什麼不同? 1. Redis有著更為複雜的資料結構並且提供對他們的原子性操作,這是一個不同於其他資料庫的進化路徑。Redis的資料型別都是基於基本資料結構的同時對程式設計師透明,無需進行額外的抽象。

2. Redis執行在記憶體中但是可以持久化到磁碟,所以在對不同資料集進行高速讀寫時需要權衡記憶體,應為資料量不能大於硬體記憶體。在記憶體資料庫方面的另一個優點是, 相比在磁碟上相同的複雜的資料結構,在記憶體中操作起來非常簡單,這樣Redis可以做很多內部複雜性很強的事情。 同時,在磁碟格式方面他們是緊湊的以追加的方式產生的,因為他們並不需要進行隨機訪問。

資料型別

概念 說明
String(字串) string是redis最基本的型別,你可以理解成與Memcached一模一樣的型別,一個key對應一個value。

string型別是二進位制安全的。意思是redis的string可以包含任何資料。比如jpg圖片或者序列化的物件 。

string型別是Redis最基本的資料型別,一個鍵最大能儲存512MB。

Hash(雜湊) Redis hash 是一個鍵值對集合。

Redis hash是一個string型別的field和value的對映表,hash特別適合用於儲存物件。

每個 hash 可以儲存 2 *32 – 1鍵值對(40多億)。

List(列表) Redis 列表是簡單的字串列表,按照插入順序排序。你可以新增一個元素導列表的頭部(左邊)或者尾部(右邊)。

列表最多可儲存 2*32 – 1元素 (4294967295, 每個列表可儲存40多億)。

Set(集合) Redis的Set是string型別的無序集合。

集合是通過雜湊表實現的,所以新增,刪除,查詢的複雜度都是O(1)。

集合中最大的成員數為 2 32 – 1(4294967295, 每個集合可儲存40多億個成員)。
*

zset(sorted set:有序集合) Redis zset 和 set 一樣也是string型別元素的集合,且不允許重複的成員。

不同的是每個元素都會關聯一個double型別的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。

zset的成員是唯一的,但分數(score)卻可以重複。

基本概念

概念 說明
HyperLogLog Redis 在 2.8.9 版本新增了 HyperLogLog 結構。

Redis HyperLogLog 是用來做基數統計的演算法,HyperLogLog 的優點是,在輸入元素的數量或者體積非常非常大時,計算基數所需的空間總是固定 的、並且是很小的。

在 Redis 裡面,每個 HyperLogLog 鍵只需要花費 12 KB 記憶體,就可以計算接近 2^64 個不同元素的基 數。這和計算基數時,元素越多耗費記憶體就越多的集合形成鮮明對比。

但是,因為 HyperLogLog 只會根據輸入元素來計算基數,而不會儲存輸入元素本身,所以 HyperLogLog 不能像集合那樣,返回輸入的各個元素。

什麼是基數?

比如資料集 {1, 3, 5, 7, 5, 7, 8}, 那麼這個資料集的基數集為 {1, 3, 5 ,7, 8}, 基數(不重複元素)為5。 基數估計就是在誤差可接受的範圍內,快速計算基數。

釋出訂閱 Redis 釋出訂閱(pub/sub)是一種訊息通訊模式:傳送者(pub)傳送訊息,訂閱者(sub)接收訊息。

Redis 客戶端可以訂閱任意數量的頻道。

下圖展示了頻道 channel1 , 以及訂閱這個頻道的三個客戶端 —— client2 、 client5 和 client1 之間的關係:

Redis 核心概念

pubsub1

當有新訊息通過 PUBLISH 命令傳送給頻道 channel1 時, 這個訊息就會被髮送給訂閱它的三個客戶端:

Redis 核心概念

pubsub2
Redis 事務 Redis 事務可以一次執行多個命令, 並且帶有以下兩個重要的保證:

1. 事務是一個單獨的隔離操作:事務中的所有命令都會序列化、按順序地執行。事務在執行的過程中,不會被其他客戶端傳送來的命令請求所打斷。

2. 事務是一個原子操作:事務中的命令要麼全部被執行,要麼全部都不執行。

一個事務從開始到執行會經歷以下三個階段:

a. 開始事務。

b. 命令入隊。

c. 執行事務。

複製(Replication)

Redis 支援簡單且易用的主從複製(master-slave replication)功能, 該功能可以讓從伺服器(slave server)成為主伺服器(master server)的精確複製品。

以下是關於 Redis 複製功能的幾個重要方面:

  1. Redis 使用非同步複製。 從 Redis 2.8 開始, 從伺服器會以每秒一次的頻率向主伺服器報告複製流(replication stream)的處理進度。
  2. 一個主伺服器可以有多個從伺服器。
  3. 不僅主伺服器可以有從伺服器, 從伺服器也可以有自己的從伺服器, 多個從伺服器之間可以構成一個圖狀結構。
  4. 複製功能不會阻塞主伺服器: 即使有一個或多個從伺服器正在進行初次同步, 主伺服器也可以繼續處理命令請求。
  5. 複製功能也不會阻塞從伺服器: 只要在 redis.conf 檔案中進行了相應的設定, 即使從伺服器正在進行初次同步, 伺服器也可以使用舊版本的資料集來處理命令查詢。不過, 在從伺服器刪除舊版本資料集並載入新版本資料集的那段時間內, 連線請求會被阻塞。
    你還可以配置從伺服器, 讓它在與主伺服器之間的連線斷開時, 向客戶端傳送一個錯誤。
  6. 複製功能可以單純地用於資料冗餘(data redundancy), 也可以通過讓多個從伺服器處理只讀命令請求來提升擴充套件性(scalability): 比如說, 繁重的 SORT 命令可以交給附屬節點去執行。
  7. 可以通過複製功能來讓主伺服器免於執行持久化操作: 只要關閉主伺服器的持久化功能, 然後由從伺服器去執行持久化操作即可。
概念 說明
複製功能的運作原理 無論是初次連線還是重新連線, 當建立一個從伺服器時, 從伺服器都將向主伺服器傳送一個 SYNC 命令。

接到 SYNC 命令的主伺服器將開始執行 BGSAVE , 並在儲存操作執行期間, 將所有新執行的寫入命令都儲存到一個緩衝區裡面。

BGSAVE 執行完畢後, 主伺服器將執行儲存操作所得的 .rdb 檔案傳送給從伺服器, 從伺服器接收這個 .rdb 檔案, 並將檔案中的資料載入到記憶體中。

之後主伺服器會以 Redis 命令協議的格式, 將寫命令緩衝區中積累的所有內容都傳送給從伺服器。

你可以通過 telnet 命令來親自驗證這個同步過程: 首先連上一個正在處理命令請求的 Redis 伺服器, 然後向它傳送 SYNC 命令, 過一陣子, 你將看到 telnet 會話(session)接收到伺服器發來的大段資料(.rdb 檔案), 之後還會看到, 所有在伺服器執行過的寫命令, 都會重新傳送到 telnet 會話來。

即使有多個從伺服器同時向主伺服器傳送 SYNC , 主伺服器也只需執行一次 BGSAVE 命令, 就可以處理所有這些從伺服器的同步請求。

從伺服器可以在主從伺服器之間的連線斷開時進行自動重連, 在 Redis 2.8 版本之前, 斷線之後重連的從伺服器總要執行一次完整重同步(full resynchronization)操作, 但是從 Redis 2.8 版本開始, 從伺服器可以根據主伺服器的情況來選擇執行完整重同步還是部分重同步(partial resynchronization)。

部分重同步 從 Redis 2.8 開始, 在網路連線短暫性失效之後, 主從伺服器可以嘗試繼續執行原有的複製程式(process), 而不一定要執行完整重同步操作。

這個特性需要主伺服器為被髮送的複製流建立一個記憶體緩衝區(in-memory backlog), 並且主伺服器和所有從伺服器之間都記錄一個複製偏移量(replication offset)和一個主伺服器 ID (master run id), 當出現網路連線斷開時, 從伺服器會重新連線, 並且向主伺服器請求繼續執行原來的複製程式:

1. 如果從伺服器記錄的主伺服器 ID 和當前要連線的主伺服器的 ID 相同, 並且從伺服器記錄的偏移量所指定的資料仍然儲存在主伺服器的複製流緩衝區裡面, 那麼主伺服器會向從伺服器傳送斷線時缺失的那部分資料, 然後複製工作可以繼續執行。

2. 否則的話, 從伺服器就要執行完整重同步操作。

Redis 2.8 的這個部分重同步特性會用到一個新增的 PSYNC 內部命令, 而 Redis 2.8 以前的舊版本只有 SYNC 命令, 不過, 只要從伺服器是 Redis 2.8 或以上的版本, 它就會根據主伺服器的版本來決定到底是使用 PSYNC 還是 SYNC

1. 如果主伺服器是 Redis 2.8 或以上版本,那麼從伺服器使用 PSYNC 命令來進行同步。

2. 如果主伺服器是 Redis 2.8 之前的版本,那麼從伺服器使用 SYNC 命令來進行同步。

只讀從伺服器 從 Redis 2.6 開始, 從伺服器支援只讀模式, 並且該模式為從伺服器的預設模式。

只讀模式由 redis.conf 檔案中的 slave-read-only 選項控制, 也可以通過 CONFIG SET 命令來開啟或關閉這個模式。

只讀從伺服器會拒絕執行任何寫命令, 所以不會出現因為操作失誤而將資料不小心寫入到了從伺服器的情況。

即使從伺服器是隻讀的, DEBUG 和 CONFIG 等管理式命令仍然是可以使用的, 所以我們還是不應該將伺服器暴露給網際網路或者任何不可信網路。 不過, 使用 redis.conf 中的命令改名選項, 我們可以通過禁止執行某些命令來提升只讀從伺服器的安全性。

你可能會感到好奇, 既然從伺服器上的寫資料會被重同步資料覆蓋, 也可能在從伺服器重啟時丟失, 那麼為什麼要讓一個從伺服器變得可寫呢?

原因是, 一些不重要的臨時資料, 仍然是可以儲存在從伺服器上面的。 比如說, 客戶端可以在從伺服器上儲存主伺服器的可達性(reachability)資訊, 從而實現故障轉移(failover)策略。

主伺服器只在有至少 N 個從伺服器的情況下,才執行寫操作 從 Redis 2.8 開始, 為了保證資料的安全性, 可以通過配置, 讓主伺服器只在有至少 N 個當前已連線從伺服器的情況下, 才執行寫命令。

不過, 因為 Redis 使用非同步複製, 所以主伺服器傳送的寫資料並不一定會被從伺服器接收到, 因此, 資料丟失的可能性仍然是存在的。

以下是這個特性的運作原理:

1 從伺服器以每秒一次的頻率 PING 主伺服器一次, 並報告複製流的處理情況。

2 主伺服器會記錄各個從伺服器最後一次向它傳送 PING 的時間。

3 使用者可以通過配置, 指定網路延遲的最大值 , 以及執行寫操作所需的至少從伺服器數量如果至少有 min-slaves-to-write 個從伺服器, 並且這些伺服器的延遲值都少於 min-slaves-max-lag 秒, 那麼主伺服器就會執行客戶端請求的寫操作。

你可以將這個特性看作 CAP 理論中的 C 的條件放寬版本: 儘管不能保證寫操作的永續性, 但起碼丟失資料的視窗會被嚴格限制在指定的秒數中。

另一方面, 如果條件達不到min-slaves-to-write 和 min-slaves-max-lag 所指定的條件, 那麼寫操作就不會被執行, 主伺服器會向請求執行寫操作的客戶端返回一個錯誤。

以下是這個特性的兩個選項和它們所需的引數:

1 min-slaves-to-write

2 min-slaves-max-lag

詳細的資訊可以參考 Redis 原始碼中附帶的 redis.conf 示例檔案。

事務(transaction)

MULTIEXECDISCARDWATCH 是 Redis 事務的基礎。
事務可以一次執行多個命令, 並且帶有以下兩個重要的保證:

  1. 事務是一個單獨的隔離操作:事務中的所有命令都會序列化、按順序地執行。事務在執行的過程中,不會被其他客戶端傳送來的命令請求所打斷。
  2. 事務是一個原子操作:事務中的命令要麼全部被執行,要麼全部都不執行。
    EXEC 命令負責觸發並執行事務中的所有命令:
    • 如果客戶端在使用 MULTI 開啟了一個事務之後,卻因為斷線而沒有成功執行 EXEC ,那麼事務中的所有命令都不會被執行。
    • 另一方面,如果客戶端成功在開啟事務之後執行 EXEC ,那麼事務中的所有命令都會被執行。
      當使用 AOF 方式做持久化的時候, Redis 會使用單個 write(2) 命令將事務寫入到磁碟中。

然而,如果 Redis 伺服器因為某些原因被管理員殺死,或者遇上某種硬體故障,那麼可能只有部分事務命令會被成功寫入到磁碟中。
如果 Redis 在重新啟動時發現 AOF 檔案出了這樣的問題,那麼它會退出,並彙報一個錯誤。
使用 redis-check-aof 程式可以修復這一問題:它會移除 AOF 檔案中不完整事務的資訊,確保伺服器可以順利啟動。
從 2.2 版本開始,Redis 還可以通過樂觀鎖(optimistic lock)實現 CAS (check-and-set)操作,具體資訊請參考文件的後半部分。

概念 說明
事務中的錯誤 使用事務時可能會遇上以下兩種錯誤:

1. 事務在執行 EXEC 之前,入隊的命令可能會出錯。比如說,命令可能會產生語法錯誤(引數數量錯誤,引數名錯誤,等等),或者其他更嚴重的錯誤,比如記憶體不足(如果伺服器使用 maxmemory 設定了最大記憶體限制的話)。

2. 命令可能在 EXEC 呼叫之後失敗。舉個例子,事務中的命令可能處理了錯誤型別的鍵,比如將列表命令用在了字串鍵上面,諸如此類。

對於發生在 EXEC 執行之前的錯誤,客戶端以前的做法是檢查命令入隊所得的返回值:如果命令入隊時返回 QUEUED ,那麼入隊成功;否則,就是入隊失敗。如果有命令在入隊時失敗,那麼大部分客戶端都會停止並取消這個事務。

不過,從 Redis 2.6.5 開始,伺服器會對命令入隊失敗的情況進行記錄,並在客戶端呼叫 EXEC 命令時,拒絕執行並自動放棄這個事務。

在 Redis 2.6.5 以前, Redis 只執行事務中那些入隊成功的命令,而忽略那些入隊失敗的命令。 而新的處理方式則使得在流水線(pipeline)中包含事務變得簡單,因為傳送事務和讀取事務的回覆都只需要和伺服器進行一次通訊。

至於那些在 EXEC 命令執行之後所產生的錯誤, 並沒有對它們進行特別處理: 即使事務中有某個/某些命令在執行時產生了錯誤, 事務中的其他命令仍然會繼續執行。

最重要的是記住這樣一條, 即使事務中有某條/某些命令執行失敗了, 事務佇列中的其他命令仍然會繼續執行 —— Redis 不會停止執行事務中的命令。

Redis 不支援回滾 如果你有使用關係式資料庫的經驗, 那麼 “Redis 在事務失敗時不進行回滾,而是繼續執行餘下的命令”這種做法可能會讓你覺得有點奇怪。

以下是這種做法的優點:

1. Redis 命令只會因為錯誤的語法而失敗(並且這些問題不能在入隊時發現),或是命令用在了錯誤型別的鍵上面:這也就是說,從實用性的角度來說,失敗的命令是由程式設計錯誤造成的,而這些錯誤應該在開發的過程中被發現,而不應該出現在生產環境中。

2. 因為不需要對回滾進行支援,所以 Redis 的內部可以保持簡單且快速。

有種觀點認為 Redis 處理事務的做法會產生 bug , 然而需要注意的是, 在通常情況下, 回滾並不能解決程式設計錯誤帶來的問題。 舉個例子, 如果你本來想通過 INCR 命令將鍵的值加上 1 , 卻不小心加上了 2 , 又或者對錯誤型別的鍵執行了 INCR , 回滾是沒有辦法處理這些情況的。

鑑於沒有任何機制能避免程式設計師自己造成的錯誤, 並且這類錯誤通常不會在生產環境中出現, 所以 Redis 選擇了更簡單、更快速的無回滾方式來處理事務。

樂觀鎖 WATCH 命令可以為 Redis 事務提供 check-and-set (CAS)行為。

WATCH 的鍵會被監視,並會發覺這些鍵是否被改動過了。 如果有至少一個被監視的鍵在 EXEC 執行之前被修改了, 那麼整個事務都會被取消, EXEC 返回空多條批量回復(null multi-bulk reply)來表示事務已經失敗。

舉個例子, 假設我們需要原子性地為某個值進行增 1 操作(假設 INCR 不存在)。

首先我們可能會這樣做:

val = GETmykey

val = val + 1

SET mykey $val

上面的這個實現在只有一個客戶端的時候可以執行得很好。 但是, 當多個客戶端同時對同一個鍵進行這樣的操作時, 就會產生競爭條件。

舉個例子, 如果客戶端 A 和 B 都讀取了鍵原來的值, 比如 10 , 那麼兩個客戶端都會將鍵的值設為 11 , 但正確的結果應該是 12 才對。

有了 WATCH , 我們就可以輕鬆地解決這類問題了:

WATCH mykey

val = GET mykey

val = val + 1

MULTI

SET mykey $val

EXEC

使用上面的程式碼, 如果在 WATCH 執行之後, EXEC 執行之前, 有其他客戶端修改了 mykey 的值, 那麼當前客戶端的事務就會失敗。 程式需要做的, 就是不斷重試這個操作, 直到沒有發生碰撞為止。

這種形式的鎖被稱作樂觀鎖, 它是一種非常強大的鎖機制。 並且因為大多數情況下, 不同的客戶端會訪問不同的鍵, 碰撞的情況一般都很少, 所以通常並不需要進行重試。

WATCH WATCH 使得 EXEC 命令需要有條件地執行: 事務只能在所有被監視鍵都沒有被修改的前提下執行, 如果這個前提不能滿足的話,事務就不會被執行。

如果你使用 WATCH 監視了一個帶過期時間的鍵, 那麼即使這個鍵過期了, 事務仍然可以正常執行, 關於這方面的詳細情況,請看這個帖子: http://code.google.com/p/redis/issues/detail?id=270

WATCH 命令可以被呼叫多次。 對鍵的監視從 WATCH 執行之後開始生效, 直到呼叫 EXEC 為止。

使用者還可以在單個 WATCH 命令中監視任意多個鍵, 就像這樣:

redis> WATCH key1 key2 key3

OK

EXEC 被呼叫時, 不管事務是否成功執行, 對所有鍵的監視都會被取消。

另外, 當客戶端斷開連線時, 該客戶端對鍵的監視也會被取消。

使用無引數的 UNWATCH 命令可以手動取消對所有鍵的監視。 對於一些需要改動多個鍵的事務, 有時候程式需要同時對多個鍵進行加鎖, 然後檢查這些鍵的當前值是否符合程式的要求。 當值達不到要求時, 就可以使用 UNWATCH 命令來取消目前對鍵的監視, 中途放棄這個事務, 並等待事務的下次嘗試。

WATCH 可以用於建立 Redis 沒有內建的原子操作。

Redis 指令碼和事務 從定義上來說, Redis 中的指令碼本身就是一種事務, 所以任何在事務裡可以完成的事, 在指令碼里面也能完成。 並且一般來說, 使用指令碼要來得更簡單,並且速度更快。

因為指令碼功能是 Redis 2.6 才引入的, 而事務功能則更早之前就存在了, 所以 Redis 才會同時存在兩種處理事務的方法。

不過我們並不打算在短時間內就移除事務功能, 因為事務提供了一種即使不使用指令碼, 也可以避免競爭條件的方法, 而且事務本身的實現並不複雜。

不過在不遠的將來, 可能所有使用者都會只使用指令碼來實現事務也說不定。 如果真的發生這種情況的話, 那麼我們將廢棄並最終移除事務功能。

Sentinel

Redis 的 Sentinel 系統用於管理多個 Redis 伺服器(instance), 該系統執行以下三個任務:

  1. 監控(Monitoring): Sentinel 會不斷地檢查你的主伺服器和從伺服器是否運作正常。
  2. 提醒(Notification): 當被監控的某個 Redis 伺服器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程式傳送通知。
  3. 自動故障遷移(Automatic failover): 當一個主伺服器不能正常工作時, Sentinel 會開始一次自動故障遷移操作, 它會將失效主伺服器的其中一個從伺服器升級為新的主伺服器, 並讓失效主伺服器的其他從伺服器改為複製新的主伺服器; 當客戶端試圖連線失效的主伺服器時, 叢集也會向客戶端返回新主伺服器的地址, 使得叢集可以使用新主伺服器代替失效伺服器。

Redis Sentinel 是一個分散式系統, 你可以在一個架構中執行多個 Sentinel 程式(progress), 這些程式使用流言協議(gossip protocols)來接收關於主伺服器是否下線的資訊, 並使用投票協議(agreement protocols)來決定是否執行自動故障遷移, 以及選擇哪個從伺服器作為新的主伺服器。
雖然 Redis Sentinel 釋出為一個單獨的可執行檔案 redis-sentinel , 但實際上它只是一個執行在特殊模式下的 Redis 伺服器, 你可以在啟動一個普通 Redis 伺服器時通過給定 –sentinel 選項來啟動 Redis Sentinel 。
Redis Sentinel 目前仍在開發中, 這個文件的內容可能隨著 Sentinel 實現的修改而變更。
Redis Sentinel 相容 Redis 2.4.16 或以上版本, 推薦使用 Redis 2.8.0 或以上的版本。

概念 說明
主觀下線和客觀下線 Redis 的 Sentinel 中關於下線(down)有兩個不同的概念:

1. 主觀下線(Subjectively Down, 簡稱 SDOWN)指的是單個 Sentinel 例項對伺服器做出的下線判斷。

2. 客觀下線(Objectively Down, 簡稱 ODOWN)指的是多個 Sentinel 例項在對同一個伺服器做出 SDOWN 判斷, 並且通過 SENTINEL is-master-down-by-addr 命令互相交流之後, 得出的伺服器下線判斷。 (一個 Sentinel 可以通過向另一個 Sentinel 傳送 SENTINEL is-master-down-by-addr 命令來詢問對方是否認為給定的伺服器已下線。)

如果一個伺服器沒有在 master-down-after-milliseconds 選項所指定的時間內, 對向它傳送 PING 命令的 Sentinel 返回一個有效回覆(valid reply), 那麼 Sentinel 就會將這個伺服器標記為主觀下線。

伺服器對 PING 命令的有效回覆可以是以下三種回覆的其中一種:

1. 返回 +PONG 。

2. 返回 -LOADING 錯誤。

3. 返回 -MASTERDOWN 錯誤。

如果伺服器返回除以上三種回覆之外的其他回覆, 又或者在指定時間內沒有回覆 PING 命令, 那麼 Sentinel 認為伺服器返回的回覆無效(non-valid)。

注意, 一個伺服器必須在 master-down-after-milliseconds 毫秒內, 一直返回無效回覆才會被 Sentinel 標記為主觀下線。

舉個例子, 如果 master-down-after-milliseconds 選項的值為 30000 毫秒(30 秒), 那麼只要伺服器能在每 29 秒之內返回至少一次有效回覆, 這個伺服器就仍然會被認為是處於正常狀態的。

從主觀下線狀態切換到客觀下線狀態並沒有使用嚴格的法定人數演算法(strong quorum algorithm), 而是使用了流言協議: 如果 Sentinel 在給定的時間範圍內, 從其他 Sentinel 那裡接收到了足夠數量的主伺服器下線報告, 那麼 Sentinel 就會將主伺服器的狀態從主觀下線改變為客觀下線。 如果之後其他 Sentinel 不再報告主伺服器已下線, 那麼客觀下線狀態就會被移除。

客觀下線條件只適用於主伺服器: 對於任何其他型別的 Redis 例項, Sentinel 在將它們判斷為下線前不需要進行協商, 所以從伺服器或者其他 Sentinel 永遠不會達到客觀下線條件。

只要一個 Sentinel 發現某個主伺服器進入了客觀下線狀態, 這個 Sentinel 就可能會被其他 Sentinel 推選出, 並對失效的主伺服器執行自動故障遷移操作。

每個 Sentinel 都需要定期執行的任務 1. 每個 Sentinel 以每秒鐘一次的頻率向它所知的主伺服器、從伺服器以及其他 Sentinel 例項傳送一個 PING 命令。

2. 如果一個例項(instance)距離最後一次有效回覆 PING 命令的時間超過 down-after-milliseconds 選項所指定的值, 那麼這個例項會被 Sentinel 標記為主觀下線。 一個有效回覆可以是: +PONG 、 -LOADING 或者 -MASTERDOWN 。

3. 如果一個主伺服器被標記為主觀下線, 那麼正在監視這個主伺服器的所有 Sentinel 要以每秒一次的頻率確認主伺服器的確進入了主觀下線狀態。

4. 如果一個主伺服器被標記為主觀下線, 並且有足夠數量的 Sentinel (至少要達到配置檔案指定的數量)在指定的時間範圍內同意這一判斷, 那麼這個主伺服器被標記為客觀下線。

5. 在一般情況下, 每個 Sentinel 會以每 10 秒一次的頻率向它已知的所有主伺服器和從伺服器傳送 INFO 命令。 當一個主伺服器被 Sentinel 標記為客觀下線時, Sentinel 向下線主伺服器的所有從伺服器傳送 INFO 命令的頻率會從 10 秒一次改為每秒一次。

6. 當沒有足夠數量的 Sentinel 同意主伺服器已經下線, 主伺服器的客觀下線狀態就會被移除。 當主伺服器重新向 Sentinel 的 PING命令返回有效回覆時, 主伺服器的主管下線狀態就會被移除。

自動發現 Sentinel 和從伺服器 一個 Sentinel 可以與其他多個 Sentinel 進行連線, 各個 Sentinel 之間可以互相檢查對方的可用性, 並進行資訊交換。

你無須為執行的每個 Sentinel 分別設定其他 Sentinel 的地址, 因為 Sentinel 可以通過釋出與訂閱功能來自動發現正在監視相同主伺服器的其他 Sentinel , 這一功能是通過向頻道 sentinel:hello 傳送資訊來實現的。

與此類似, 你也不必手動列出主伺服器屬下的所有從伺服器, 因為 Sentinel 可以通過詢問主伺服器來獲得所有從伺服器的資訊。

1. 每個 Sentinel 會以每兩秒一次的頻率, 通過釋出與訂閱功能, 向被它監視的所有主伺服器和從伺服器的 sentinel:hello 頻道傳送一條資訊, 資訊中包含了 Sentinel 的 IP 地址、埠號和執行 ID (runid)。

2. 每個 Sentinel 都訂閱了被它監視的所有主伺服器和從伺服器的 sentinel:hello 頻道, 查詢之前未出現過的 sentinel (looking for unknown sentinels)。 當一個 Sentinel 發現一個新的 Sentinel 時, 它會將新的 Sentinel 新增到一個列表中, 這個列表儲存了 Sentinel 已知的, 監視同一個主伺服器的所有其他 Sentinel 。

3. Sentinel 傳送的資訊中還包括完整的主伺服器當前配置(configuration)。 如果一個 Sentinel 包含的主伺服器配置比另一個 Sentinel 傳送的配置要舊, 那麼這個 Sentinel 會立即升級到新配置上。

4. 在將一個新 Sentinel 新增到監視主伺服器的列表上面之前, Sentinel 會先檢查列表中是否已經包含了和要新增的 Sentinel 擁有相同執行 ID 或者相同地址(包括 IP 地址和埠號)的 Sentinel , 如果是的話, Sentinel 會先移除列表中已有的那些擁有相同執行 ID 或者相同地址的 Sentinel , 然後再新增新 Sentinel 。

故障轉移 一次故障轉移操作由以下步驟組成:

1. 發現主伺服器已經進入客觀下線狀態。

2. 對我們的當前紀元進行自增(詳情請參考 Raft leader election ), 並嘗試在這個紀元中當選。

3. 如果當選失敗, 那麼在設定的故障遷移超時時間的兩倍之後, 重新嘗試當選。 如果當選成功, 那麼執行以下步驟。

4. 選出一個從伺服器,並將它升級為主伺服器。

5. 向被選中的從伺服器傳送 SLAVEOF NO ONE 命令,讓它轉變為主伺服器。

6. 通過釋出與訂閱功能, 將更新後的配置傳播給所有其他 Sentinel , 其他 Sentinel 對它們自己的配置進行更新。

7. 向已下線主伺服器的從伺服器傳送 SLAVEOF 命令, 讓它們去複製新的主伺服器。

8. 當所有從伺服器都已經開始複製新的主伺服器時, 領頭 Sentinel 終止這次故障遷移操作。

每當一個 Redis 例項被重新配置(reconfigured) —— 無論是被設定成主伺服器、從伺服器、又或者被設定成其他主伺服器的從伺服器 —— Sentinel 都會向被重新配置的例項傳送一個 CONFIG REWRITE 命令, 從而確保這些配置會持久化在硬碟裡。

Sentinel 使用以下規則來選擇新的主伺服器:

1. 在失效主伺服器屬下的從伺服器當中, 那些被標記為主觀下線、已斷線、或者最後一次回覆 PING 命令的時間大於五秒鐘的從伺服器都會被淘汰。

2. 在失效主伺服器屬下的從伺服器當中, 那些與失效主伺服器連線斷開的時長超過 down-after 選項指定的時長十倍的從伺服器都會被淘汰。

3. 在經歷了以上兩輪淘汰之後剩下來的從伺服器中, 我們選出複製偏移量(replication offset)最大的那個從伺服器作為新的主伺服器; 如果複製偏移量不可用, 或者從伺服器的複製偏移量相同, 那麼帶有最小執行 ID 的那個從伺服器成為新的主伺服器。

Sentinel 自動故障遷移的一致性特質

Sentinel 自動故障遷移使用 Raft 演算法來選舉領頭(leader) Sentinel , 從而確保在一個給定的紀元(epoch)裡, 只有一個領頭產生。

這表示在同一個紀元中, 不會有兩個 Sentinel 同時被選中為領頭, 並且各個 Sentinel 在同一個紀元中只會對一個領頭進行投票。

更高的配置紀元總是優於較低的紀元, 因此每個 Sentinel 都會主動使用更新的紀元來代替自己的配置。

簡單來說, 我們可以將 Sentinel 配置看作是一個帶有版本號的狀態。 一個狀態會以最後寫入者勝出(last-write-wins)的方式(也即是,最新的配置總是勝出)傳播至所有其他 Sentinel 。

舉個例子, 當出現網路分割(network partitions)時, 一個 Sentinel 可能會包含了較舊的配置, 而當這個 Sentinel 接到其他 Sentinel 發來的版本更新的配置時, Sentinel 就會對自己的配置進行更新。

如果要在網路分割出現的情況下仍然保持一致性, 那麼應該使用 min-slaves-to-write 選項, 讓主伺服器在連線的從例項少於給定數量時停止執行寫操作, 與此同時, 應該在每個執行 Redis 主伺服器或從伺服器的機器上執行 Redis Sentinel 程式。

Sentinel 狀態的持久化

Sentinel 的狀態會被持久化在 Sentinel 配置檔案裡面。

每當 Sentinel 接收到一個新的配置, 或者當領頭 Sentinel 為主伺服器建立一個新的配置時, 這個配置會與配置紀元一起被儲存到磁碟裡面。

這意味著停止和重啟 Sentinel 程式都是安全的。

Sentinel 在非故障遷移的情況下對例項進行重新配置

即使沒有自動故障遷移操作在進行, Sentinel 總會嘗試將當前的配置設定到被監視的例項上面。 特別是:

1. 根據當前的配置, 如果一個從伺服器被宣告為主伺服器, 那麼它會代替原有的主伺服器, 成為新的主伺服器, 並且成為原有主伺服器的所有從伺服器的複製物件。

2. 那些連線了錯誤主伺服器的從伺服器會被重新配置, 使得這些從伺服器會去複製正確的主伺服器。

不過, 在以上這些條件滿足之後, Sentinel 在對例項進行重新配置之前仍然會等待一段足夠長的時間, 確保可以接收到其他 Sentinel 發來的配置更新, 從而避免自身因為儲存了過期的配置而對例項進行了不必要的重新配置。

TILT 模式 Redis Sentinel 嚴重依賴計算機的時間功能: 比如說, 為了判斷一個例項是否可用, Sentinel 會記錄這個例項最後一次相應 PING 命令的時間, 並將這個時間和當前時間進行對比, 從而知道這個例項有多長時間沒有和 Sentinel 進行任何成功通訊。

不過, 一旦計算機的時間功能出現故障, 或者計算機非常忙碌, 又或者程式因為某些原因而被阻塞時, Sentinel 可能也會跟著出現故障。

TILT 模式是一種特殊的保護模式: 當 Sentinel 發現系統有些不對勁時, Sentinel 就會進入 TILT 模式。

因為 Sentinel 的時間中斷器預設每秒執行 10 次, 所以我們預期時間中斷器的兩次執行之間的間隔為 100 毫秒左右。 Sentinel 的做法是, 記錄上一次時間中斷器執行時的時間, 並將它和這一次時間中斷器執行的時間進行對比:

1. 如果兩次呼叫時間之間的差距為負值, 或者非常大(超過 2 秒鐘), 那麼 Sentinel 進入 TILT 模式。

2. 如果 Sentinel 已經進入 TILT 模式, 那麼 Sentinel 延遲退出 TILT 模式的時間。

當 Sentinel 進入 TILT 模式時, 它仍然會繼續監視所有目標, 但是:

1. 它不再執行任何操作,比如故障轉移。

2. 當有例項向這個 Sentinel 傳送 SENTINEL is-master-down-by-addr 命令時, Sentinel 返回負值: 因為這個 Sentinel 所進行的下線判斷已經不再準確。

如果 TILT 可以正常維持 30 秒鐘, 那麼 Sentinel 退出 TILT 模式。

其他

叢集教程
Redis 叢集規範

相關文章