Redis系列之基礎篇【下】

Redis系列之基礎篇【下】

1.過期時間詳解

2.發佈訂閱詳解

3.緩存持久化詳解

4.內存回收詳解

====================================

一. 過期時間詳解

過期時間設置
在Redis中提供了Expire命令設置一個鍵的過期時間,到期以後Redis會自動刪除它。這個在我們實際使用過程中用得非常多。
EXPIRE命令的使用方法爲

EXPIRE key seconds

其中seconds 參數表示鍵的過期時間,單位爲秒。
EXPIRE 返回值爲1表示設置成功,0表示設置失敗或者鍵不存在
如果向知道一個鍵還有多久時間被刪除,可以使用TTL命令

TTL key

當鍵不存在時,TTL命令會返回-2
而對於沒有給指定鍵設置過期時間的,通過TTL命令會返回-1
如果向取消鍵的過期時間設置(使該鍵恢復成爲永久的),可以使用PERSIST命令,如果該命令執行成功或者成功
清除了過期時間,則返回1 。 否則返回0(鍵不存在或者本身就是永久的)
EXPIRE命令的seconds命令必須是整數,所以最小單位是1秒,如果向要更精確的控制鍵的過期時間可以使用
PEXPIRE命令,當然實際過程中用秒的單位就夠了。 PEXPIRE命令的單位是毫秒。即PEXPIRE key 1000與EXPIRE
key 1相等;對應的PTTL以毫秒單位獲取鍵的剩餘有效時間
還有一個針對字符串獨有的過期時間設置方式

setex(String key,int seconds,String value)

過期刪除的原理
Redis 中的主鍵失效是如何實現的,即失效的主鍵是如何刪除的?實際上,Redis 刪除失效主鍵的方法主要有兩
種:

  1. 消極方法(passive way)
  2. 積極方法(active way)

消極方法(passive way):在主鍵被訪問時如果發現它已經失效,那麼就刪除它
積極方法(active way):週期性地從設置了失效時間的主鍵中選擇一部分失效的主鍵刪除對於那些從未被查詢的key,即便它們已經過期,被動方式也無法清除。因此Redis會週期性地隨機測試一些key,已過期的key將會被刪掉。Redis每秒會進行10次操作,具體的流程:

  1. 隨機測試 20 個帶有timeout信息的key;
  2. 刪除其中已經過期的key;
  3. 如果超過25%的key被刪除,則重複執行步驟1;
    這是一個簡單的概率算法(trivial probabilistic algorithm),基於假設我們隨機抽取的key代表了全部的key空間。

二. 發佈訂閱詳解

Redis提供了發佈訂閱功能,可以用於消息的傳輸,Redis提供了一組命令可以讓開發者實現“發佈/訂閱”模式(publish/subscribe) . 該模式同樣可以實現進程間的消息傳遞,它的實現原理是
發佈/訂閱模式包含兩種角色,分別是發佈者和訂閱者。訂閱者可以訂閱一個或多個頻道,而發佈者可以向指定的
頻道發送消息,所有訂閱此頻道的訂閱者都會收到該消息
發佈者發佈消息的命令是PUBLISH, 用法是
PUBLISH channel message
比如向channel.1發一條消息:hello
PUBLISH channel.1 “hello”
這樣就實現了消息的發送,該命令的返回值表示接收到這條消息的訂閱者數量。因爲在執行這條命令的時候還沒有
訂閱者訂閱該頻道,所以返回爲0. 另外值得注意的是消息發送出去不會持久化,如果發送之前沒有訂閱者,那麼後
續再有訂閱者訂閱該頻道,之前的消息就收不到了
訂閱者訂閱消息的命令是
SUBSCRIBE channel [channel …]
該命令同時可以訂閱多個頻道,比如訂閱channel.1的頻道。 SUBSCRIBE channel.1
執行SUBSCRIBE命令後客戶端會進入訂閱狀態

結構圖
channel分兩類,一個是普通channel、另一個是pattern channel(規則匹配), producer1發佈了一條消息
【publish abc hello】,redis server發給abc這個普通channel上的所有訂閱者,同時abc也匹配上了pattern
channel的名字,所以這條消息也會同時發送給pattern channel *bc上的所有訂閱者
在這裏插入圖片描述

三.緩存持久化詳解

Redis支持兩種方式的持久化,一種是RDB方式、另一種是AOF(append-only-file)方式。前者會根據指定的規
則“定時”將內存中的數據存儲在硬盤上,而後者在每次執行命令後將命令本身記錄下來。兩種持久化方式可以單獨
使用其中一種,也可以將這兩種方式結合使用。

RDB方式

當符合一定條件時,Redis會單獨創建(fork)一個子進程來進行持久化,會先將數據寫入到一個臨時文件中,等到持久化過程都結束了,再用這個臨時文件替換上次持久化好的文件。整個過程中,主進程是不進行任何IO操作的,這就確保了極高的性能。如果需要進行大規模數據的恢復,且對於數據恢復的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺點是最後一次持久化後的數據可能丟失–fork的作用是複製一個與當前進程一樣的進程。新進程的所有數據(變量、環境變量、程序計數器等)數值都和原進程一致,但是是一個全新的進程,並作爲原進程的子進程Redis會在以下幾種情況下對數據進行快照

  1. 根據配置規則進行自動快照
  2. 用戶執行SAVE或者GBSAVE命令
  3. 執行FLUSHALL命令
  4. 執行復制(replication)時

根據配置規則進行自動快照
Redis允許用戶自定義快照條件,當符合快照條件時,Redis會自動執行快照操作。快照的條件可以由用戶在配置文件中配置。配置格式如下第一個參數是時間窗口,第二個是鍵的個數,也就是說,在第一個時間參數配置範圍內被更改的鍵的個數大於後面的changes時,即符合快照條件。redis默認配置了三個規則
save 900 1
save 300 10
save 60 10000
每條快照規則佔一行,每條規則之間是“或”的關係。 在900秒(15分)內有一個以上的鍵被更改則進行快照。

用戶執行SAVE或BGSAVE命令
除了讓Redis自動進行快照以外,當我們對服務進行重啓或者服務器遷移我們需要人工去幹預備份。redis提供了兩
條命令來完成這個任務

  1. save命令
    當執行save命令時,Redis同步做快照操作,在快照執行過程中會阻塞所有來自客戶端的請求。當redis內存中的數
    據較多時,通過該命令將導致Redis較長時間的不響應。所以不建議在生產環境上使用這個命令,而是推薦使用
    bgsave命令
  2. bgsave命令
    bgsave命令可以在後臺異步地進行快照操作,快照的同時服務器還可以繼續響應來自客戶端的請求。執行BGSAVE
    後,Redis會立即返回ok表示開始執行快照操作。
    通過LASTSAVE命令可以獲取最近一次成功執行快照的時間; (自動快照採用的是異步快照操作)

執行FLUSHALL命令
該命令在前面講過,會清除redis在內存中的所有數據。執行該命令後,只要redis中配置的快照規則不爲空,也就
是save 的規則存在。redis就會執行一次快照操作。不管規則是什麼樣的都會執行。如果沒有定義快照規則,就不
會執行快照操作

執行復制時
該操作主要是在主從模式下,redis會在複製初始化時進行自動快照。這個會在後面講到;
這裏只需要瞭解當執行復制操作時,及時沒有定義自動快照規則,並且沒有手動執行過快照操作,它仍然會生成
RDB快照文件

AOF方式

當使用Redis存儲非臨時數據時,一般需要打開AOF持久化來降低進程終止導致的數據丟失。AOF可以將Redis執行
的每一條寫命令追加到硬盤文件中,這一過程會降低Redis的性能,但大部分情況下這個影響是能夠接受的,另外
使用較快的硬盤可以提高AOF的性能。

開啓AOF
默認情況下Redis沒有開啓AOF(append only file)方式的持久化,可以通過appendonly參數啓用,在redis.conf
中找到 appendonly yes
開啓AOF持久化後每執行一條會更改Redis中的數據的命令後,Redis就會將該命令寫入硬盤中的AOF文件。AOF文
件的保存位置和RDB文件的位置相同,都是通過dir參數設置的,默認的文件名是apendonly.aof. 可以在redis.conf
中的屬性 appendfilename appendonlyh.aof修改

AOF的實現
AOF文件以純文本的形式記錄Redis執行的寫命令例如開啓AOF持久化的情況下執行如下4條命令
set foo 1
set foo 2
set foo 3
get
redis 會將前3條命令寫入AOF文件中,通過vim的方式可以看到aof文件中的內容
我們會發現AOF文件的內容正是Redis發送的原始通信協議的內容,從內容中我們發現Redis只記錄了3
條命令。然後這時有一個問題是前面2條命令其實是冗餘的,因爲這兩條的執行結果都會被第三條命令覆
蓋。隨着執行的命令越來越多,AOF文件的大小也會越來越大,其實內存中實際的數據可能沒有多少,
那這樣就會造成磁盤空間以及redis數據還原的過程比較長的問題。因此我們希望Redis可以自動優化
AOF文件,就上面這個例子來說,前面兩條是可以被刪除的。 而實際上Redis也考慮到了,可以配置一
個條件,每當達到一定條件時Redis就會自動重寫AOF文件,這個條件的配置

auto-aof-rewritepercentage 100 auto-aof-rewrite-min-size 64mb

auto-aof-rewrite-percentage 表示的是當目前的AOF文件大小超過上一次重寫時的AOF文件大小的百分之多少時會
再次進行重寫,如果之前沒有重寫過,則以啓動時AOF文件大小爲依據
auto-aof-rewrite-min-size 表示限制了允許重寫的最小AOF文件大小,通常在AOF文件很小的情況下即使其中有很
多冗餘的命令我們也並不太關心。
另外,還可以通過BGREWRITEAOF 命令手動執行AOF,執行完以後冗餘的命令已經被刪除了
在啓動時,Redis會逐個執行AOF文件中的命令來將硬盤中的數據載入到內存中,載入的速度相對於RDB會慢一些

AOF的重寫原理
在這裏插入圖片描述
Redis 可以在 AOF 文件體積變得過大時,自動地在後臺對 AOF 進行重寫: 重寫後的新 AOF 文件包含了恢復當前
數據集所需的最小命令集合。
重寫的流程是這樣,主進程會fork一個子進程出來進行AOF重寫,這個重寫過程並不是基於原有的aof文件來做
的,而是有點類似於快照的方式,全量遍歷內存中的數據,然後逐個序列到aof文件中。在fork子進程這個過程
中,服務端仍然可以對外提供服務,那這個時候重寫的aof文件的數據和redis內存數據不一致了怎麼辦?不用擔
心,這個過程中,主進程的數據更新操作,會緩存到aof_rewrite_buf中,也就是單獨開闢一塊緩存來存儲重寫期間
收到的命令,當子進程重寫完以後再把緩存中的數據追加到新的aof文件。
當所有的數據全部追加到新的aof文件中後,把新的aof文件重命名爲,此後所有的操作都會被寫入新的aof文件。
如果在rewrite過程中出現故障,不會影響原來aof文件的正常工作,只有當rewrite完成後纔會切換文件。因此這個
rewrite過程是比較可靠的
在這裏插入圖片描述

Redis啓動加載持久文件流程

在這裏插入圖片描述
Redis4.0後的混合持久化機制
開啓混合持久化
4.0版本的混合持久化默認關閉的,通過aof-use-rdb-preamble配置參數控制,yes則表示開啓,no表示禁用,5.0之後默認開啓。
混合持久化是通過bgrewriteaof完成的,不同的是當開啓混合持久化時,fork出的子進程先將共享的內存副本全量的以RDB方式寫入aof文件,然後在將重寫緩衝區的增量命令以AOF方式寫入到文件,寫入完成後通知主進程更新統計信息,並將新的含有RDB格式和AOF格式的AOF文件替換舊的的AOF文件。簡單的說:新的AOF文件前半段是RDB格式的全量數據後半段是AOF格式的增量數據,

優點:混合持久化結合了RDB持久化 和 AOF 持久化的優點, 由於絕大部分都是RDB格式,加載速度快,同時結合AOF,增量的數據以AOF方式保存了,數據更少的丟失。
缺點:兼容性差,一旦開啓了混合持久化,在4.0之前版本都不識別該aof文件,同時由於前部分是RDB格式,閱讀性較差。

常見問題:

  1. redis提供了rdb持久化方案,爲什麼還要aof?
    優化數據丟失問題,rdb會丟失最後一次快照後的數據,aof丟失不會超過2秒的數據

  2. 如果aof和rdb同時存在,聽誰的?
    aof

  3. rdb和aof優勢劣勢
    rdb 適合大規模的數據恢復,對數據完整性和一致性不高 , 在一定間隔時間做一次備份,如果redis意外down機的話,就會丟失最後一次快照後的所有操作
    aof 根據配置項而定

官方建議
兩種持久化機制同時開啓,如果兩個同時開啓 優先使用aof持久化機制

性能建議(這裏只針對單機版redis持久化做性能建議):
因爲RDB文件只用作後備用途,只要15分鐘備份一次就夠了,只保留save 900 1這條規則。
如果Enalbe AOF,好處是在最惡劣情況下也只會丟失不超過兩秒數據,啓動腳本較簡單隻load自己的AOF文件就可以了。
代價一是帶來了持續的IO,二是AOF rewrite的最後將rewrite過程中產生的新數據寫到新文件造成的阻塞幾乎是不可避免的。
只要硬盤許可,應該儘量減少AOF rewrite的頻率,AOF重寫的基礎大小默認值64M太小了,可以設到5G以上。
默認超過原大小100%大小時重寫可以改到適當的數值。

四. 內存回收詳解

Redis中提供了多種內存回收策略,當內存容量不足時,爲了保證程序的運行,這時就不得不淘汰內存中的一些對
象,釋放這些對象佔用的空間,那麼選擇淘汰哪些對象呢?
其中,默認的策略爲noeviction策略,當內存使用達到閾值的時候,所有引起申請內存的命令會報錯

  • allkeys-lru:從數據集(server.db[i].dict)中挑選最近最少使用的數據淘汰【適合場景】如果我們的應用對緩存的訪問都是相對熱點數據,那麼可以選擇這個策略
  • allkeys-random:隨機移除某個key。 適合的場景:如果我們的應用對於緩存key的訪問概率相等,則可以使用這個策略
  • volatile-random:從已設置過期時間的數據集(server.db[i].expires)中任意選擇數據淘汰。
  • volatile-lru:從已設置過期時間的數據集(server.db[i].expires)中挑選最近最少使用的數據淘汰。
  • volatile-ttl:從已設置過期時間的數據集(server.db[i].expires)中挑選將要過期的數據淘汰【適合場景】這種策略使得我們可以向Redis提示哪些key更適合被淘汰,我們可以自己控制

實際上Redis實現的LRU並不是可靠的LRU,也就是名義上我們使用LRU算法淘汰內存數據,但是實際上被淘汰的鍵
並不一定是真正的最少使用的數據,這裏涉及到一個權衡的問題,如果需要在所有的數據中搜索最符合條件的數
據,那麼一定會增加系統的開銷,Redis是單線程的,所以耗時的操作會謹慎一些。爲了在一定成本內實現相對的
LRU,早期的Redis版本是基於採樣的LRU,也就是放棄了從所有數據中搜索解改爲採樣空間搜索最優解。Redis3.0
版本之後,Redis作者對於基於採樣的LRU進行了一些優化,目的是在一定的成本內讓結果更靠近真實的LRU。

手寫LRU
在這裏插入圖片描述
注:希望大家技術越來越6p

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章