Redis 實戰 —— 07. 複製、處理故障、事務及效能優化

滿賦諸機發表於2021-01-26

複製簡介 P61

關係型資料庫通常會使用一個主伺服器 (master) 向多個從伺服器 (slave) 傳送更新,並使用從伺服器來處理所有讀請求。 Redis 也採用了同樣的方法實現自己的複製特性,並將其用作擴充套件效能的一種手段。 P69

在接收到主伺服器傳送的資料初始副本 (initial copy of the data) 之後,客戶端每次向主伺服器進行寫入時,從伺服器都會實時地得到更新。 P69

複製 P62

對於一個正在執行的 Redis 伺服器,使用者可以通過傳送 SLAVEOF NO ONE 命令來讓伺服器終止複製操作,不再接受主伺服器的資料更新;也可以通過傳送 SLAVEOF host port 命令來讓伺服器開始複製一個新的主伺服器。 P69

配置選項
# 設定本機為指定伺服器的從伺服器
#
# slaveof <master-host> <master-port>

# 當主伺服器設定了密碼保護時(用 requirepass 指定的密碼)
# 從伺服器服務連線主伺服器需要設定相應的密碼
#
# masterauth <master-password>


# 當從伺服器 與主伺服器失去連線 或者 正在進行復制 時
# yes: 從伺服器會繼續響應客戶端的請求(預設 yes)
# no: 除了 INFO 和 SLAVOF 命令之外的任何請求都會
#     返回一個錯誤 "SYNC with master in progress"
#
slave-serve-stale-data yes

# 從伺服器每隔一定時間會向主伺服器傳送 ping
# 預設 10 秒
#
# repl-ping-slave-period 10

# ping 回覆 或 主伺服器批量資料傳輸 超時時長
# 預設 60 秒
# 確保 repl-timeout 大於 repl-ping-slave-period
#
# repl-timeout 60
從伺服器連線主伺服器時的步驟 P70
步驟 主伺服器操作 從伺服器操作
1 (等待命令進入) 連線(或者重連)主伺服器,傳送 SYNC 命令
2 開始執行 BGSAVE ,並使用緩衝區記錄 BGSAVE 之後執行所有寫命令 根據配置選項 (slave-serve-stale-data) 來決定是繼續使用現有的資料(如果有的話)來處理客戶端的命令請求,還是向客戶端返回錯誤
3 BGSAVE 執行完畢,向從伺服器傳送快照檔案,並在傳送期間繼續使用緩衝區記錄被執行的寫命令 丟棄所有舊資料(如果有的話),開始載入主伺服器發來的快照檔案
4 快照檔案傳送完畢,開始向從伺服器傳送儲存在緩衝區裡面的寫命令 完成對快照檔案的解釋操作,像往常一樣開始接受命令請求
5 緩衝區儲存的寫命令傳送完畢;從現在開始,每執行一個寫命令,就向從伺服器傳送相同的寫命令 執行主伺服器發來的所有儲存在緩衝區裡面的寫命令;並從現在開始,接受並執行主伺服器傳來的每個寫命令

在實際中最好讓主伺服器只使用 50% ~ 65% 的記憶體,留下 30% ~ 45% 的記憶體用於執行 BGSAVE 命令和建立記錄寫命令的緩衝區。 P70

從伺服器在進行同步時,會清空自己的所有資料。 P70

Redis 不支援主主複製 (master-master replication) P71

當一個從伺服器連線一個已有的主伺服器時,有時可以重用已有的快照檔案: P71

  • 步驟 3 尚未執行:所有從伺服器都會接收到相同的快照檔案和相同的緩衝區寫命令
  • 步驟 3 正在執行或已經執行完畢:當主伺服器與比較早進行連線的從伺服器執行完複製所需的 5 個步驟之後,主伺服器會與新連線的從伺服器執行一次新的步驟 1 至步驟 5
主從鏈 P71

Redis 的主伺服器和從伺服器沒有什麼特別不同的地方,所以從伺服器也可以擁有自己的從伺服器,並由此形成主從鏈 (master/slave chaining) 。 P71

不過,如果從伺服器 X 擁有從伺服器 Y ,那麼當從伺服器 X 在執行步驟 4 時,它將斷開與從伺服器 Y 的連線,導致從伺服器 Y 需要重新連線並重新同步。 P71

當讀請求比寫請求重要,且讀請求的數量遠遠超過一臺 Redis 伺服器可以處理的範圍時,就需要新增新的從伺服器來處理讀請求。隨著負載不斷上升,主伺服器可能會無法快速地更新所有從伺服器,或者因為重新連線和重新同步從伺服器而導致系統超載。為了緩解這個問題,可以建立一個由 Redis 主從節點 (master/slave node) 組成的中間層來分擔主伺服器的複製工作。 P71

通過同時使用複製和 AOF 持久化,使用者可以增強 Redis 對於系統崩潰的抵抗能力。 P73

處理系統故障

驗證快照檔案和 AOF 檔案

redis-check-aof [--fix] <file.aof> 可以檢查 AOF 檔案,並且可以進行修復:將第一個出錯命令(大部分情況下在檔案末尾)及之後的所有命令刪除。 P74

redis-check-dump <dump.rdb> 可以檢查快照檔案。快照檔案目前無法進行修復,因為快照檔案本身進行了壓縮。 P74

事務

Redis 事務的作用: P76

  • 防止資料出錯
  • 在某些情況下提升效能。利用事務一次性傳送多個命令,然後等待所有回覆出現實現流水線 (pipeline)。通過減少客戶端與 Redis 伺服器之間的網路通訊次數來提升 Redis 在執行多個命令時的效能。

關聯式資料庫事務與 Redis 事務的區別: P76

  • 關聯式資料庫:先向資料庫伺服器傳送 BEGIN ,然後執行各個相互一致 (consistent) 的讀寫操作,最後可以選擇傳送 COMMIT 來確認之前的修改,或者傳送 ROLLBACK 來放棄之前的修改。
  • Redis :以特殊命令 MULTI 開始,然後傳入多個命令,最後以 EXEC 結束,並依次執行傳入的命令。Redis 事務不能以一致的形式讀取資料,使得某一型別的問題難以解決,且無法實現二階段提交。

通過使用 WATCH, MULTI/EXEC, UNWATCH/DISCARD 等命令,程式可以在執行某些重要操作時,通過確保自己正在使用的資料沒有發生變化來避免出錯。 P78

  • WATCH: 使用 WATCh 對鍵進行監視之後,直到使用者執行 EXEC 的這段時間裡面,如果有其他客戶端搶先對任何被監視的鍵進行了替換、更新或刪除等操作,那麼當使用者嘗試執行 EXEC 時,事務將失敗並返回一個錯誤。(之後使用者可選擇重試事務或者放棄事務)
  • UNWATCH: 可以在 WATCH 執行之後、 MULTI 執行之前對連線進行重置 (reset)
  • DISCARD: 可以在 MULTI 執行之後、 EXEC 執行之前對連線進行重置,即取消 WATCH 並清空所有已入隊命令

為什麼 Redis 沒有實現典型的加鎖功能? P82

  • 加鎖是悲觀鎖,持有鎖的客戶端執行越慢,等待解鎖的客戶端被阻塞的時間越長
  • WATCH 是樂觀鎖,客戶端不必等待取得鎖,只需要在事務執行失敗時重試即可,樂觀鎖可以提高併發能力

非事務型流水線 (non-transactional pipeline)

對於無需事務的大量操作可以使用非事務型流水線,可以避免事務消耗資源。

Python 中通過修改入參即可將事務改為非事務型流水線,而 Go 中根據具體框架的不同,可能需要手動封裝流水線的處理邏輯。

效能優化

要對 Redis 的效能進行優化,首先需要弄清楚各種型別的 Redis 命令能跑多塊,而這一點可以通過呼叫 Redis 附帶的效能測試程式 redis-benchmark 得知。 P85

切記不要將輸出結果看作是應用程式的實際效能,因為 redis-benchmark 不會處理執行命令所獲得的命令回覆,所以它節約了大量用於對命令回覆進行語法分析的時間。 P86

可能影響效能的原因 P86
  • 未使用流水線:可視情況適當使用流水線
  • 對於每個命令或每組命令都建立了新的連線:使用連線池重用 Redis 連線
  • Redis 的資料結構或命令不合理(value 非常大,使用 keys, hgetall 等):優化資料結構和命令

本文首發於公眾號:滿賦諸機(點選檢視原文) 開源在 GitHub :reading-notes/redis-in-action

相關文章