1、簡述
(1)關於Redis鍵的過期策略,首先要了解兩種時間的區別,生存時間和過期時間;
-
-
- 生存時間:一段時長,如30秒、6000毫秒,設定鍵的生存時間就是設定這個鍵可以存在多長時間,命令有兩個 expire(秒)、pexpire(毫秒)(可以參考 Redis(四)--- Redis命令參考)。
- 過期時間:一個時間點,unix時間戳,設定鍵的過期時間就是設定鍵在之後的某個時間點過期,命令兩個expreat(秒時間戳)、pexpireat(毫秒時間戳)。
- 無論是過期時間或生存時間,在底層儲存都會轉換為unix時間戳,由於過期就是設定的unix時間錯,所以我們通常把兩種時間都叫做過期時間。
-
(2)當記憶體使用達到Redis限制的上限時,就會觸發記憶體淘汰機制。
(3)Redis的釋出與訂閱功能可以作為簡單的訊息佇列使用。
(4)當多條命令需要原子性執行時,可以開啟Redis的事物命令進行執行。
2、過期策略
通過上一章(Redis(五)--- Redis的持久化RDB和AOF)第一節可以知道,Reids鍵的過期時間都是存在資料庫結果中的過期字典中的,那麼一個鍵過期了,什麼時候回進行刪除呢?有三種策略。
(1)定時刪除:在設定過期時間的同時,設定一個定時器,定時器的執行時間就是過期的時間點。
-
-
- 優點:對記憶體最友好,過期的鍵會以最快的被刪除,釋放記憶體。
- 缺點:對CPU時間最不友好,在大量鍵設定過期時間時,會建立大量的定時器,執行浪費CPU時間。
-
(2)惰性刪除:不管鍵是否過期,只有每次取值的時候,才檢查是否過期,過期就刪除。
-
-
- 優點:對CPU時間最友好,取值時檢查,只對當前鍵操作,不影響其他。
- 缺點:隊記憶體不友好,可能會存在大量過期的未被使用的鍵值沒有刪除,無用資料佔用了大量記憶體。
-
(3)定期刪除:每隔一段時間,程式對資料庫進行一次檢查,過期的就刪除。
-
-
- 優點:前兩種方案的折中,通過減少執行頻率來減少對CPU時間的影響,通過定期刪除減少了對記憶體的浪費。
- 缺點:執行頻率需要掌握好,不然太頻繁則退化成定時刪除,太少則退化成惰性刪除。
-
Redis採用的是惰性刪除和定期刪除兩種策略。
持久化和複製對過期鍵的處理
(1)RDB持久化:
主伺服器:RDB檔案無論是生成或載入,都會對過期鍵進行檢查;生成時,過期鍵不寫入;載入時,過期鍵會忽略。
從伺服器:載入時,不會檢查是否過期,資料都會載入。
(2)AOF持久化:AOF檔案寫入時,鍵過期未刪除,不影響;鍵過期已刪除,則在AOF檔案後追加DEL命令。
(3)AOF重寫:AOF重寫過程中會進行檢查,過且的鍵忽略。
(4)複製:主從模式下,由主伺服器進行刪除過期鍵,並顯示的向從伺服器傳送DEL命令;從伺服器自身不具備刪除過期鍵值行為。
3、記憶體淘汰機制
當Redis的記憶體使用達到設定的記憶體上限時就會觸發記憶體淘汰機制,按照特定的淘汰演算法進行資料清理,釋放記憶體。
具體的記憶體淘汰演算法有一以下幾種:
(1)noeviction:不淘汰,記憶體不足時, 新寫入會報錯。
(2)allkeys-lru:LRU,記憶體不足時,淘汰最近最少使用的key。
(3)allkeys-random:隨機,記憶體不足時,在所有key中隨機選擇一個key淘汰。
(4)volatile-lru:過期時間內LRU,記憶體不足時,在設定了過期時間的key中,淘汰最近最少使用的key。
(5)volatile-random:過期時間內隨機,記憶體不足時,在設定了過期時間的key中,隨機選擇一個key淘汰。
(6)volatile-ttl:更早過期時間,記憶體不足時,在設定了過期時間的key中,選擇有更早過期時間的key淘汰。
Redis預設使用的是LRU演算法,需要注意的是LRU並不是多有的keys進行LRU,而是在所有的key中隨機選擇3個key,在這3個key中進行LRU演算法選擇;3個這個個數可以在redis配置檔案中進行配置maxmeory-samples選項。
在大規模併發的情況下,我們可以使用叢集方式進行記憶體的擴充。
4、訊息
這裡所說到的訊息是指Redis的釋出與訂閱功能,在筆者的專案中,就實際用到了這個功能,具體是在分散式session中用到的,作用是減少的共享sesssion訪問redis的次數,用釋出與訂閱模式通知session的更新、刪除等;具體思路參考了J2Cache框架,J2Cache是開源框架,作者紅薯等;當然筆者的實現太簡單了,沒有那麼多功能;還有一次用到的就是作為簡單的訊息佇列使用的,在一個小的獨立模組中,沒有使用訊息佇列,用此功能代替。
Redis的釋出訂閱功能由publish、subscribe、psubscribe等命令組成,客戶端可以訂閱一個和多個頻道,頻道的所有訂閱者都可以收到其他客戶端傳送的訊息,說道這裡和微信的公眾號推送好像啊。
客戶端不單單可以訂閱某個頻道,還可以以通配方式訂閱多個頻道,如圖:
訂閱頻道的底層儲存結構
(1)常規訂閱模式
Redis將所有頻道的訂閱關係都儲存到底層的一個字典裡(貌似redis的底層資料都是放在字典裡,key-value資料,設定的過期時間),字典的鍵是被訂閱的頻道,字典的值是一個連結串列,連結串列裡記錄了所有訂閱者。
-
-
- 當有訂閱者訂閱頻道時,訂閱者就會放到訂閱頻道對應的連結串列的表尾,如果是新頻道就建立一個空連結串列後再新增。
- 當有訂閱者退訂頻道時,就會刪除對應連結串列上的節點。
-
(2)通配訂閱模式
在Redis的書中把此種訂閱稱為模式的訂閱,而筆者本身為了便於記憶和理解更習慣稱之為通配訂閱模式;通配模式訂閱的資料都存在底層資料的連結串列中(這次不是字典了),連結串列的每個節點記錄著一個pubsubPattern結構,結構裡client屬性記錄了客戶端,而pattern屬性記錄了訂閱頻道的萬用字元,如圖
-
-
- 當客戶端以萬用字元模式訂閱時,連結串列便會增加一個pubsubPattern結構儲存客戶端和萬用字元結構,然後將其新增到連結串列的表尾。
- 當有客戶端退訂時,將會在連結串列中查詢到對應的萬用字元結構,並刪除對應的pubsubPattern結構。
-
瞭解了底層的儲存結構就可以明白訊息的傳送就是輪訓連結串列的查詢匹配的訂閱者的過程。
關於釋出訂閱命令的用法請參考 《Redis(四)--- Redis命令參考》
5、事物
Redis通過multi、exec、watch等命令實現事務功能;事物是將多個命令打包,然後一次性的順序執行。
Redis事物也ACID性質:
(1)原子性(Atomicity):事物內的命令,要麼都執行,要麼都不執行;
(2)一致性(Consistency):事物執行前後,資料庫是一致的,不會包含非法或無效資料,不論事物是否執行成功。
(3)隔離性(Isolation):多個事物併發執行,不會相互影響;並行和序列結果一致,因為redis是單執行緒的方式執行事物的。
(4)耐久性(Durability):當一個事物執行完畢後,結果將被儲存到永久的儲存介質中,即便伺服器停機,事物之後的資料也不會丟失。
事物的實現
事物以MULTI命令開始,以EXEC命令開始執行命令,在兩個命令中間可以書寫多條命令,這些命令會放入事物佇列等待執行,這個對列是放在客戶端底層結構的multiState資料結構中。
1 typedef struct multiState { 2 // 事務佇列,FIFO順序 3 multiCmd *commands; 4 // 已入隊命令計數 5 int count; 6 } multiState; 7 8 9 typedef struct multiCmd { 10 // 引數 11 robj **argv; 12 // 引數數量 13 int argc; 14 // 命令指標 15 struct redisCommand *cmd; 16 } multiCmd;
-
- 事物佇列,具有先進先出特性,先放入的命令,事物開始後先執行。
- 事物佇列是一個multiCmd型別的陣列,陣列中的每個元素都儲存一個已入隊的命令。
- 事物開始執行,伺服器會遍歷這個客戶端的事務佇列,執行佇列中的所有命令,然後將結果返回給客戶端。
- watch命令可以監視事物內的命令操作的鍵,如果鍵在事物內時有其他客戶端修改了,那麼事物將會被拒絕執行;watch命令在multi命令之前執行。
6、總結
-
- Redis的過期策略和記憶體淘汰機制是兩種不同的方式,注意不要混淆
- 在記憶體淘汰期間並不會影響過期刪除處理,過期策略主要用於處理記憶體中過且的資料。
- 釋出訂閱功能可以作為簡單的訊息佇列使用。
- 事物使用是安全的,伺服器會根據客戶端是否開啟了REDIS_DIRTY_CAS標識來決定是否執行事務,如果開啟了,則證明至少有一個鍵被修改了,則會拒絕執行事物。
參考:
《Redis設計與實現》黃健巨集著,網上對Redis的詳解等
此部落格為筆者使用redis很久之後,參考網路上各類文章總結性書寫,原創手打,如有錯誤歡迎指正。