Redis核心概念

image.png

概述

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 之間的關系:

image.png


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

image.png
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)

MULTI 、 EXECDISCARDWATCH 是 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<br><br> 上面的這個實現在只有一個客戶端的時候可以執行得很好。 但是, 當多個客戶端同時對同一個鍵進行這樣的操作時, 就會產生競爭條件。<br><br> 舉個例子, 如果客戶端 A 和 B 都讀取了鍵原來的值, 比如 10 , 那么兩個客戶端都會將鍵的值設為 11 , 但正確的結果應該是 12 才對。<br><br> 有了 [WATCH](http://doc.redisfans.com/transaction/watch.html#watch) , 我們就可以輕松地解決這類問題了:<br><br> WATCH mykey<br><br> val = GET mykey<br><br> val = val + 1<br><br> MULTI<br><br> SET mykeyval

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 集群規范


個人介紹:

高廣超:多年一線互聯網研發與架構設計經驗,擅長設計與落地高可用、高性能、可擴展的互聯網架構。

本文首發在 http://www.lxweimin.com/u/2766e4cfc391轉載請注明!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,048評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,414評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,169評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,722評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,465評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,823評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,813評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,000評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,554評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,295評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,513評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,722評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,125評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,430評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,237評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,482評論 2 379

推薦閱讀更多精彩內容