Redis 的效能表現取決於多個因素,其中資料持久化策略和複製機制扮演著至關重要的角色。僅追加檔案(AOF)雖然能最大限度減少資料損失,但檔案大小會不斷增長,影響 Redis 重啟速度。BGREWRITEAOF
命令可以重寫 AOF 檔案,移除冗餘命令,但仍受限於 fork 時間和記憶體使用率。透過 auto-aof-rewrite-percentage
和 auto-aof-rewrite-min-size
引數,可以設定自動執行 BGREWRITEAOF
的條件,平衡效能與檔案大小。除了 AOF,快照也是一種重要的持久化方式,建議定期備份快照和 AOF 檔案到其他伺服器,確保資料安全。
第四章:保持資料安全與確保效能
4.1.3 重寫/壓縮僅追加檔案(AOF)
在瞭解了AOF(Append-Only File)持久化機制後,您可能會疑惑為什麼還需要快照(Snapshot)。如果使用僅追加檔案可以將資料損失降至最低,甚至可以忽略不計,並且可以最小化定期將資料持久化到磁碟所需的時間,那麼似乎我們的選擇應該很明確。然而,實際上這種選擇並不那麼簡單:因為每次對Redis的寫入操作都會導致將命令記錄到磁碟上的日誌中,所以僅追加的日誌檔案(AOF)會持續增長。隨著時間的推移,日益增長的AOF可能會導致磁碟空間不足,但更常見的問題是,當Redis重新啟動時,它需要執行AOF中的每一個命令。當處理大型AOF檔案時,Redis可能需要很長時間才能啟動。
為瞭解決AOF持續增長的問題,我們可以使用BGREWRITEAOF
命令,這個命令會透過移除冗餘的命令來重寫AOF,使其盡可能地縮短。BGREWRITEAOF
的工作原理與快照的BGSAVE
類別似:執行一個fork操作,然後在子程式中重寫僅追加的日誌。因此,所有與快照效能相關的限制,如fork時間、記憶體使用等,在使用AOF時仍然存在。但更糟糕的是,因為AOF的大小可能是轉儲檔案的許多倍(如果不加以控制),當AOF被重寫時,作業系統需要刪除舊的AOF,這可能會導致系統在刪除數十GB的AOF檔案時掛起數秒鐘。
對於快照,我們可以使用save
組態選項來啟用自動使用BGSAVE
寫入快照。使用AOF時,有兩個組態選項可以啟用自動執行BGREWRITEAOF
:auto-aof-rewrite-percentage
和auto-aof-rewrite-min-size
。例如,如果設定auto-aof-rewrite-percentage 100
和auto-aof-rewrite-min-size 64mb
,當AOF被啟用時,Redis將在AOF的大小至少比上一次重寫後的AOF大小大100%,並且AOF大小至少為64MB時啟動BGREWRITEAOF
。如果我們的AOF重寫太頻繁,我們可以將代表100%的100增加到更大的值,但這可能會導致Redis在一段時間沒有進行重寫後需要更長的時間來啟動。
無論我們選擇使用僅追加檔案還是快照,將資料儲存在磁碟上是一個很好的第一步。但是,除非我們的資料已經被備份到其他地方(最好是多個位置),否則我們仍然面臨著資料丟失的風險。只要有可能,我建議將快照和新重寫的僅追加檔案備份到其他伺服器上。
透過使用僅追加檔案或快照,我們可以在系統重新啟動或當機之間保持資料的安全。隨著負載的增加,或對資料完整性要求變得更加嚴格,我們可能需要考慮使用複製(Replication)來幫助我們。
4.2 複製(Replication)
多年來,在擴充套件平台以應對更高負載的過程中,工程師和管理員已經將複製新增到他們的技巧函式庫中,以幫助系統擴充套件。複製是一種方法,透過這種方法,其他伺服器會接收到資料的連續更新副本,以便這些副本可以服務讀取查詢。在關係型資料函式庫的世界中,一個主資料函式庫將寫入操作傳送到多個從資料函式庫是很常見的做法,從資料函式庫執行所有的讀取查詢。Redis已經採用了這種複製方法作為擴充套件的一種方式,本文將討論在Redis中組態複製,以及Redis在複製過程中的運作方式。
儘管Redis可能很快,但在某些情況下,一個Redis伺服器的執行速度可能不夠快。特別是,對SET和ZSET的操作可能涉及數萬甚至數百萬個專案。當我們開始處理數百萬個專案時,集合操作可能需要幾秒鐘才能完成,而不是毫秒或微秒。但是,即使單個命令可以在10毫秒內完成,這仍然限制了我們每秒只能從單個Redis例項執行100個命令。
SUNIONSTORE的效能範例
作為考慮Redis效能的一個參考,在2.4 GHz Intel Core 2 Duo上,Redis將花費7-8毫秒來執行一個SUNIONSTORE
操作,將兩個包含10,000個專案的SET合併成一個包含20,000個專案的SET。
對於需要擴充套件讀取查詢或可能需要寫入臨時資料的情況(我們將在第7章討論一些這樣的場景),我們可以設定額外的從屬Redis伺服器來保持資料集的副本。在從主伺服器接收到初始資料副本後,從屬伺服器會實時保持更新,因為客戶端向主伺服器寫入資料。在主/從設定中,客戶端不會連線到主伺服器來讀取資料,而是連線到其中一個從屬伺服器來讀取資料(通常是以隨機的方式選擇以嘗試平衡負載)。
讓我們來討論如何為主/從操作組態Redis,以及Redis在整個過程中的行為。
4.2.1 為複製組態Redis
正如我在4.1.1節中提到的,當一個從屬伺服器連線到主伺服器時,主伺服器將啟動一個BGSAVE
操作。要在主伺服器端組態複製,我們只需要確保在清單4.1中顯示的dir
和dbfilename
組態選項下列出的路徑和檔名對於Redis程式是可寫入的。
儘管有多種選項控制著從屬伺服器的行為,但只有一個選項是真正必要的來啟用從屬功能:slaveof
。如果我們在組態檔案中設定slaveof host port
,那麼使用該組態啟動的Redis將使用提供的主機和埠作為它應該連線到的主Redis伺服器。如果我們有一個已經執行的系統,我們可以告訴一個Redis伺服器停止從屬,或者甚至從屬於一個新的或不同的主伺服器。要連線到一個新的主伺服器,我們可以使用SLAVEOF host port
命令,或者如果我們想停止從主伺服器更新資料,我們可以使用SLAVEOF no one
。
程式碼範例:組態 Redis 複製
# 在 redis.conf 中設定 slaveof
slaveof 127.0.0.1 6379
# 或者在執行中的 Redis 使用 SLAVEOF 命令
redis-cli SLAVEOF 127.0.0.1 6379
內容解密:
slaveof 127.0.0.1 6379
:這個指令告訴 Redis 將當前例項設定為指定主機和埠的從屬。這裡的主機是127.0.0.1
,埠是6379
。redis-cli SLAVEOF 127.0.0.1 6379
:這個命令用於在執行中的 Redis 例項上設定新的主伺服器。同樣,這裡的主機是127.0.0.1
,埠是6379
。SLAVEOF no one
:這個命令用於使當前 Redis 例項停止作為從屬,並轉變為主伺服器。
為Redis組態主/從操作並不是很複雜,但瞭解Redis何時成為主伺服器或從屬伺服器的行為是非常有趣和有用的。
graph LR; A[Master Redis] -->|BGSAVE|> B[Slave Redis]; B -->|SYNC|> A; A -->|Continuous Updates|> B;
圖表翻譯:
此圖表展示了 Redis 主/從複製的基本流程。主 Redis 伺服器透過 BGSAVE
操作初始化從屬 Redis 的資料,然後透過 SYNC
命令進行資料同步,最後持續將更新推播到從屬 Redis。
組態Redis進行複製是一個相對簡單的過程,但瞭解其背後的運作機制對於確保系統的高效運作至關重要。透過正確地設定和管理複製,我們可以顯著提高系統的可擴充套件性和資料的安全性。
Redis 複製啟動流程詳解
Redis 的主從複製(Master-Slave Replication)是確保資料安全和高用性的重要機制。當一個從節點(Slave)連線到主節點(Master)時,會觸發一系列操作以確保資料的一致性。
Redis 複製流程
下表列出了當一個從節點連線到主節點時,主從節點上發生的所有操作步驟:
步驟 | 主節點操作 | 從節點操作 |
---|---|---|
1 | (等待命令) | (重新)連線到主節點;發出 SYNC 命令 |
2 | 啟動 BGSAVE 操作;保留所有在 BGSAVE 之後傳送的寫入命令 | 服務舊資料(如果有),或傳回錯誤給命令(取決於組態) |
3 | 完成 BGSAVE;開始將快照傳送給從節點;繼續保留寫入命令的 backlog | 丟棄所有舊資料(如果有);開始載入接收到的快照 |
4 | 完成快照傳送給從節點;開始將寫入命令 backlog 傳送給從節點 | 完成解析快照;開始正常回應命令 |
5 | 完成 backlog 傳送;開始實時傳輸寫入命令 | 完成執行主節點的寫入命令 backlog;繼續執行實時命令 |
程式碼範例:組態 Redis 主從複製
import redis
# 組態主節點
master = redis.Redis(host='master_host', port=6379, db=0)
# 組態從節點
slave = redis.Redis(host='slave_host', port=6379, db=0)
# 在從節點上執行 SLAVEOF 命令,使其成為主節點的從節點
slave.slaveof('master_host', 6379)
內容解密:
- 匯入 Redis 客戶端函式庫:使用 Python 的
redis
函式庫來與 Redis 例項互動。 - 組態主從節點:分別建立與主節點和從節點的連線。
- 執行 SLAVEOF 命令:在從節點上執行
SLAVEOF
命令,使其連線到指定的主節點並開始複製流程。
同步過程中的資料處理
在同步過程中,從節點會清空其現有的所有資料,並載入從主節點接收到的資料。這是因為 Redis 的設計是確保資料的一致性。
警告:Redis 不支援主主複製
有些使用者可能會誤以為可以透過設定兩個 Redis 例項互為對方的從節點來實作多主複製,但這實際上是不可行的。這種做法會導致兩個例項不斷地相互通訊,造成資源浪費,並可能導致資料不一致。
多個從節點連線的情況
當多個從節點嘗試連線到主節點時,可能會發生兩種不同的情況:
- 在步驟 3 之前:所有從節點將接收相同的快照和 backlog 寫入命令。
- 在步驟 3 之後:新的從節點將觸發新的同步流程,主節點將為其單獨進行 BGSAVE 和 backlog 操作。
主從鏈的應用
在某些情況下,當需要複製到多個從節點時,網路頻寬可能會成為瓶頸。為瞭解決這個問題,可以建立主從鏈,即讓從節點再擁有自己的從節點,從而分散複製的負擔。
主從鏈結構
graph LR A[Master] --> B[Slave1] A --> C[Slave2] B --> D[Slave3] B --> E[Slave4]
圖表翻譯: 此圖示展示了一個典型的 Redis 主從鏈結構,其中主節點(Master)連線到多個從節點(Slave1 和 Slave2),而 Slave1 又進一步連線到其自己的從節點(Slave3 和 Slave4)。這種結構有助於分散複製負擔,提高系統的可擴充套件性。
確保資料安全與效能最佳化
在探討Redis的資料安全與效能最佳化時,我們必須考慮到資料持久化、複製以及系統故障處理等關鍵議題。本章節將深入分析如何結合Redis的複製功能和僅追加檔案(Append-Only Files, AOF)機制,以確保資料的安全性並維持系統的效能。
資料持久化與效能的權衡
在第4.1.2節中,我們曾經討論過使用AOF搭配同步(syncing)來限制資料遺失的機會。雖然設定Redis在每次寫入時都同步到磁碟可以幾乎完全避免資料遺失(除了系統或硬碟當機的情況),但這將嚴重限制效能。如果我們讓Redis每秒同步一次,我們可以在保持一定效能的同時,將資料遺失的風險降至最低。
結合複製與AOF確保資料安全
為了確保資料能夠在多台機器上持久化,我們需要設定一個主伺服器(master)搭配多個從伺服器(slaves)。透過在從伺服器(以及可選的主伺服器)上組態appendonly yes
和appendfsync everysec
,我們可以建立一組每秒都會將資料同步到磁碟的機器叢集。
# Redis 組態示例
appendonly yes
appendfsync everysec
內容解密:
這段組態啟用了Redis的AOF持久化機制,並設定每秒將寫入操作同步到磁碟。這種組態在資料安全性和效能之間取得了良好的平衡。
驗證磁碟寫入
要驗證寫入主伺服器的資料是否成功到達從伺服器並持久化到磁碟,我們需要進行兩步檢查:首先,確認資料是否已經被從伺服器接收;其次,確認資料是否已經被寫入磁碟。
步驟1:寫入唯一識別符到主伺服器
在寫入重要資料後,向主伺服器寫入一個唯一的虛擬值,然後在從伺服器上檢查這個值是否存在。
步驟2:檢查從伺服器的AOF待處理同步狀態
透過檢查INFO
命令的輸出中的aof_pending_bio_fsync
值,可以瞭解是否有待處理的AOF同步操作。如果這個值為0,表示所有已知資料都已經被寫入磁碟。
def wait_for_sync(mconn, sconn):
identifier = str(uuid.uuid4())
mconn.zadd('sync:wait', identifier, time.time())
# 等待從伺服器連線到主伺服器
while not sconn.info()['master_link_status'] != 'up':
time.sleep(.001)
# 等待從伺服器接收到資料變更
while not sconn.zscore('sync:wait', identifier):
time.sleep(.001)
# 等待最多一秒,直到資料被同步到磁碟
deadline = time.time() + 1.01
while time.time() < deadline:
if sconn.info()['aof_pending_bio_fsync'] == 0:
break
time.sleep(.001)
# 清理狀態並移除過期的條目
mconn.zrem('sync:wait', identifier)
mconn.zremrangebyscore('sync:wait', 0, time.time()-900)
內容解密:
這個wait_for_sync
函式首先向主伺服器新增一個唯一的識別符,然後等待從伺服器接收到這個變更。接著,它檢查從伺服器的AOF同步狀態,等待直到資料被確認寫入磁碟或超時。最後,它清理相關的狀態資訊。
處理系統故障
為了能夠處理Redis中的系統故障,我們需要提前準備。這包括了設定合理的持久化機制、複製策略以及監控機制,以確保在發生故障時能夠快速還原。
第4章 確保資料安全與效能維護
在前面的章節中,我們探討了Redis的持久化機制,包括快照(snapshotting)和追加檔案(append-only file)兩種方式。這些機制能夠幫助我們在發生系統故障時,盡可能地減少資料損失。然而,即使採用了這些機制,我們仍然需要做一些額外的工作,以確保資料的一致性和完整性。本章將重點討論如何在系統故障發生時,進行有效的還原和處理。
4.3.1 驗證快照和追加檔案
當系統發生故障時,我們需要使用Redis提供的工具來驗證和修復快照和追加檔案。Redis提供了兩個命令列工具,分別是redis-check-aof
和redis-check-dump
,用於檢查和修復追加檔案和快照檔案的狀態。
使用redis-check-aof
和redis-check-dump
如果我們在沒有任何引數的情況下執行這兩個命令,將會看到基本的幫助資訊:
$ redis-check-aof
Usage: redis-check-aof [--fix] <file.aof>
$ redis-check-dump
Usage: redis-check-dump <dump.rdb>
$
對於redis-check-aof
命令,如果我們提供--fix
引數,它將嘗試修復追加檔案。修復的方法是掃描整個追加檔案,尋找不完整或錯誤的命令,一旦發現第一個錯誤的命令,就會將檔案截斷到該命令之前的位置。
$ redis-check-aof --fix appendonly.aof
修復快照檔案的限制
目前,Redis並沒有提供修復損壞的快照檔案的方法。雖然理論上可以找到第一個錯誤發生的位置,但由於快照檔案是經過壓縮的,因此錯誤可能會導致剩餘的檔案內容變得不可讀。因此,建議定期備份重要的快照檔案,並計算SHA1或SHA256雜湊值,以便在還原時驗證檔案的完整性。
$ sha256sum dump.rdb
校驗和與雜湊
Redis 2.6及以後版本在快照檔案中包含了CRC64校驗和。CRC校驗和可以用於檢測某些型別的網路傳輸或磁碟損壞錯誤。然而,對於任意錯誤的檢測,SHA系列的密碼雜湊函式(如SHA1或SHA256)更加合適。
4.3.2 更換故障的主伺服器
當我們使用Redis的主從複製架構時,可能會遇到主伺服器故障的情況。這時,我們需要將從伺服器提升為新的主伺服器,或者使用新的伺服器替換故障的主伺服器。下面是一個具體的例子:
假設機器A執行著Redis主伺服器,而機器B執行著Redis從伺服器。由於某些原因,機器A突然失去網路連線。我們可以使用機器C來替換機器A,成為新的主伺服器。
步驟一:在機器B上建立快照
首先,我們需要在機器B上執行SAVE
命令,建立一個新的快照檔案。
redis 127.0.0.1:6379> SAVE
OK
步驟二:將快照檔案複製到機器C
接下來,我們需要將機器B上的快照檔案複製到機器C。
$ scp /var/local/redis/dump.rdb machine-c.vpn:/var/local/redis/
步驟三:在機器C上啟動Redis
將快照檔案複製到機器C後,我們需要在機器C上啟動Redis服務。
$ sudo /etc/init.d/redis-server start
步驟四:將機器B設定為機器C的從伺服器
最後,我們需要將機器B設定為機器C的從伺服器。
redis 127.0.0.1:6379> SLAVEOF machine-c.vpn 6379
OK
詳細命令流程
以下是上述步驟的詳細命令流程:
user@vpn-master ~:$ ssh root@machine-b.vpn
Last login: Wed Mar 28 15:21:06 2012 from ...
root@machine-b ~:$ redis-cli
redis 127.0.0.1:6379> SAVE
OK
redis 127.0.0.1:6379> QUIT
root@machine-b ~:$ scp /var/local/redis/dump.rdb machine-c.vpn:/var/local/redis/
dump.rdb 100% 525MB 8.1MB/s 01:05
root@machine-b ~:$ ssh machine-c.vpn
Last login: Tue Mar 27 12:42:31 2012 from ...
root@machine-c ~:$ sudo /etc/init.d/redis-server start
Starting Redis server...
root@machine-c ~:$ exit
root@machine-b ~:$ redis-cli
redis 127.0.0.1:6379> SLAVEOF machine-c.vpn 6379
OK
redis 127.0.0.1:6379> QUIT
root@machine-b ~:$ exit
user@vpn-master ~:$
#### 內容解密:
此段落描述瞭如何在一台Redis主伺服器故障時,使用一台從伺服器和一台新的伺服器進行替換。首先,在從伺服器上執行SAVE
命令生成最新的快照,接著將這個快照複製到新的伺服器上,並在新伺服器上啟動Redis服務。最後,將原來的從伺服器設定為新主伺服器的從伺服器,從而實作主伺服器的替換。這一系列操作確保了Redis服務的高用性。
隨著Redis的不斷發展,未來可能會出現更多高效、可靠的持久化機制和故障還原方案。開發者和維運人員需要持續關注Redis的新特性和最佳實踐,以確保系統的最佳效能和資料安全性。
圖表說明
以下是本章內容的流程圖,用於展示更換故障主伺服器的步驟:
graph LR; A[機器A(主伺服器)故障] --> B[在機器B上執行SAVE]; B --> C[將dump.rdb複製到機器C]; C --> D[在機器C上啟動Redis]; D --> E[將機器B設定為機器C的從伺服器]; E --> F[完成主伺服器替換];
圖表翻譯:
此圖展示了當Redis主伺服器(機器A)發生故障時,如何使用從伺服器(機器B)和新伺服器(機器C)進行替換的流程。首先,在機器B上執行SAVE
命令生成最新的資料快照;接著,將這個快照檔案複製到機器C;然後,在機器C上啟動Redis服務;最後,將機器B設定為機器C的從伺服器,從而完成主伺服器的替換。這一系列操作確保了Redis服務的高用性和資料的安全性。
詳細擴充內容
為了進一步提升文章的完整性和技術深度,以下是一些額外的擴充內容:
持久化機制的深入解析:進一步分析Redis提供的兩種持久化機制,包括它們的工作原理、優缺點以及適用場景。
實際案例分析:分享一些實際應用中遇到的Redis故障案例,以及如何透過上述方法進行還原和處理的經驗。
最佳實踐建議:根據實際經驗,提供一些關於如何組態Redis持久化機制、如何進行備份以及如何最佳化效能的最佳實踐建議。
未來趨勢展望:探討Redis未來可能的發展方向,包括新的持久化技術、更高效的故障還原方案等。
透過這些擴充內容,可以進一步提升文章的技術價值和實用性,為讀者提供更全面的知識和指導。