Redis&持久化存儲

Redis支持的數據類型:

string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。前端

string類型是Redis最基本的數據類型,一個鍵最大能存儲512MB。web

  1. 能表達三種類型:字符串、整數和浮點數。根據場景相互間自動轉換,而且根據須要選取底層的承載方式
  2. value內部以int、sds做爲結構存儲。int存放整型數據,sds存放字節/字符串和浮點型數據
  3. sds內部結構: 
    • 用buf數組存儲字符串的內容,但數組的長度會大於所存儲內容的長度。會有一格專門存放」\0」(C標準庫)做爲結尾,還有預留多幾個空的(即free區域),當append字符串的長度小於free區域,則sds不會從新申請內存,直接使用free區域
    • 擴容:當對字符串的操做完成後預期的串長度小於1M時,擴容後的buf數組大小=預期長度*2+1;若大於1M,則buf老是會預留出1M的free空間
    • value對象一般具備兩個內存部分:redisObject部分和redisObject的ptr指向的sds部分。建立value對象時,一般須要爲redisObject和sds申請兩次內存。單對於短小的字符串,能夠把二者連續存放,因此能夠一次性把二者的內存一塊兒申請了

 

hash(map) 是一個鍵值對集合,特別適合用於存儲對象。每一個 hash 能夠存儲 2的32 - 1次方 鍵值對(40多億)。redis

  1. map又叫hash。map內部的key和value不能再嵌套map了,只能是string類型:整形、浮點型和字符串
  2. map主要由hashtable和ziplist兩種承載方式實現,對於數據量較小的map,採用ziplist實現
  3. hashtable內部結構 
    • 主要分爲三層,自底向上分別是dictEntry、dictht、dict
    • dictEntry:管理一個key-value對,同時保留同一個桶中相鄰元素的指針,一次維護哈希桶的內部連
    • dictht:維護哈希表的全部桶鏈
    • dict:當dictht須要擴容/縮容時,用於管理dictht的遷移
    • 哈希表的核心結構是dictht,它的table字段維護着hash桶,它是一個數組,每一個元素指向桶的第一個元素(dictEntry)
    • set值的流程:先經過MurmurHash算法求出key的hash值,再對桶的個數取模,獲得key對應的桶,再進入桶中,遍歷所有entry,斷定是否已有相同的key,若是沒有,則將新key對應的鍵值對插入到桶頭,而且更新dictht的used數量,used表示hash表中已經存了多少元素。因爲每次插入都要遍歷hash桶中的所有entry,因此當桶中entry不少時,性能會線性降低
    • 擴容:經過負載因子斷定是否須要增長桶數。負載因子=哈希表中已有元素/哈希桶數的比值。有兩個閾值,小於1必定不擴容;大於5必定擴容。擴容時新的桶數目是現有桶的2n倍
    • 縮容:負載因子的閾值是0.1
    • 擴/縮容經過新建哈希表的方式實現。即擴容時,會並存兩個哈希表,一個是源表,一個是目標表。經過將源表的桶逐步遷移到目標表,以數據遷移的方式實現擴容,遷移完成後目標表覆蓋源表。遷移過程當中,首先訪問源表,若是發現key對應的源表桶已完成遷移,則從新訪問目標表,不然在源表中操做
    • redis是單線程處理請求,遷移和訪問的請求在相同線程內進行,因此不會存在併發性問題

 

list 列表是簡單的字符串列表,按照插入順序進行排序,能夠添加一個元素導列表的頭部(左邊)或者尾部(右邊)。算法

  1. list類型的value對象內部以linkedlist或ziplist承載。當list的元素個數和單個元素的長度較小時,redis會採用ziplist實現以減小內存佔用,不然採用linkedlist結構
  2. linkedlist內部實現是雙向鏈表。在list中定義了頭尾元素指針和列表的長度,是的pop/push操做、llen操做的複雜度爲O(1)。因爲是鏈表,lindex類的操做複雜度仍然是O(N)
  3. ziplist的內部結構 
    • 全部內容被放置在連續的內存中。其中zlbytes表示ziplist的總長度,zltail指向最末元素,zllen表示元素個數,entry表示元素自身內容,zlend做爲ziplist定界符
    • rpush、rpop、llen,複雜度爲O(1);lpush/pop操做因爲涉及全列表元素的移動,複雜度爲O(N)

 

set 集合是string類型的無需集合,經過哈希表實現,添加,刪除,查找的複雜度都是O(1)。若插入相同元素時,第二次的插入將被忽略。數據庫

  1. set以intset或hashtable來存儲。hashtable中的value永遠爲null,當set中只包含整數型的元素時,則採用intset
  2. intset的內部結構 
    • 核心元素是一個字節數組,從小到大有序存放着set的元素
    • 因爲元素有序排列,因此set的獲取操做採用二分查找方式實現,複雜度O(log(N))。進行插入時,首先經過二分查找獲得本次插入的位置,再對元素進行擴容,再將預計插入位置以後的全部元素向右移動一個位置,最後插入元素,插入複雜度爲O(N)。刪除相似

 

zset 有序集合,也是string類型元素的集合,且不容許重複的成員。vim

  1. 相似map是一個key-value對,可是有序的。value是一個浮點數,稱爲score,內部是按照score從小到大排序
  2. 內部結構以ziplist或skiplist+hashtable來實現

 

 

Redis是什麼

Redis是一個支持網絡、基於內存、也可持久性的鍵值對存儲數據庫。和Memcached相似,但它支持存儲的value類型相對更多,包括string(字符串)、list(列表)、set(集合)、zset(sorted set --有序集合)和hash(哈希類型)。這些數據類型都支持push/pop、add/remove、取交集並集和差集等其餘操做,並且這些操做都是原子性的,此外Redis中單個value的最大限制是1GB,不像 memcached只能保存1MB的數據。 Redis 整個數據庫統統加載在內存當中進行操做,按期經過異步操做把數據庫數據flush到硬盤上進行保存。由於是純內存操做,Redis每秒能夠處理超過10萬次讀操做 8萬次寫操做,是已知性能最快的Key-Value DB。所以Redis能夠用來實現不少有用的功能,比方說利用List實現一個FIFO雙向鏈表,可做爲一個輕量級的高性能消息隊列服務,利用Set能夠作高性能的tag系統等等。另外Redis也能夠對存入的Key-Value設置expire時間,所以也能夠被看成一個功能增強版的memcached來用。
Redis支持主從同步。數據能夠從主服務器向任意數量的從服務器上同步,從服務器能夠是關聯其餘從服務器的主服務器。這使得Redis可執行單層樹複製。同步對讀取操做的可擴展性和數據冗餘頗有幫助。redis協議位於TCP層之上,即客戶端和redis實例保持雙工的鏈接,交互的都是序列化後的協議數據。

MemcacheDB是一個分佈式、key-value形式的持久存儲系統。它不是一個緩存組件,而是一個基於對象存取的、可靠的、快速的持久存儲引擎後端

Redis的主要缺點是數據庫容量受到物理內存的限制,不能用做海量數據的高性能讀寫,所以Redis適合的場景主要侷限在較小數據量的高性能操做和運算上。數組

 

用redis實現分佈式鎖緩存

  1. 主要使用的命令: 
    • setnx key val。當且僅當key不存在時,set一個key爲val的字符串,返回1;若key存在,則什麼都不作,返回0。
    • expire key timeout。爲key設置一個超時時間,單位爲second,超過這個時間鎖會自動釋放,避免死鎖。
    • delete key。刪除鎖
  2. 實現思想: 
    • 使用setnx加鎖,若是返回1,則說明加鎖成功,並設置超時時間,避免系統掛了,鎖無法釋放。在finally中delete刪除鎖釋放。
    • 若是須要設置超時等待時間,則能夠加個while循環,在獲取不到鎖的狀況下,進行循環獲取鎖,超時了則退出。

 

緩存算法: 安全

  • FIFO算法:First in First out,先進先出。原則:一個數據最早進入緩存中,則應該最先淘汰掉。也就是說,當緩存滿的時候,應當把最早進入緩存的數據給淘汰掉。
  • LFU算法:Least Frequently Used,最不常用算法。
  • LRU算法:Least Recently Used,近期最少使用算法。
  • LRU和LFU的區別。LFU算法是根據在一段時間裏數據項被使用的次數選擇出最少使用的數據項,即根據使用次數的差別來決定。而LRU是根據使用時間的差別來決定的。

 

回收策略: 

 

  • volatile-lru從已設置過時時間的數據集中挑選最近最少使用的數據進行淘汰,Redis回收那些超時的(僅僅是超時的)鍵值對,即:它只淘汰那些超時的鍵值對。

  • allkeys-lru從數據集中挑選最近最少使用的數據進行淘汰,淘汰最近最少使用的key-value,Redis對全部(不只僅是超時的)的鍵值對採用最近最少使用的淘汰策略

  • volatile-lfu淘汰最近最不經常使用的key-value,Redis淘汰在必定時期內被訪問次數最少的超時鍵值對。

  • allkeys-lfu淘汰最近最不經常使用的key-value,Redis對全部的鍵值對採用最近最不經常使用的淘汰策略

  • volatile-random從已設置過時時間的數據集中任意選擇數據進行淘汰採用隨機淘汰策略刪除超時的鍵值對。

  • allkeys-random從數據集中任意選擇數據進行淘汰。採用隨機淘汰策略刪除全部的鍵值對,這個策略不經常使用。

  • volatile-ttl從已設置過時時間的數據集中挑選將要過時的數據進行淘汰採用刪除存活時間最短的鍵值對策略。

  • noeviction:禁止驅逐數據,不淘汰任何鍵值對。當內存達到限制的最大內存而且客戶端還在繼續執行會佔用內存的命令時,會返回錯誤。當內存滿時,若是進行讀操做,例如get命令它將正常工做,而作寫操做它將返回錯誤。即:當Redis用這個策略內存達到最大的時候,它就只能讀不能寫了。

Redis默認採用noeviction策略。

通常的經驗規則:

  • 使用allkeys-lru策略:當你但願你的請求符合一個冪定律分佈,也就是說,你但願部分的子集元素將比其它其它元素被訪問的更多。若是你不肯定選擇什麼,這是個很好的選擇。
  • 使用allkeys-random若是你是循環訪問,全部的鍵被連續的掃描,或者你但願請求分佈正常(全部元素被訪問的機率都差很少)。
  • 使用volatile-ttl若是你想要經過建立緩存對象時設置TTL值,來決定哪些對象應該被過時。

 

緩存雪崩多是由於數據未加載到緩存中,或者在設置緩存採用了相同的過時時間,致使緩存在某一時刻同時大面積失效,從而致使全部請求都轉發到數據庫去查詢,致使數據庫CPU和內存負載太高,甚至宕機。

  解決思路: 

  • 線程互斥:只讓一個線程構建緩存,其餘線程等待構建緩存的線程執行完,從新從緩存獲取數據才能夠,每一個時刻只有一個線程在執行請求,減輕了db的壓力,但缺點也很明顯,下降了系統的qps。
  • 交錯失效時間:能夠在原有的失效時間基礎上增長一個隨機值,好比1-5分鐘隨機,這樣每個緩存的過時時間的重複率就會下降,就很難引起集體失效的事件。
  • 分析用戶行爲,而後失效時間均勻分佈。
  • 若是是某臺緩存服務器宕機,則考慮作主備。

 

緩存穿透是指查詢一個必定不存在的數據,因爲緩存是請求數據不命中時被動寫入的,而且出於容錯考慮,若是從存儲層查不到數據則不寫入緩存,這將致使這個不存在的數據每次請求都要到存儲層去查詢,在流量大時數據庫可能就掛掉了,通俗說就是惡意用戶模擬請求不少緩存中不存在的數據,因爲緩存中都沒有,致使這些請求短期內直接落在了數據庫上,致使數據庫異常。從系統層面來看像是穿透了緩存層直接達到db。

  解決思路:

  • 空值緩存:一種比較簡單的解決辦法,在第一次查詢完不存在的數據後,將該key與對應的空值也放入緩存中,只不過設定爲較短的失效時間,最長不超過五分鐘。這樣則能夠應對短期的大量的該key攻擊,設置爲較短的失效時間是由於該值可能業務無關,存在乎義不大,且該次的查詢也未必是攻擊者發起,無太久存儲的必要,故能夠早點失效。

  • 布隆過濾器(bloom filter):相似於哈希表的一種算法,將全部可能存在的數據哈希到一個足夠大的bitmap中,在進行數據庫查詢以前會使用這個bitmap進行過濾,若是一個必定不存在的數據會被這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力。 

 

緩存擊穿對於一些設置了過時時間的key,若是這些key在某些時間點被超高併發地訪問,是一種很是「熱點」的數據。這個時候可能會發生緩存被「擊穿」的問題,和緩存雪崩的區別在於:緩存擊穿是針對某一/幾個key緩存,緩存雪崩則是不少key。當緩存在某個時間點過時的時候,剛好在這個時間點對這個Key有大量的併發請求過來,這些請求發現緩存過時通常都會從後端DB加載數據並回設到緩存,這個時候大併發的請求可能會瞬間把後端DB壓垮。

好比:微博有一個熱門話題的功能,用戶對於熱門話題的搜索量每每在一些時刻會大大的高於其餘話題,這種咱們成爲系統的「熱點「,因爲系統中對這些熱點的數據緩存也存在失效時間,在熱點的緩存到達失效時間時,此時可能依然會有大量的請求到達系統,沒有了緩存層的保護,這些請求一樣的會到達db從而可能引發故障。擊穿與雪崩的區別即在於擊穿是對於特定的熱點數據來講,而雪崩是所有數據

解決思路:
  • 二級緩存:對於熱點數據進行二級緩存,並對於不一樣級別的緩存設定不一樣的失效時間,則請求不會直接擊穿緩存層到達數據庫。
  • 互斥鎖(mutex key): 只讓一個線程構建緩存,其餘線程等待構建緩存的線程執行完,從新從緩存獲取數據便可。
  • LRU算法:根據數據的歷史訪問記錄來進行淘汰數據,其核心思想是「若是數據最近被訪問過,那麼未來被訪問的概率也更高」。最多見的實現是使用一個鏈表保存緩存數據,緩存步驟: 首先將新數據放入鏈表的頭部 在進行數據插入的過程當中,若是檢測到鏈表中有數據被再次訪問也就是有請求再次訪問這些數據,那麼就其插入的鏈表的頭部,由於它們相對其餘數據來講多是熱點數據,具備保留時間更久的意義 最後當鏈表數據放滿時將底部的數據淘汰,也就是不常訪問的數據

    

緩存併發:若是網站併發訪問高,一個緩存若是失效,可能出現多個進程同時查詢DB,同時設置緩存的狀況,若是併發確實很大,這也可能形成DB壓力過大,還有緩存頻繁更新的問題。

  解決思路:對緩存查詢加鎖,若是KEY不存在,就加鎖,而後查DB入緩存,而後解鎖;其餘進程若是發現有鎖就等待,而後等解鎖後返回數據或者進入DB查詢。

 

緩存預熱目的就是在系統上線前,將數據加載到緩存中。

  解決思路:數據量不大的話,在系統啓動的時候直接加載。或者本身寫個簡單的緩存預熱程序。

 

Redis的事務命令:

Redis 事務容許一組命令在單一步驟中執行。事務有兩個特色:

  • 事務是一個單獨的隔離操做:事務中的全部命令都會序列化、按順序地執行。事務在執行的過程當中,不會被其餘客戶端發送來的命令請求所打斷。
  • Redis 事務是原子的。原子意味着要麼全部的命令都執行,要麼都不執

 

MULTI:標記一個事務塊的開始。

EXEC:執行全部事務塊內的命令。

DISCARD:取消事務,放棄執行事務塊內的全部命令。

WATCH key[key ...]:監視一個或多個key,若事務在執行這個key以前被其餘命令所改動,那麼事務將被打斷。

UNWATCH:取消 WATCH 命令對全部 key 的監視。

  舉個栗子:

redis 127.0.0.1:6379> MULTI
OK
 
redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
QUEUED
 
redis 127.0.0.1:6379> GET book-name
QUEUED
 
redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
QUEUED
 
redis 127.0.0.1:6379> SMEMBERS tag
QUEUED
 
redis 127.0.0.1:6379> EXEC
1) OK
2) "Mastering C++ in 21 days"
3) (integer) 3
4) 1) "Mastering Series"
   2) "C++"
   3) "Programming"

 

 

Redis設置密碼:

第一種方式(修改配置文件)
Sudo vim /etc/redis/redis.conf
命令模式搜索requirepass,把註釋掉的那行打開
# requirepass footbared
requirepass 123456

第二種方式:直接在redis中設置,只對當前啓動有效
設置密碼:config set requirepass 123456
取消密碼:config set requirepass 「」

 

 

Redis的存儲機制:

RDB (快照Snapshot)工做原理: 將databases中的key-value的二進制形式存儲在了rdb文件中。先將數據存在內存,而後當數據累計達到某些設定的閥值的時候,就會觸發一次DUMP操做,將變化的數據一次性寫入數據文件(RDB文件)。

 

AOF 工做原理: 是將數據也是先存在內存,可是在存儲的時候會使用調用fsync來完成對本次寫操做的日誌記錄,這個日誌揭露文件實際上是一個基於Redis網絡交互協議的文本文件。AOF調用fsync也不是說所有都是無阻塞的,在某些系統上可能出現fsync阻塞進程的狀況,對於這種狀況能夠經過配置修改,但默認狀況不要修改。AOF最關鍵的配置就是關於調用fsync追加日誌文件的平率,有兩種預設頻率,always每次記錄進來都添加,everysecond 每秒添加一次。兩個配置各有所長後面分析。因爲是採用日誌追加的方式來持久話數據,因此引出了第二個日誌的概念:rewrite. 後面介紹它的由來。

不管是那種機制,Redis都是將數據存儲在內存中。

 

性能&安全比較

性能:

RDB方式的性能明顯高於AOF方式,RDB採用壓縮的二進制方式存儲數據,數據文件比較小,加載快速。存儲的時候是按照配置項中的save策略來存儲,每次都是聚合不少數據批量存儲,寫入的效率很好,而AOF則通常都是工做在實時存儲或者準實時模式下。相對來講存儲的頻率高,效率卻偏低。

數據安全:

AOF數據安全性高於RDB存儲,Snapshot存儲是基於累計批量的思想,也就是在容許的狀況下,累計的數據越多那麼寫入效率也就越高,但數據的累計是靠時間的積累完成的,那麼若是在長時間數據不寫入RDB,但Redis又遇到了崩潰,那麼沒有寫入的數據就沒法恢復了,可是AOF方式恰恰相反,根據AOF配置的存儲頻率的策略能夠作到最少的數據丟失和較高的數據恢復能力。

 

Redis常見性能問題? 

1).Master寫內存快照,save命令調度rdbSave函數,會阻塞主線程的工做,當快照比較大時對性能影響是很是大的,會間斷性暫停服務,因此Master最好不要保存寫內存快照。

2).Master AOF持久化,若是不重寫AOF文件,這個持久化方式對性能的影響是最小的,可是AOF文件會不斷增大,AOF文件過大會影響Master重啓的恢復速度。Master最好不要作任何持久化工做,包括內存快照和AOF日誌文件,特別是不要啓用內存快照作持久化,若是數據比較關鍵,可以使用某個Slave開啓AOF備份數據,策略爲每秒同步一次。

3).Master調用 BGREWRITEAOF 重寫AOF文件,AOF在重寫的時候會佔大量的CPU和內存資源,致使服務load太高,出現短暫服務暫停現象。

4).Redis主從複製的性能問題,爲了主從複製的速度和鏈接的穩定性,Slave和Master最好在同一個局域網內


Redis適用場景

(1)會話緩存(Session Cache)最常使用,優點在於:Redis提供持久化。好比用戶的購物車數據。

(2)全頁緩存(FPC)

(3)隊列 ,Reids提供的 list 和 set 操做,這使得Redis能做爲一個很好的消息隊列平臺來使用。Redis做爲隊列使用的操做,相似於Python當中對 list 的 push/pop 操做。

(4)排行榜/計數器

 

Redis特色:

  • 速度快:使用標準C寫,全部數據都在內存中完成,讀寫速度分別達到10萬/20萬
  • 持久化:對數據的更新採用Copy-on-write技術,能夠異步地保存到磁盤上,主要有兩種策略,一是根據時間,更新次數的快照(save 300 10 )二是基於語句追加方式(Append-only file,aof)
  • 自動操做:對不一樣數據類型的操做都是自動的,很安全
  • 快速的主--從複製,官方提供了一個數據,Slave在21秒即完成了對Amazon網站10G key set的複製。
  • Sharding技術: 很容易將數據分佈到多個Redis實例中,數據庫的擴展是個永恆的話題,在關係型數據庫中,主要是以添加硬件、以分區爲主要技術形式的縱向擴展解決了不少的應用場景,但隨着web2.0、移動互聯網、雲計算等應用的興起,這種擴展模式已經不太適合了,因此近年來,像採用主從配置、數據庫複製形式的,Sharding這種技術把負載分佈到多個特理節點上去的橫向擴展方式用處愈來愈多。

  缺點:

  • 是數據庫容量受到物理內存的限制,不能用做海量數據的高性能讀寫,所以Redis適合的場景主要侷限在較小數據量的高性能操做和運算上。
  • Redis較難支持在線擴容,在集羣容量達到上限時在線擴容會變得很複雜。爲避免這一問題,運維人員在系統上線時必須確保有足夠的空間,這對資源形成了很大的浪費。
  • Redis 不具有自動容錯和恢復功能,主機從機的宕機都會致使前端部分讀寫請求失敗,須要等待機器重啓或者手動切換前端的 IP 才能恢復。
  • 主機宕機,宕機前有部分數據未能及時同步到從機,切換 IP 後還會引入數據不一致的問題,下降了系統的可用性。
  • Redis 的主從複製採用全量複製,複製過程當中主機會 fork出一個子進程對內存作一份快照,並將子進程的內存快照保存爲文件發送給從機,這一過程須要確保主機有足夠多的空餘內存。若快照文件較大,對集羣的服務能力會產生較大的影響,並且複製過程是在從機新加入集羣或者從機和主機網絡斷開重連時都會進行,也就是網絡波動都會形成主機和從機間的一次全量的數據複製,這對實際的系統運營形成了不小的麻煩

 

Redis使用場景:

  • 1. 取最新 N 個數據的操做
  • 2. 排行榜應用,取 TOP N 操做
  • 3. 須要精準設定過時時間的應用
  • 4. 計數器應用
  • 5. uniq 操做,獲取某段時間全部數據排重值
  • 6. Pub/Sub 構建實時消息系統
  • 7. 構建隊列系統
  • 8. 緩存

 

Redis宕機?

主從模式下的宕機區分:

  • slave從機宕機:在Redis中從庫從新啓動後會自動加入到主從架構中,自動完成同步數據;若是從數據庫實現了持久化,只要從新假如到主從架構中會實現增量同步。
  • Master 宕機:假如主從都沒數據持久化,此時千萬不要立馬重啓服務,不然可能會形成數據丟失,正確的操做以下:
    • 在slave數據上執行SLAVEOF ON ONE,來斷開主從關係並把slave升級爲主庫
    • 此時從新啓動主數據庫,執行SLAVEOF,把它設置爲從庫,自動備份數據。

  注:以上過程很容易配置錯誤,可使用簡單的方法:redis的哨兵(sentinel)的功能。

 

哨兵(sentinel的原理:Redis提供了sentinel(哨兵)機制經過哨兵模式啓動redis後,自動監控master/slave的運行狀態,基本原理是:心跳機制+投票裁決。

心跳機制:每一個sentinel會向其它sentinal、master、slave定時發送消息,以確認對方是否「活」着,若是發現對方在指定時間(可配置)內未迴應,則暫時認爲對方已掛(所謂的「主觀認爲宕機」 Subjective Down,簡稱SDOWN)。

投票裁決:若"哨兵羣"中的多數sentinel,都報告某一master沒響應,系統才認爲該master"完全死亡"(即:客觀上的真正down機,Objective Down,簡稱ODOWN),經過必定的vote算法,從剩下的slave節點中,選一臺提高爲master,而後自動修改相關配置。

哨兵的配置:複製redis中sentinel.conf,根據狀況進行配置

 

 

Redis持久化方式:

  • RDB 持久化(Redis DataBase)能夠在指定的時間間隔內生成數據集的時間點快照(point-in-time snapshot)。
  • AOF 持久化(Append-only file)記錄服務器執行的全部寫操做命令,並在服務器啓動時,經過從新執行這些命令來還原數據集。 AOF 文件中的命令所有以 Redis 協議的格式來保存,新命令會被追加到文件的末尾。 Redis 還能夠在後臺對 AOF 文件進行重寫(rewrite),使得 AOF 文件的體積不會超出保存數據集狀態所需的實際大小。
  • Redis 還能夠同時使用 AOF 持久化和 RDB 持久化。 在這種狀況下, 當 Redis 重啓時, 它會優先使用 AOF 文件來還原數據集, 由於 AOF 文件保存的數據集一般比 RDB 文件所保存的數據集更完整。

 

RDB 快照

在默認狀況下, Redis 將數據庫快照保存在名字爲 dump.rdb 的二進制文件中。能夠對 Redis 進行設置, 讓它在「 N 秒內數據集至少有 M 個改動」這一條件被知足時, 自動保存一次數據集。

執行保存操做:調用 SAVE 或者 BGSAVE ,手動讓 Redis 進行數據集保存操做。一個栗子,save 60 1000   會讓 Redis 在知足「 60 秒內有至少有 1000 個鍵被改動」這一條件時, 自動保存一次數據集。這種持久化方式被稱爲快照(snapshot)。

  • SAVE:阻塞redis的服務器進程,直到RDB文件被建立完畢。
  • BGSAVE:派生(fork)一個子進程來建立新的RDB文件,記錄接收到BGSAVE當時的數據庫狀態,父進程繼續處理接收到的命令,子進程完成文件的建立以後,會發送信號給父進程,而與此同時,父進程處理命令的同時,經過輪詢來接收子進程的信號。

 

快照的運做方式

當Redis須要保存 dump.rdb 文件時,執行如下操做:

  1. Redis 調用 fork() ,同時擁有父進程和子進程。
  2. 子進程將數據集寫入到一個臨時 RDB 文件中。
  3. 當子進程完成對新 RDB 文件的寫入時,Redis 用新 RDB 文件替換原來的 RDB 文件,並刪除舊的 RDB 文件。

這種工做方式使得 Redis 能夠從寫時複製(copy-on-write)機制中獲益。

 

 

RDB 優勢

  • RDB 是一個很是緊湊(compact)的文件,它保存了 Redis 在某個時間點上的數據集。 這種文件很是適合用於進行備份: 好比說,你能夠在最近的 24 小時內,每小時備份一次 RDB 文件,而且在每月的每一天,也備份一個 RDB 文件。 這樣的話,即便趕上問題,也能夠隨時將數據集還原到不一樣的版本。
  • 生成的文件是一種壓縮的二進制文件,方便於在網絡中傳輸。
  • RDB 很是適用於災難恢復(disaster recovery):它只有一個文件,而且內容都很是緊湊,能夠(在加密後)將它傳送到別的數據中心。
  • RDB 能夠最大化 Redis 的性能:父進程在保存 RDB 文件時惟一要作的就是 fork 出一個子進程,而後這個子進程就會處理接下來的全部保存工做,父進程無須執行任何磁盤 I/O 操做。
  • RDB 在恢復大數據集時的速度比 AOF 的恢復速度要快。

 

RDB 缺點

  • 若是想要避免在服務器故障時丟失數據,那麼 RDB 不太適合。 RDB保存一次備份以後,到下一次保存以前,這段期間所產生的的新數據,若是尚未達到生成備份文件的條件,此時服務器宕機就會丟失這部分數據。雖然 Redis 容許你設置不一樣的保存點(save point)來控制保存 RDB 文件的頻率, 可是由於 RDB 文件須要保存整個數據集的狀態,因此它是一個很是耗費系統資源的操做。 所以如果須要至少 5 分鐘才保存一次 RDB 文件的狀況下, 一旦發生故障停機,可能會丟失好幾分鐘的數據。
  • 每次保存 RDB 的時候,Redis 都要 fork() 建立出一個子進程,並由子進程來進行實際的持久化工做。 在數據集比較龐大時, fork() 可能會很是耗時,形成服務器在某某毫秒內中止處理客戶端; 若是數據集很是巨大,而且 CPU 時間很是緊張的話,那麼這種中止時間甚至可能會長達整整一秒。 雖然 AOF 重寫也須要進行 fork() ,但不管 AOF 重寫的執行間隔有多長,數據的耐久性都不會有任何損失。建立子線程和生成rdb文件會佔用大量 的系統資源和處理時間

 

RDB 配置文件

# dbfilename:配置RDB文件的名稱,默認叫 dump.rdb
dbfilename dump.rdb

# dir:配置的RDB文件存儲在本地的路徑,若是是在 /redis/redis-3.0.6/src 下啓動的redis-cli,則數據會存儲在當前src目錄下
dir ./

# snapshot觸發時機的配置:save <seconds> <changes> , 可經過 save 「」 關閉snapshot功能  
# changes:對於此值設置需謹慎,要評估系統的變動操做密集程度 
save 900 1        # 更改了1個key的時候,間隔900秒後,至少有一個變動操做,進行持久化存儲snapshot   
save 300 10      # 更改了10個key的時候,間隔300s進行持久化存儲
save 60 10000   # 更改10000個key的時候,間隔360s進行存儲。

# 當生成 RDB 文件出錯沒法繼續時,是不然色客戶端的「變動操做」,是否繼續處理 Redis 寫命令,默認爲不處理。「錯誤」可能由於磁盤已滿/磁盤故障/OS級別異常等  
stop-writes-on-bgsave-error yes  

# 是否對rdb文件進行壓縮,默認爲「yes」,壓縮每每意味着「額外的cpu消耗」,同時也意味這較小的文件尺寸以及較短的  
rdbcompression yes      

#
是否對 RDB 文件進行校驗和校驗
rdbchecksum
 
 

 

RDB 命令演示

Redis shutdown模擬服務器宕機
mv dump.rdb dump.rdb.bak
中止redis服務器service redis stop
啓動redis-server /etc/redis/redis.conf redis-cli 查詢keys * cp dump.rdb.bak dump.rdb ps aux | grep redis Kill -9 xxxx 中止redis服務器service redis stop 重啓redis-server 重啓redis-cli 查詢keys *

 

 

 

AOF:只進行追加操做的文件(append-only file)

快照功能並非很是耐久(durable): 若是 Redis 由於某些緣由而形成故障停機, 那麼服務器將丟失最近寫入、且仍未保存到快照中的那些數據。若是對於數據追求徹底耐久能力(full durability)的要求,快照功能就不太適用。從 1.1 版本開始, Redis 增長了一種徹底耐久的持久化方式: AOF 持久化。

經過修改配置文件來打開 AOF 功能:appendonly yes 。每當 Redis 執行一個改變數據集的命令時(好比 SET key value [EX seconds] [PX milliseconds] [NX|XX]), 這個命令就會被追加到 AOF 文件的末尾(對數據的每一條修改命令追加到aof文件)這樣的話,當 Redis 從新啓時, 程序就能夠經過從新執行 AOF 文件中的命令來達到重建數據集的目的。RDB持久化至關於備份數據庫狀態,而AOF持久化是備份數據庫接收到的命令。

 

AOF文件生成的過程:命令追加、文件寫入、文件同步

 

AOF 重寫(Rewrite)

由於 AOF 的運做方式是不斷地將命令追加到文件的末尾,按照記錄日誌的方式去工做的,因此隨着寫入命令的不斷增長,成千上萬的數據插入必然致使日誌文件的擴大,AOF 文件的體積也會變得愈來愈大。舉個栗子:若是你對一個計數器調用了 100 次 INCR key , 那麼僅僅是爲了保存這個計數器的當前值, AOF 文件就須要使用 100 條記錄(entry)。然而實際上,只使用一條 SET key value [EX seconds] [PX milliseconds] [NX|XX] 命令已經足以保存計數器的當前值了, 其他 99 條記錄實際上都是多餘的。

爲了處理這種狀況, Redis 支持另外一種特性:能夠在不打斷服務客戶端的狀況下,對 AOF 文件進行重建(rebuild):執行 BGREWRITEAOF 命令:將日誌文件中的全部數據都從新寫到另一個新的日誌文件中,不一樣的是,在舊文件當中對於key的屢次操做,只會保留最終值的那次操做命令記錄到日誌文件中。Redis 2.2 須要本身手動執行 BGREWRITEAOF 命令; Redis 2.4 則能夠自動觸發 AOF 重寫。

兩個配置須要注意:

auto-aof-rewrite-percentage 100 (當前寫入日誌文件的大小佔到初始日誌文件大小的某個百分比時觸發Rewrite)
auto-aof-rewrite-min-size 64mb (本次Rewrite最小的寫入數據量)
兩個條件須要同時知足。

 

AOF 的運做方式

AOF 重寫和 RDB 建立快照同樣,都巧妙地利用了寫時複製機制。

如下是 AOF 重寫的執行步驟:

  1. Redis 執行 fork() ,如今同時擁有父進程和子進程。
  2. 子進程開始將新 AOF 文件的內容寫入到臨時文件。
  3. 對於全部新執行的寫入命令,父進程一邊將它們累積到一個內存緩存中,一邊將這些改動追加到現有 AOF 文件的末尾:這樣即便在重寫的中途發生停機,現有的 AOF 文件也仍是安全的。
  4. 當子進程完成重寫工做時,它給父進程發送一個信號,父進程在接收到信號以後,將內存緩存中的全部數據追加到新 AOF 文件的末尾。
  5. 搞定!如今 Redis 原子地用新文件替換舊文件,以後全部命令都會直接追加到新 AOF 文件的末尾。

 

AOF的優勢

  • 提供了多種同步命令方式,默認1s同步(fsync)一次寫命令,就算髮生故障停機,最多丟失1s的數據。( fsync 會在後臺線程執行,因此主線程能夠繼續努力地處理命令請求)。
  • AOF 文件是一個只進行追加操做的日誌文件(append only log), 所以對 AOF 文件的寫入不須要進行查找(seek), 即便日誌由於某些緣由而包含了未寫入完整的命令(好比寫入時磁盤已滿,寫入中途停機等), redis-check-aof 工具也能夠輕易地修復這種問題。
  • Redis 能夠在 AOF 文件體積變得過大時,自動地在後臺對 AOF 進行重寫: 重寫後的新 AOF 文件包含了恢復當前數據集所需的最小命令集合。 整個重寫操做是絕對安全的,由於 Redis 在建立新 AOF 文件的過程當中,會繼續將命令追加到現有的 AOF 文件裏面,即便重寫過程當中發生停機,現有的 AOF 文件也不會丟失。 而一旦新 AOF 文件建立完畢,Redis 就會從舊 AOF 文件切換到新 AOF 文件,並開始對新 AOF 文件進行追加操做。
  • AOF 文件有序地保存了對數據庫執行的全部寫入操做,若是你不當心執行了 FLUSHALL 命令,只要 AOF 文件未被重寫,那麼只要中止服務器, 移除 AOF 文件末尾的 FLUSHALL 命令,並重啓 Redis,就能夠將數據集恢復到 FLUSHALL 執行以前的狀態。

 

AOF 的缺點

  • 對於相同的數據集來講,AOF 文件的體積一般要大於 RDB 文件的體積。當Redis負載較高時,RDB比AOF性能更好。
  • 根據所使用的 fsync 策略,AOF 的速度可能會慢於 RDB 。 在通常狀況下, 每秒 fsync 的性能依然很是高, 而關閉 fsync 可讓 AOF 的速度和 RDB 同樣快, 即便在高負荷之下也是如此。 不過在處理巨大的寫入載入時,RDB 能夠提供更有保證的最大延遲時間(latency)。
  • AOF 曾經發生過的 bug :由於個別命令的緣由,致使 AOF 文件在從新載入時,沒法將數據集恢復成保存時的原樣。(舉個栗子,阻塞命令 BRPOPLPUSH source destination timeout 就曾引發過這樣的 bug)它們會自動生成隨機、複雜的數據集,並經過從新載入這些數據來確保一切正常。雖然這種 bug 在 AOF 文件中並不常見,可是對比來講,RDB 幾乎是不可能出現這種 bug 的。

 

AOF 配置文件

# 是否打開 AOF 持久化功能,默認爲「no」,可經過「yes」來開啓AOF功能  
appendonly yes      # 只有在「yes」下,aof重寫/文件同步等特性纔會生效  


# 指定AOF文件的名稱  
appendfilename appendonly.aof  


# 同步頻率:指定AOF操做時,文件的同步策略,有三個合法值:always everysec no,默認爲everysec  會影響到服務器間隔多久完成一次命令的記錄。
# always:每一條aof記錄都當即同步到文件,這是最安全的方式,但大量磁盤操做和阻塞延遲形成IO開支大,速度最慢不推薦。
# everysec:將緩存區的內容每隔一秒寫入AOF文件中,性能和安全都比較中庸,是Redis官方推薦的方式。
# no :寫入AOF文件中的操做由操做系統決定,Redis不直接調用。通常而言爲了提升效率,操做系統會等待緩存區被填滿,纔會開始同步數據到磁盤。在物理服務器故障時,數據丟失量會因OS配置有關
appendfsync everysec  


# 在aof-rewrite期間,appendfsync是否暫緩文件同步,"no"表示「不暫緩」,「yes」表示「暫緩」,默認爲「no」  
no-appendfsync-on-rewrite no  


# aof文件rewrite觸發的最小文件尺寸(mb,gb),只有大於此aof文件大於此尺寸是纔會觸發rewrite,默認「64mb」,建議「512mb」  
auto-aof-rewrite-min-size 64mb  


# 相對於「上一次」rewrite,本次rewrite觸發時aof文件應該增加的百分比。  
# 每一次rewrite以後,redis都會記錄下此時「新aof」文件的大小(例如A),那麼當aof文件增加到A*(1 + p)以後,觸發下一次rewrite,每一次aof記錄的添加都會檢測當前aof文件的尺寸。  
auto-aof-rewrite-percentage 100

# 當前AOF文件啓動新的日誌重寫過程的最小值,避免剛剛啓動Reids時因爲文件尺寸較小致使頻繁的重寫。 auto-aof-rewrite-min-size 64mb

 

RDB和AOF如何選擇

- AOF更加安全,能夠將數據更加及時的同步到文件中,可是AOF須要較多的磁盤IO開支,AOF文件尺寸較大,文件內容恢復數度相對較慢 
- RDB(snapshot),安全性較差,它是「正常時期」數據備份以及 master-slave 數據同步的最佳手段,文件尺寸較小,恢復數度較快。 
(1)不要僅僅使用RDB,由於那樣會致使你丟失不少數據 
(2)也不要僅僅使用AOF,由於那樣有兩個問題,第一,經過AOF作冷備的操做,沒有RDB作冷備來的恢復速度更快; 第二,RDB每次簡單粗暴生成數據快照更加健壯,能夠避免AOF這種複雜的備份和恢復機制的bug 。
(3)綜合使用AOF和RDB兩種持久化機制,用AOF來保證數據不丟失,做爲數據恢復的第一選擇; 用RDB來作不一樣程度的冷備,在AOF文件都丟失或損壞不可用的時候,還可使用RDB來進行快速的數據恢復。

 

怎麼從 RDB 持久化切換到 AOF 持久化

在 Redis 2.2 或以上版本,能夠在不重啓的狀況下,從 RDB 切換到 AOF :

  1. 爲最新的 dump.rdb 文件建立一個備份。
  2. 將備份放到一個安全的地方。
  3. 執行如下兩條命令:
redis-cli> CONFIG SET appendonly yes redis-cli> CONFIG SET save "" 
  1. 確保命令執行以後,數據庫的鍵的數量沒有改變。
  2. 確保寫命令會被正確地追加到 AOF 文件的末尾。

步驟 3 執行的第一條命令開啓了 AOF 功能: Redis 會阻塞直到初始 AOF 文件建立完成爲止, 以後 Redis 會繼續處理命令請求, 並開始將寫入命令追加到 AOF 文件末尾。

步驟 3 執行的第二條命令用於關閉 RDB 功能。 這一步是可選的, 若是你願意的話, 也能夠同時使用 RDB 和 AOF 這兩種持久化功能。

別忘了在 redis.conf 中打開 AOF 功能! 不然服務器重啓以後, 以前經過 CONFIG SET 設置的配置就會被遺忘, 程序會按原來的配置來啓動服務器。

 

 

對比

  • AOF更安全,可將數據及時同步到文件中,但須要較多的磁盤IO,AOF文件尺寸較大,文件內容恢復相對較慢, 也更完整。
  • RDB持久化,安全性較差,它是正常時期數據備份及 master-slave數據同步的最佳手段,文件尺寸較小,恢復數度較快。

 

 

備份 Redis 數據

官方建議:

  • 建立一個按期任務(cron job), 每小時將一個 RDB 文件備份到一個文件夾, 而且天天將一個 RDB 文件備份到另外一個文件夾。
  • 確保快照的備份都帶有相應的日期和時間信息, 每次執行按期任務腳本時, 使用 find 命令來刪除過時的快照: 好比能夠保留最近 48 小時內的每小時快照, 還能夠保留最近一兩個月的每日快照。
  • 至少天天一次, 將 RDB 備份到你的數據中心以外, 或者至少是備份到你運行 Redis 服務器的物理機器以外。

 

 

從數據集中任意選擇數據進行淘汰

相關文章
相關標籤/搜索