Redis 提供了靈活的鍵過期機制,允許開發者設定鍵的存活時間 (TTL),並在過期後自動刪除。這對於管理快取資料、控制記憶體使用量非常有效。除了使用 DEL 命令手動刪除鍵,EXPIREEXPIREATPEXPIREPEXPIREAT 等命令提供了更精細的過期時間控制,單位可以是秒或毫秒。TTLPTTL 命令則可以檢視鍵的剩餘存活時間。實際應用中,可以利用鍵過期機製取代帶有時間戳的 ZSET,簡化程式碼並提升效率。理解 Redis 的持久化機制對於資料安全至關重要。Redis 提供了快照和 AOF 兩種持久化方式。快照持久化會在特定時間點建立資料的完整副本,BGSAVE 命令可在後台非同步執行快照,避免阻塞主程式,而 SAVE 命令則會阻塞 Redis 直到快照完成。AOF 持久化則記錄所有寫入操作,並在重啟時重新執行這些操作以還原資料。開發者可以根據業務需求和系統環境選擇合適的持久化策略,並組態 appendfsync 選項來控制 AOF 資料寫入磁碟的頻率,平衡資料安全性和效能。

Redis 中的鍵過期機制

在 Redis 中寫入資料時,可能會遇到資料不再需要的情況。我們可以使用 DEL 命令明確刪除資料,或者使用過期機制讓 Redis 在指定的時間後自動刪除鍵。當我們說一個鍵具有存活時間(time to live),或它將在某個時間過期時,意味著 Redis 將在該鍵的過期時間到達時自動刪除它。

為什麼使用鍵過期機制?

讓鍵在一定時間後過期對於處理快取資料的清理非常有用。雖然在其他章節中,我們不常看到 Redis 中鍵過期的使用(除了第 6.2 節、第 7.1 節和第 7.2 節),這主要是因為所使用的資料結構型別;我們使用的命令很少提供自動設定鍵過期時間的功能。對於容器型別的資料結構(如 LIST、SET、HASH 和 ZSET),我們只能對整個鍵設定過期時間,而不能對個別專案進行設定(這也是為什麼我們在某些地方使用帶有時間戳的 ZSET)。

與鍵過期相關的命令

本文將介紹用於在指定超時後或特定時間自動過期和刪除鍵的命令。閱讀本文後,您將能夠使用過期機制來保持 Redis 記憶體使用率低,並清理不再需要的資料。

表 3.13 Redis 中用於處理過期的命令

命令示例用法和描述
PERSISTPERSIST key-name — 移除鍵的過期時間
TTLTTL key-name — 傳回鍵過期前的剩餘時間(秒)
EXPIREEXPIRE key-name seconds — 設定鍵在指定的秒數後過期
EXPIREATEXPIREAT key-name timestamp — 設定鍵在指定的 Unix 時間戳後過期
PTTLPTTL key-name — 傳回鍵過期前的剩餘毫秒數(Redis 2.6 及以後版本)
PEXPIREPEXPIRE key-name milliseconds — 設定鍵在指定的毫秒數後過期(Redis 2.6 及以後版本)
PEXPIREATPEXPIREAT key-name timestamp-milliseconds — 設定鍵在指定的毫秒級 Unix 時間戳後過期(Redis 2.6 及以後版本)

使用範例

>>> conn.set('key', 'value')
True
>>> conn.get('key')
'value'
>>> conn.expire('key', 2)  # 設定 'key' 在 2 秒後過期
True
>>> time.sleep(2)
>>> conn.get('key')  # 鍵已過期,被刪除
>>> conn.set('key', 'value2')
True
>>> conn.expire('key', 100); conn.ttl('key')  # 設定 'key' 在 100 秒後過期,並檢查剩餘時間
True
100

程式碼解密:

  1. conn.set('key', 'value'):設定一個名為 'key' 的字串值為 'value'
  2. conn.get('key'):取得 'key' 的值,傳回 'value'
  3. conn.expire('key', 2):設定 'key' 在 2 秒後過期。
  4. time.sleep(2):等待 2 秒,讓 'key' 過期。
  5. conn.get('key'):嘗試取得已過期的 'key',傳回 None 因為鍵已被刪除。
  6. conn.expire('key', 100):重新設定 'key' 在 100 秒後過期。
  7. conn.ttl('key'):檢查 'key' 的剩餘存活時間,傳回剩餘秒數。

練習:使用 EXPIRE 取代帶有時間戳的 ZSET

在第 2.1 節、第 2.2 節和第 2.5 節中,我們使用帶有時間戳的 ZSET 來維護會話 ID 的列表以進行清理。透過使用 ZSET,我們可以在清理會話時進行分析。如果我們不需要分析,可以使用過期機制來達到類別似的效果,而無需清理函式。您可以嘗試更新 update_token()add_to_cart() 函式,使其使用鍵過期機制,而不是使用 “recent” ZSET 和清理函式嗎?

確保資料安全與效能

在前幾章中,您已經瞭解了 Redis 中可用的各種命令及其如何操作資料結構,甚至使用 Redis 解決了一些問題。本章將為您準備使用 Redis 建構真實的軟體,展示如何保持資料安全,即使面對系統故障,並指出在保持資料完整性的同時提高 Redis 效能的方法。

本章涵蓋內容

■ 將資料持久化到磁碟
■ 將資料複製到其他機器
■ 處理系統故障
■ Redis 交易
■ 非交易管道
■ 診斷效能問題

Redis 資料持久化流程

  graph LR;
    A[寫入資料] --> B[持久化到磁碟];
    B --> C[主從複製];
    C --> D[資料備份];
    D --> E[系統還原];

圖表翻譯:
此圖展示了 Redis 資料持久化的流程。首先,資料被寫入 Redis,然後持久化到磁碟,接著透過主從複製將資料同步到其他機器進行備份,最終實作系統還原的功能。

重點解析

  1. 資料持久化:Redis 提供了多種持久化選項,可以將資料儲存到磁碟上,以確保資料不會因為系統故障而丟失。
  2. 主從複製:透過將資料複製到其他機器,可以提高資料的可用性和系統的容錯能力。
  3. 效能最佳化:本章還將介紹如何診斷和解決 Redis 的效能問題,以確保系統的高效運作。

第四章 保持資料安全與確保效能

在這一章中,我們將探討Redis的內部運作機制,以確保資料的正確性,並進一步提升資料操作的效能。首先,我們將探討Redis如何將資料儲存於磁碟上,以便在重啟後資料仍然存在。

4.1 持久化選項

Redis提供了兩種不同的方式將資料持久化到磁碟。第一種稱為快照(snapshotting),它會在某一時刻將記憶體中的資料寫入磁碟。第二種方法稱為僅追加檔案(Append-Only File, AOF),它會將所有寫入命令追加到檔案中。這兩種方法可以單獨使用,也可以結合使用,甚至在某些情況下不使用,具體取決於您的資料和應用需求。

4.1.1 使用快照持久化到磁碟

在Redis中,我們可以透過建立快照來建立記憶體資料在某一時刻的副本。建立快照後,這些快照可以被備份、複製到其他伺服器以建立伺服器的克隆,或者保留下來以供未來重啟使用。

在組態方面,快照被寫入到組態中指定的dbfilename檔案中,並儲存在dir路徑下。在下一次快照執行之前,如果Redis、系統或硬體發生當機,那麼自上一次快照開始(並完成)以來寫入Redis的資料將會丟失。

例如,假設Redis目前有10 GB的資料正在記憶體中執行。前一個快照在下午2:35開始並已完成。現在,在下午3:06開始了一個新的快照,在快照完成於下午3:08之前的這段時間內,有35個鍵被更新。如果系統在下午3:06至3:08之間當機並阻止Redis完成其快照操作,那麼在下午2:35至現在之間寫入的資料將會丟失。但是,如果系統在快照完成後立即當機,那麼只有那35個鍵的更新會丟失。

有五種方法可以啟動快照:

  1. 任何Redis客戶端都可以透過呼叫BGSAVE命令來啟動快照。在支援BGSAVE的平台上(基本上除了Windows以外的所有平台),Redis會進行fork操作,子程式會將快照寫入磁碟,而父程式繼續回應命令。

    # BGSAVE 命令範例
    redis-cli BGSAVE
    

    內容解密:

    • BGSAVE命令是非同步的,它允許Redis在後台進行快照操作,而不會阻塞主程式。
    • 當執行BGSAVE時,Redis會fork一個子程式來處理快照,主程式繼續處理客戶端請求。
  2. Redis客戶端也可以透過呼叫SAVE命令來啟動快照,這將導致Redis停止回應任何命令,直到快照完成。這個命令不常用,除非我們需要在磁碟上有我們的資料,並且我們可以等待它完成,或者我們沒有足夠的記憶體來執行BGSAVE

    # SAVE 命令範例
    redis-cli SAVE
    

    內容解密:

    • SAVE命令是同步的,它會阻塞Redis的主程式,直到快照操作完成。
    • 由於其阻塞性質,SAVE命令通常只在特定情況下使用,例如在關閉Redis服務之前確保資料被儲存。
  3. 如果Redis組態了save行,例如save 60 10000,Redis會在最後一次成功儲存開始後的60秒內發生了10,000次寫入時自動觸發BGSAVE操作。當存在多個save行時,任何時候只要其中一條規則匹配,就會觸發BGSAVE

    # save 組態範例
    save 60 10000
    

    內容解密:

    • save組態選項允許根據寫入操作的頻率自動觸發快照。
    • 這種機制確保了即使沒有手動干預,Redis也能定期儲存資料到磁碟。

Redis 快照流程

  graph LR
    A[開始] --> B{是否觸發BGSAVE?}
    B -->|是| C[Fork 子程式]
    B -->|否| D[繼續處理請求]
    C --> E[子程式寫入快照]
    E --> F[完成快照]
    F --> D

圖表翻譯: 此圖示展示了Redis執行快照的流程。首先,系統檢查是否需要觸發BGSAVE操作。如果需要,則fork一個子程式來處理快照操作;如果不需要,則繼續處理客戶端請求。子程式負責將資料寫入磁碟,完成後主程式繼續正常運作。

結語

本章節詳細介紹了Redis的持久化選項,包括快照和AOF,並探討瞭如何使用這些機制來確保資料的安全性和系統的效能。透過合理組態和使用這些持久化策略,可以有效地保護Redis中的資料,並根據具體需求最佳化系統效能。接下來的章節將繼續探討Redis的其他重要功能和最佳實踐。

Redis 資料持久化選項:快照與效能考量

Redis 提供了多種資料持久化機制,其中快照(Snapshot)是最基本的持久化方式之一。透過定期將記憶體中的資料儲存到磁碟,Redis 能夠在發生故障時還原資料。本章將探討如何組態 Redis 的快照持久化,以及在不同場景下的最佳實踐。

Redis 快照持久化的運作機制

當 Redis 接收到 SHUTDOWN 命令或標準的 TERM 訊號時,會執行 SAVE 操作,阻塞客戶端的進一步命令,然後關閉服務。此外,當一個 Redis 伺服器連線到另一個 Redis 伺服器並發出 SYNC 命令開始複製時,主 Redis 伺服器會啟動 BGSAVE 操作(如果尚未執行或最近未完成)。

# Redis 組態範例:save 900 1
# 當至少有一次寫入操作發生在過去 900 秒(15 分鐘)內時,自動執行 BGSAVE
save 900 1

內容解密:

此組態告訴 Redis 在滿足特定條件時自動執行 BGSAVE。條件是自上次 BGSAVE 以來至少發生了一次寫入操作,並且距上次 BGSAVE 已過去至少 900 秒(15 分鐘)。這種組態適合開發環境,因為它減少了快照的頻率,從而降低了對系統資源的佔用。

不同場景下的快照組態

開發環境

在開發環境中,主要關注的是最小化快照對系統效能的影響。由於開發環境通常不需要極高的資料可靠性,可以採用較為寬鬆的快照策略,如 save 900 1。這表示 Redis 將在至少 900 秒內有至少一次寫入操作時執行 BGSAVE

日誌彙總與分析

在處理日誌彙總和分析的場景中,需要考慮在發生故障時能夠容忍的資料丟失量。如果能夠接受丟失最多一小時的資料,可以組態 save 3600 1(3600 秒等於一小時)。為了在發生故障後能夠還原,需要記錄處理日誌的進度資訊。

使用 Redis 記錄日誌處理進度

以下是一個 Python 範例,展示如何使用 Redis 記錄日誌處理的進度:

def process_logs(conn, path, callback):
    # 取得目前的處理進度
    current_file, offset = conn.mget('progress:file', 'progress:position')
    pipe = conn.pipeline()

    def update_progress():
        pipe.mset({
            'progress:file': fname,
            'progress:position': offset
        })
        pipe.execute()

    for fname in sorted(os.listdir(path)):
        if fname < current_file:
            continue
        inp = open(os.path.join(path, fname), 'rb')
        if fname == current_file:
            inp.seek(int(offset, 10))
        else:
            offset = 0
            current_file = None
        for lno, line in enumerate(inp):
            callback(pipe, line)
            offset = int(offset) + len(line)
            if not (lno+1) % 1000:
                update_progress()
        update_progress()
        inp.close()

內容解密:

此函式透過 Redis 紀錄目前處理的日誌檔案和位置,以便在系統當機後能夠從上次中斷的地方繼續處理。它利用了 Redis 的 MGETMSET 命令來取得和更新進度資訊,並使用管道(Pipeline)來提高效能。

大資料場景下的考量

當 Redis 中儲存的資料量達到幾 GB 或以上時,快照操作可能會對系統效能產生顯著影響。隨著資料量的增長,執行 BGSAVE 所需的時間也會增加。在記憶體使用量達到數十 GB 的情況下,BGSAVE 可能會導致系統暫停較長時間,或引發大量虛擬記憶體使用,從而降低 Redis 的效能。

  graph LR
    A[開始 BGSAVE] --> B[Fork Redis 程式]
    B --> C[儲存資料到磁碟]
    C --> D[完成 BGSAVE]
    D --> E[繼續處理請求]

圖表翻譯: 此圖表展示了 Redis 執行 BGSAVE 的流程。首先,Redis 會 fork 一個子程式來執行快照操作。然後,子程式將資料儲存到磁碟。完成後,Redis 繼續處理客戶端請求。

Redis 資料持久化:快照與附加檔案持久化

Redis 作為高效能的記憶體資料函式庫,其持久化機制對於資料安全和系統效能至關重要。本章將探討 Redis 的兩種主要持久化方式:快照(Snapshot)和附加檔案(Append-Only File, AOF)持久化。

4.1 快照持久化

快照持久化是 Redis 預設的持久化方式之一,它透過建立資料函式庫在某一時間點的完整副本來實作資料持久化。Redis 提供了兩種主要的快照持久化命令:BGSAVESAVE

4.1.1 BGSAVE 與 SAVE 的比較

  • BGSAVE 命令:該命令會 fork 一個子程式來建立快照,而主程式繼續處理客戶端請求。雖然這種方式避免了阻塞主程式,但 fork 操作本身可能導致短暫的停頓,尤其是在記憶體使用量較大的情況下。

    # Redis 設定檔中啟用自動快照
    save 900 1  # 900秒內至少有1次變更時執行BGSAVE
    save 300 10 # 300秒內至少有10次變更時執行BGSAVE
    
  • SAVE 命令:與 BGSAVE 不同,SAVE 命令會阻塞 Redis 主程式直到快照建立完成。雖然這種方式避免了 fork 操作帶來的延遲,但會暫停所有客戶端請求。

#### 內容解密:

  • BGSAVE 命令透過 fork 子程式實作非同步快照,適合對即時性要求較高的場景。
  • SAVE 命令則是同步操作,會阻塞所有請求,適用於對資料一致性要求極高且可以容忍短暫停機的場景。

虛擬化環境下的效能考量

在虛擬化環境(如 Xen)中,BGSAVE 的 fork 操作可能會導致更長的停頓時間。例如,使用 20 GB 記憶體的 Redis 例項可能導致 4-6 秒的停頓。因此,選擇合適的持久化策略需考慮應用場景和系統環境。

4.2 附加檔案(AOF)持久化

除了快照持久化,Redis 還支援附加檔案(AOF)持久化。AOF 持久化透過記錄每次資料變更操作來實作資料還原。

#### AOF 持久化的工作原理

AOF 持久化將每次寫入操作追加到檔案末尾。透過重新執行這些操作,可以重建資料函式庫的當前狀態。啟用 AOF 持久化需要在設定檔中設定 appendonly yes

# 啟用AOF持久化
appendonly yes

#### fsync 策略選擇

Redis 提供三種 appendfsync 策略,用於控制 AOF 資料寫入磁碟的頻率:

策略描述
always每個寫入操作都立即同步到磁碟,保證最大程度的資料安全,但效能較低。
everysec每秒同步一次,平衡了資料安全和效能。
no由作業系統控制何時將資料同步到磁碟,可能導致資料丟失。

#### SSD 與 appendfsync always 的注意事項

使用 SSD 時,若設定 appendfsync always,可能會因頻繁的小資料量寫入導致寫入放大,縮短 SSD 的壽命。

圖表翻譯:Redis 持久化選項比較

  graph LR
A[Redis 持久化選項] --> B[快照持久化]
A --> C[AOF 持久化]
B --> D[BGSAVE]
B --> E[SAVE]
C --> F[appendfsync always]
C --> G[appendfsync everysec]
C --> H[appendfsync no]

圖表翻譯: 此圖示展示了 Redis 的兩種主要持久化方式及其子選項。快照持久化包括 BGSAVESAVE 命令,而 AOF 持久化則提供了不同的 appendfsync 策略供選擇。