1. 簡介
redis是一個key-value儲存系統。與memcached一樣,為了保證效率,資料都是快取在記憶體中。區別的是redis會週期性的把更新的資料寫入磁碟或者把修改操作寫入追加的記錄檔案,並且在此基礎上實現了master-slave同步。
1.1 資料結構
Redis可以儲存鍵與5種不同資料結構型別之間的對映,這5種資料結構型別分別為String(字串)、List(列表)、Set(集合)、Hash(雜湊)和 Zset(有序集合)。
這些資料型別都支援push/pop、add/remove及取交集並集和差集及更豐富的操作,而且這些操作都是原子性的。在此基礎上,redis支援各種不同方式的排序。
1.2 為什麼快
- 完全基於記憶體,絕大部分請求是純粹的記憶體操作,非常快速。資料存在記憶體中,類似於HashMap,HashMap的優勢就是查詢和操作的時間複雜度都是O(1);
- 資料結構簡單,對資料操作也簡單,Redis中的資料結構是專門進行設計的;
- 採用單程式單執行緒(這也解釋為什麼redis可以處理高併發問題),避免了不必要的上下文切換和競爭條件,也不存在多程式或者多執行緒導致的切換而消耗 CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因為可能出現死鎖而導致的效能消耗;
- 使用多路I/O複用模型;
- 使用底層模型不同,它們之間底層實現方式以及與客戶端之間通訊的應用協議不一樣,Redis直接自己構建了VM 機制 ,因為一般的系統呼叫系統函式的話,會浪費一定的時間去移動和請求;
2. 安裝
redis 安裝很簡單,只要下載好,直接執行即可。官方下載地址
這裡主要講下docker-compose安裝
docker-compose.yaml
配置資訊如下
version: '3.7'
services:
redis:
# 容器名稱
container_name: redis
# 映象 預設redis:latest
image: redis
# 自啟動
restart: always
# 覆蓋dockerfile中的cmd名稱
# 啟動redis並開啟AOF模式
command: ["redis-server", "--appendonly", "yes"]
# 宿主機埠6379:容器預設埠6379
ports:
- "6379:6379"
# 檔案對映
volumes:
- ./data:/data
我們可以使用redis桌面工具Redis Desktop Manager
連線安裝啟動好的redis server進行測試。
建立一個新的連結,因為安裝的時候並沒有開啟認證,所以僅需要輸入地址和埠即可。
3. 常用命令
由於redis命令太多了 這裡主要講一下常用的命令。
我們可以隨便進一個database進行練習
3.1 keys
序號 | 命令 | 描述 |
---|---|---|
1 | DEL key | 該命令用於在 key 存在時刪除 key。 |
2 | EXISTS key | 檢查給定 key 是否存在。 |
3 | EXPIRE key seconds | 為給定 key 設定過期時間,以秒計。 |
4 | EXPIREAT key timestamp | 為 key 設定過期時間。 不同在於 EXPIREAT 命令接受的時間引數是 UNIX 時間戳(unix timestamp)。 |
5 | PEXPIRE key milliseconds | 設定 key 的過期時間以毫秒計。 |
6 | PEXPIREAT key milliseconds-timestamp | 設定 key 過期時間的時間戳(unix timestamp) 以毫秒計7 |
7 | KEYS pattern | 查詢所有符合給定模式( pattern)的 key 。 |
8 | MOVE key db | 將當前資料庫的 key 移動到給定的資料庫 db 當中。 |
9 | PERSIST key | 移除 key 的過期時間,key 將持久保持。 |
10 | PTTL key | 以毫秒為單位返回 key 的剩餘的過期時間。 |
11 | TTL key | 以秒為單位,返回給定 key 的剩餘生存時間(TTL, time to live)。 |
12 | RENAME key newkey | 修改 key 的名稱 |
13 | TYPE key | 返回 key 所儲存的值的型別。 |
3.2 string
序號 | 命令 | 描述 |
---|---|---|
1 | SET key value | 設定指定 key 的值 |
2 | GET key | 獲取指定 key 的值。 |
3 | GETRANGE key start end | 返回 key 中字串值的子字元 |
4 | GETSET key value | 將給定 key 的值設為 value ,並返回 key 的舊值(old value)。 |
5 | MGET key1 key2.. | 獲取所有(一個或多個)給定 key 的值。 |
6 | SETEX key seconds value | 將值 value 關聯到 key ,並將 key 的過期時間設為 seconds (以秒為單位)。 |
7 | SETNX key value | 只有在 key 不存在時設定 key 的值。 |
8 | STRLEN key | 返回 key 所儲存的字串值的長度。 |
9 | MSET key value [key value ...] | 同時設定一個或多個 key-value 對。 |
10 | PSETEX key milliseconds value | 這個命令和 SETEX 命令相似,但它以毫秒為單位設定 key 的生存時間,而不是像 SETEX 命令那樣,以秒為單位。 |
11 | INCR key | 將 key 中儲存的數字值增一。 |
12 | INCRBY key increment | 將 key 所儲存的值加上給定的增量值(increment)。 |
13 | INCRBYFLOAT key increment | 將 key 所儲存的值加上給定的浮點增量值(increment) 。 |
14 | DECR key | 將 key 中儲存的數字值減一。 |
15 | DECRBY key decrement | key 所儲存的值減去給定的減量值(decrement) 。 |
3.3 hash
序號 | 命令 | 描述 |
---|---|---|
1 | HDEL key field1 [field2] | 刪除一個或多個雜湊表欄位 |
2 | HEXISTS key field | 檢視雜湊表 key 中,指定的欄位是否存在。 |
3 | HGET key field | 獲取儲存在雜湊表中指定欄位的值。 |
4 | HGETALL key | 獲取在雜湊表中指定 key 的所有欄位和對應的值 |
5 | HINCRBY key field increment | 為雜湊表 key 中的指定欄位的整數值加上增量 increment 。 |
6 | HINCRBYFLOAT key field increment | 為雜湊表 key 中的指定欄位的浮點數值加上增量 increment 。 |
7 | HKEYS key | 獲取所有雜湊表中的欄位 |
8 | HLEN key | 獲取雜湊表中欄位的數量 |
9 | HMGET key field1 [field2] | 獲取所有給定欄位的值 |
10 | HMSET key field1 value1 [field2 value2 ] | 同時將多個 field-value (域-值)對設定到雜湊表 key 中。 |
11 | HSET key field value | 將雜湊表 key 中的欄位 field 的值設為 value 。 |
12 | HSETNX key field value | 只有在欄位 field 不存在時,設定雜湊表欄位的值。 |
13 | HVALS key | 獲取雜湊表中所有值。 |
3.4 list
序號 | 命令及描述 | 描述 |
---|---|---|
1 | BLPOP key1 [key2 ] timeout | 移出並獲取列表的第一個元素, 如果列表沒有元素會阻塞列表直到等待超時或發現可彈出元素為止。 |
2 | BRPOP key1 [key2 ] timeout | 移出並獲取列表的最後一個元素, 如果列表沒有元素會阻塞列表直到等待超時或發現可彈出元素為止。 |
3 | LINDEX key index] | 通過索引獲取列表中的元素 |
4 | LINSERT key BEFORE|AFTER pivot value | 在列表的元素前或者後插入元素 |
5 | LLEN key | 獲取列表長度 |
6 | LPOP key | 移出並獲取列表的第一個元素 |
7 | LPUSH key value1 [value2] | 將一個或多個值插入到列表頭部 |
8 | LPUSHX key value | 將一個值插入到已存在的列表頭部 |
9 | LRANGE key start stop | 獲取列表指定範圍內的元素 |
10 | LREM key count value | 移除列表元素 |
11 | LSET key index value | 通過索引設定列表元素的值 |
12 | LTRIM key start stop | 對一個列表進行修剪(trim),就是說,讓列表只保留指定區間內的元素,不在指定區間之內的元素都將被刪除。 |
13 | RPOP key | 移除列表的最後一個元素,返回值為移除的元素。 |
14 | RPUSH key value1 [value2] | 在列表中新增一個或多個值 |
15 | RPUSHX key value | 為已存在的列表新增值 |
3.5 set
序號 | 命令 | 描述 |
---|---|---|
1 | SADD key member1 [member2] | 向集合新增一個或多個成員 |
2 | SCARD key | 獲取集合的成員數 |
3 | SDIFF key1 [key2] | 返回第一個集合與其他集合之間的差異。 |
4 | SDIFFSTORE destination key1 [key2] | 返回給定所有集合的差集並儲存在 destination 中 |
5 | SINTER key1 [key2] | 返回給定所有集合的交集 |
6 | SINTERSTORE destination key1 [key2] | 返回給定所有集合的交集並儲存在 destination 中 |
7 | SISMEMBER key member | 判斷 member 元素是否是集合 key 的成員 |
8 | SMEMBERS key | 返回集合中的所有成員 |
9 | SPOP key | 移除並返回集合中的一個隨機元素 |
10 | SRANDMEMBER key [count] | 返回集合中一個或多個隨機數 |
11 | SREM key member1 [member2] | 移除集合中一個或多個成員 |
12 | SUNION key1 [key2] | 返回所有給定集合的並集 |
13 | SUNIONSTORE destination key1 [key2] | 所有給定集合的並集儲存在 destination 集合中 |
3.6 sorted set
序號 | 命令 | 描述 |
---|---|---|
1 | ZADD key score1 member1 [score2 member2] | 向有序集合新增一個或多個成員,或者更新已存在成員的分數 |
2 | ZCARD key | 獲取有序集合的成員數 |
3 | ZCOUNT key min max | 計算在有序集合中指定區間分數的成員數 |
4 | ZINCRBY key increment member | 有序集合中對指定成員的分數加上增量 increment |
5 | ZINTERSTORE destination numkeys key [key ...] | 計算給定的一個或多個有序集的交集並將結果集儲存在新的有序集合 destination 中 |
6 | ZLEXCOUNT key min max | 在有序集合中計算指定字典區間內成員數量 |
7 | ZRANGE key start stop [WITHSCORES] | 通過索引區間返回有序集合指定區間內的成員 |
8 | ZRANGEBYLEX key min max [LIMIT offset count] | 通過字典區間返回有序集合的成員 |
9 | ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] | 通過分數返回有序集合指定區間內的成員 |
10 | ZRANK key member | 返回有序集合中指定成員的索引 |
11 | ZREM key member [member ...] | 移除有序集合中的一個或多個成員 |
12 | ZREMRANGEBYLEX key min max | 移除有序集合中給定的字典區間的所有成員 |
13 | ZREMRANGEBYRANK key start stop | 移除有序集合中給定的排名區間的所有成員 |
14 | ZREMRANGEBYSCORE key min max | 移除有序集合中給定的分數區間的所有成員 |
15 | ZREVRANGE key start stop [WITHSCORES] | 返回有序集中指定區間內的成員,通過索引,分數從高到低 |
16 | ZREVRANGEBYSCORE key max min [WITHSCORES] | 返回有序集中指定分數區間內的成員,分數從高到低排序 |
17 | ZREVRANK key member | 返回有序集合中指定成員的排名,有序整合員按分數值遞減(從大到小)排序 |
18 | ZSCORE key member | 返回有序集中,成員的分數值 |
19 | ZUNIONSTORE destination numkeys key [key ...] | 計算給定的一個或多個有序集的並集,並儲存在新的 key 中 |
20 | ZSCAN key cursor [MATCH pattern] [COUNT count] | 迭代有序集合中的元素(包括元素成員和元素分值) |
4. key過期策略
key 以兩種方式過期:被動方式(惰性過期)和主動方式(定期過期)。
4.1 惰性過期
只有當訪問一個key時,才會判斷該key是否已過期,過期則清除。該策略可以最大化地節省CPU資源,卻對記憶體非常不友好。極端情況可能出現大量的過期key沒有再次被訪問,從而不會被清除,佔用大量記憶體。
4.2 定期過期
redis會每隔一定的時間,從expires字典中掃描一定數量的key,並清除其中已過期的key。通過調整定時掃描的時間間隔和每次掃描的限定耗時,可以在不同情況下使得CPU和記憶體資源達到最優的平衡效果。
Redis 會定期在具有過期設定的key中隨機測試一些鍵。所有已經過期的key都從key空間中刪除。
具體來說,Redis 每秒執行 10 次(100ms一次):
- 從具有關聯過期的key集中測試 20 個隨機key。
- 刪除所有發現過期的key。
- 如果超過 25% 的key已過期,將從步驟 1 重新開始。
這是一個平凡的概率演算法,基本上假設是我們的樣本代表整個金鑰空間,我們繼續過期直到可能過期的金鑰百分比低於25%
這意味著在任何給定時刻,使用記憶體的已過期鍵的最大數量最大等於每秒最大寫入運算元量除以 4。
5. 驅逐key
當 Redis 用作快取時,通常可以方便地讓它在您新增新資料時自動驅逐舊資料。
LRU (Least Recently Used)實際上只是支援的驅逐方法之一。 Redis 使用的 LRU 演算法,實際上是精確 LRU 的近似值。
從 Redis 4.0 版開始,引入了新的 LFU(Least Frequently Used)驅逐策略。
5.1 配置指令
我們可以通過redis命令檢視當前的驅逐策略,可以看出當前預設的驅逐策略為noeviction
127.0.0.1:6379> config get maxmemory-policy
1) "maxmemory-policy"
2) "noeviction"
檢視當前的maxmemory
設定,可以看出當前的設定為0
,相當於沒有限制
config get maxmemory
1) "maxmemory"
2) "0"
當然,如果要設定這些引數,可以使用命令或者直接修改配置檔案redis.conf
例如,為了配置 100 兆位元組的記憶體限制,可以在redis.conf
檔案中使用以下指令。
maxmemory 100mb
5.2 驅逐策略
-
noeviction
:當記憶體使用超過配置的時候會返回錯誤,不會驅逐任何key。 -
allkeys-lru
:通過首先嚐試刪除最近較少使用的 (LRU) key,以便為新增的新資料騰出空間。 -
volatile-lru
:通過首先嚐試刪除最近較少使用的 (LRU)key,但僅限於具有過期時間的key,以便為新增的新資料騰出空間。 -
allkeys-random
:隨機驅逐key以便為新增的新資料騰出空間。 -
volatile-random
:隨機驅逐key以便為新增的新資料騰出空間,但僅驅逐具有過期時間的key。 -
volatile-ttl
:驅逐具有過期時間的key,並嘗試首先驅逐具有較短生存時間(TTL)的key,以便為新增的新資料騰出空間。 -
allkeys-lfu
:通過首先嚐試刪除使用頻率最少的key,以便為新增的新資料騰出空間。 -
volatile-lfu
:通過首先嚐試刪除使用頻率最少的key,但僅限於具有過期時間的key,以便為新增的新資料騰出空間。
7和8從 Redis 4.0 版開始引入
如何抉擇:
如果沒有與先決條件匹配的驅逐鍵,則策略volatile-lru、volatile-random和volatile-ttl 的行為類似於noeviction。
根據應用程式的訪問模式選擇正確的驅逐策略很重要,但是您可以在應用程式執行時在執行時重新配置策略,並使用 Redis INFO輸出監控快取未命中和命中的數量以調整您的設定.
一般來說,作為一個經驗法則:
- 當期望請求的流行度呈冪律分佈時,請使用allkeys-lru策略,也就是說,您期望元素子集的訪問頻率遠高於其他元素。如果您不確定,這是一個不錯的選擇。
- 如果您有一個迴圈訪問,其中所有的鍵都被連續掃描,或者當您希望分佈均勻(所有元素可能以相同的概率訪問)時,請使用allkeys-random。
- 如果您希望能夠通過在建立快取物件時使用不同的 TTL 值來向 Redis 提供關於什麼是到期的良好候選者的提示,請使用volatile-ttl。
該volatile-lru和volatile-random,當你想用一個例項兩個快取,並擁有一套永久鍵的策略是有用的。然而,執行兩個 Redis 例項來解決這樣的問題通常是一個更好的主意。
還值得注意的是,為鍵設定過期會消耗記憶體,因此使用像allkeys-lru這樣的策略會提高記憶體效率,因為無需為鍵在記憶體壓力下被逐出設定過期。
5.3 驅逐程式如何運作
重要的是要了解驅逐過程的工作方式如下:
- 客戶端執行新命令,導致新增更多資料。
- Redis 檢查記憶體使用情況,如果大於
maxmemory
限制,則根據策略驅逐鍵。 - 執行新命令,等等。
所以我們不斷地越過記憶體限制的邊界,越過它,然後通過驅逐鍵返回到限制之下。
如果某個命令導致使用大量記憶體(例如儲存到新金鑰中的大集合交集)一段時間,則記憶體限制可能會明顯超出。
5.4 新生KEY策略
另外一個問題是,當建立新物件的時候,物件的使用計數如果為0,很容易就會被淘汰掉(LFU),還需要為新生key設定一個初始counter。counter會被初始化為LFU_INIT_VAL,預設5。
6. 永續性
Redis 提供了一系列不同的永續性選項:
- RDB(Redis Database):RDB 永續性以指定的時間間隔執行資料集的時間點快照。
- AOF(Append Only File):AOF 持久化記錄伺服器收到的每個寫操作,在伺服器啟動時會再次播放,重建原始資料集。命令使用與 Redis 協議本身相同的格式以僅附加的方式記錄。當日志變得太大時,Redis 能夠在後臺重寫日誌。
- 無永續性:如果希望資料在伺服器執行時一直存在,您可以完全禁用永續性。
- RDB + AOF:可以在同一個例項中組合 AOF 和 RDB。請注意,在這種情況下,當 Redis 重新啟動時,AOF 檔案將用於重建原始資料集,因為它保證是最完整的。
要理解的最重要的事情是 RDB 和 AOF 永續性之間的不同權衡。讓我們從 RDB 開始:
6.1 RDB優勢
- RDB 是 Redis 資料的非常緊湊的單檔案時間點表示。RDB 檔案非常適合備份。例如,您可能希望在最近 24 小時內每小時存檔一次 RDB 檔案,並在 30 天內每天儲存一個 RDB 快照。這使您可以在發生災難時輕鬆恢復不同版本的資料集。
- RDB 非常適合災難恢復,它是一個可以傳輸到遠端資料中心或 Amazon S3(可能已加密)的緊湊檔案。
- RDB 最大限度地提高了 Redis 的效能,因為 Redis 父程式為了持久化需要做的唯一工作是派生一個將完成所有其餘工作的子程式。父例項永遠不會執行磁碟 I/O 或類似操作。
- 與 AOF 相比,RDB 允許更快地重新啟動大資料集。
- 在副本上,RDB 支援重啟和故障轉移後的部分重新同步。
6.2 RDB 的缺點
- 如果您需要在 Redis 停止工作(例如斷電後)時將資料丟失的可能性降至最低,那麼 RDB 並不好。您可以在生成 RDB 的地方配置不同的儲存點(例如,在對資料集進行至少 5 分鐘和 100 次寫入之後,但您可以有多個儲存點)。但是,您通常會每五分鐘或更長時間建立一個 RDB 快照,因此,如果 Redis 因任何原因在沒有正確關閉的情況下停止工作,您應該準備好丟失最近幾分鐘的資料。
- RDB 經常需要 fork() 以便使用子程式在磁碟上持久化。如果資料集很大,Fork() 可能會很耗時,如果資料集很大且 CPU 效能不是很好,可能會導致 Redis 停止為客戶端服務幾毫秒甚至一秒鐘。AOF 也需要 fork() 但你可以調整你想要重寫日誌的頻率,而不會對永續性進行任何權衡。
6.3 AOF優勢
- 使用 AOF Redis 更持久:你可以有不同的 fsync 策略:根本沒有 fsync,每秒 fsync,每次查詢 fsync。使用 fsync 每秒寫入效能的預設策略仍然很棒(fsync 是使用後臺執行緒執行的,當沒有 fsync 正在進行時,主執行緒將努力執行寫入。)但您只能丟失一秒鐘的寫入。
- AOF 日誌是僅附加日誌,因此在斷電時不會出現尋道或損壞問題。即使日誌由於某種原因(磁碟已滿或其他原因)以半寫命令結束,redis-check-aof 工具也能夠輕鬆修復它。
- 當 AOF 變得太大時,Redis 能夠在後臺自動重寫。重寫是完全安全的,因為當 Redis 繼續追加到舊檔案時,會使用建立當前資料集所需的最少操作集生成一個全新的檔案,一旦第二個檔案準備就緒,Redis 就會切換這兩個檔案並開始追加到新的那一個。
- AOF 以易於理解和解析的格式包含所有操作的日誌。您甚至可以輕鬆匯出 AOF 檔案。例如,即使您不小心使用FLUSHALL命令重新整理了所有內容,只要在此期間沒有重寫日誌,您仍然可以通過停止伺服器、刪除最新命令並重新啟動 Redis 來再次儲存您的資料集。
6.4 AOF的缺點
- AOF 檔案通常比相同資料集的等效 RDB 檔案大。
- AOF 可能比 RDB 慢,具體取決於確切的 fsync 策略。一般來說,將 fsync 設定為每秒效能仍然非常高,並且禁用 fsync 後,即使在高負載下,它也應該與 RDB 一樣快。即使在寫入負載巨大的情況下,RDB 仍然能夠提供更多關於最大延遲的保證。
- 過去我們在特定命令中遇到過罕見的錯誤(例如有一個涉及阻塞命令的錯誤,如BRPOPLPUSH) 導致生成的 AOF 在重新載入時無法重現完全相同的資料集。這些錯誤很少見,我們在測試套件中進行了測試,自動建立隨機複雜資料集並重新載入它們以檢查一切正常。但是,使用 RDB 永續性幾乎不可能出現此類錯誤。為了更清楚地說明這一點:Redis AOF 通過增量更新現有狀態來工作,就像 MySQL 或 MongoDB 那樣,而 RDB 快照一次又一次地從頭開始建立所有內容,這在概念上更加健壯。但是 - 1)需要注意的是,每次Redis重寫AOF時,都是從資料集中包含的實際資料開始重新建立,與始終附加的 AOF 檔案(或重寫讀取舊 AOF 而不是讀取記憶體中的資料)相比,對錯誤的抵抗力更強。2) 我們從未收到過使用者關於在現實世界中檢測到的 AOF 損壞的單一報告。
6.5 如何抉擇?
一般的跡象是,如果您想要與 PostgreSQL 可以提供的資料安全程度相當的資料安全性,則應該同時使用這兩種永續性方法。
如果您非常關心您的資料,但在發生災難時仍然可以忍受幾分鐘的資料丟失,您可以簡單地單獨使用 RDB。
有很多使用者單獨使用 AOF,但我們不鼓勵它,因為不時擁有 RDB 快照對於進行資料庫備份、更快地重新啟動以及在 AOF 引擎中出現錯誤時是一個好主意。
注意:由於所有這些原因,我們很可能在未來將 AOF 和 RDB 統一為一個持久化模型(長期計劃)。
以下部分將說明有關這兩種永續性模型的更多細節。
6.6 RDB如何運作的
預設情況下,Redis 將資料集的快照儲存在磁碟上的一個名為dump.rdb
. 如果資料集中至少有 M 次更改,您可以將 Redis 配置為每 N 秒儲存一次資料集,或者您可以手動呼叫SAVE或BGSAVE命令。
例如,如果至少有 1000 個鍵更改,此配置將使 Redis 每 60 秒自動將資料集轉儲到磁碟:
save 60 1000
這種策略被稱為快照。
具體操作步驟如下:
每當 Redis 需要將資料集轉儲到磁碟時,就會發生以下情況:
- Redisforks。我們現在有一個子程式和一個父程式。
- 子程式開始將資料集寫入臨時 RDB 檔案。
- 當子程式寫完新的 RDB 檔案時,它會替換舊的。
這種方法允許 Redis 從寫時複製語義中受益。
6.7 AOF 如何運作的
快照不是很持久。如果您執行Redis的計算機停止執行,您的電源線發生故障,或者您不小心kill -9
您的例項,Redis上寫入的最新資料將丟失。雖然這對於某些應用程式來說可能不是什麼大問題,但存在完全永續性的用例,在這些情況下,Redis 不是一個可行的選擇。
該只追加檔案是Redis的選擇,完全耐用的策略。它在 1.1 版中可用。
你可以在你的配置檔案中開啟AOF:
appendonly yes
從現在開始,每次 Redis 收到更改資料集的命令(例如SET)時,它都會將其附加到 AOF 中。當您重新啟動 Redis 時,它將重新播放 AOF 以重建狀態。
6.7.1 日誌重寫
您可以猜到,隨著寫入操作的執行,AOF 變得越來越大。例如,如果您將一個計數器遞增 100 次,您最終將在資料集中得到一個包含最終值的鍵,但在 AOF 中有 100 個條目。重建當前狀態不需要這些條目中的 99 個。
所以Redis支援了一個有趣的特性:它能夠在不中斷對客戶端的服務的情況下在後臺重建AOF。每當您發出BGREWRITEAOF 時, Redis 都會寫入在記憶體中重建當前資料集所需的最短命令序列。如果您在 Redis 2.2 中使用 AOF,則需要不時執行BGREWRITEAOF。Redis 2.4 能夠自動觸發日誌重寫(更多資訊請參見 2.4 示例配置檔案)。
6.7.2 僅追加檔案的永續性如何?
您可以配置 Redis 將 fsync
資料在磁碟上的次數。共有三個選項:
appendfsync always
:fsync
每次將新命令附加到 AOF 時。非常非常緩慢,非常安全。請注意,在執行來自多個客戶端或管道的一批命令之後,這些命令會附加到 AOF,因此這意味著單個寫入和單個 fsync(在傳送回覆之前)。appendfsync everysec
:fsync
每一秒。足夠快(在 2.4 中可能和快照一樣快),如果發生災難,您可能會丟失 1 秒的資料。appendfsync no
:永遠不要fsync
,只需將您的資料交到作業系統的手中。更快、更不安全的方法。通常 Linux 會使用這種配置每 30 秒重新整理一次資料,但這取決於核心的精確調整。
建議(和預設)策略是fsync
每秒。它既非常快又非常安全。該always
策略在實踐中很慢,但它支援組提交,因此如果有多個並行寫入,Redis 會嘗試執行單個fsync
操作。
6.7.3 如果我的 AOF 被截斷,我該怎麼辦?
有可能是伺服器在寫入AOF檔案時崩潰,或者寫入時儲存AOF檔案的卷已滿。發生這種情況時,AOF 仍包含表示資料集給定時間點版本的一致資料(使用預設 AOF fsync 策略,該版本可能長達一秒),但 AOF 中的最後一個命令可能會被截斷。Redis 的最新主要版本無論如何都可以載入 AOF,只需丟棄檔案中最後一個格式不正確的命令。在這種情況下,伺服器將發出如下日誌:
* Reading RDB preamble from AOF file...
* Reading the remaining AOF tail...
# !!! Warning: short read while loading the AOF file !!!
# !!! Truncating the AOF at offset 439 !!!
# AOF loaded anyway because aof-load-truncated is enabled
如果需要,您可以更改預設配置以強制 Redis 在這種情況下停止,但預設配置是繼續,無論檔案中的最後一個命令格式不正確,以保證重啟後的可用性。
舊版本的 Redis 可能無法恢復,可能需要執行以下步驟:
-
製作 AOF 檔案的備份副本。
-
使用
redis-check-aof
Redis附帶的工具修復原始檔案:$ redis-check-aof --fix
-
可選地用於
diff -u
檢查兩個檔案之間的區別。 -
使用固定檔案重新啟動伺服器。
6.7.4 如果我的 AOF 損壞了,我該怎麼辦?
如果 AOF 檔案不僅被截斷,而且被中間的無效位元組序列損壞,事情就會變得更加複雜。Redis 會在啟動時抱怨並中止:
* Reading the remaining AOF tail...
# Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>
最好的辦法是執行該redis-check-aof
實用程式,最初沒有--fix
選項,然後瞭解問題,在檔案中給定的偏移處跳轉,看看是否可以手動修復檔案:AOF 使用相同的格式Redis 協議,手動修復非常簡單。否則有可能讓實用程式為我們修復檔案,但在這種情況下,從無效部分到檔案末尾的所有 AOF 部分可能會被丟棄,如果損壞發生,將導致大量資料丟失在檔案的初始部分。
AOF具體操作如下:
日誌重寫使用已用於快照的相同的寫時複製技巧。這是它的工作原理:
- Redis fork,所以現在我們有一個子程式和一個父程式。
- 子程式開始在臨時檔案中寫入新的 AOF。
- 父程式在記憶體緩衝區中累積所有新更改(但同時它將新更改寫入舊的僅附加檔案中,因此如果重寫失敗,我們是安全的)。
- 當子程式完成重寫檔案時,父程式收到一個訊號,並將記憶體緩衝區附加到子程式生成的檔案的末尾。
- 現在 Redis 原子地將舊檔案重新命名為新檔案,並開始將新資料附加到新檔案中。