RDB的持久化策略
(快照方式,默認持久化方式): 按照規則定時將內存中的數據同步到磁盤,它有以下4個觸發場景。
1. 自己配置的快照規則 vim /redis/bin/ redis.conf;按照save <seconds> <changes>這個規則自己添加或修改規則。
2. save或者bgsave命令
save:將內存的數據同步到磁盤中,這個操作會阻塞客戶端的請求(不建議用,太耗時了)。
bgsave:在后台異步執行快照操作,這個操作不會阻塞客戶端的請求。
3. 執行flushall清除內存的所有數據時,只要save <seconds> <changes>規則不為空,就會執行快照。
4. 執行復制的時候(集群中 )
快照的實現原理與優缺點:
redis使用fork函數復制出一份當前進程的副本(子進程);父進程繼續處理客戶端指令,子進程開始將內存的數據寫入硬盤中的臨時文件,寫入完成后就會替換之前的舊文件。優點:1. 多進程處理,性能最快。缺點:1. 若子進程數據未寫完而redis異常退出,就會丟失最后一次快照以后更改的所有數據; 2. 如果數據集比較大的時候,fork可以能比較耗時,造成服務器在一段時間內停止處理客戶端的請求。
AOF的持久化策略
開啟AOF持久化:vim /redis/bin/ redis.conf 將appendonly默認值no改為yes; 重啟后執行對數據的變更命令, 會在bin目錄下生成對應的.aof文件, aof文件中會記錄所有的操作命令!
AOF優化:
auto-aof-rewrite-percentage 100 表示當前aof文件大小超過上一次aof文件大小的百分之多少的時候會進行重寫。如果之前沒有重寫過,以啟動時aof文件大小為准
auto-aof-rewrite-min-size 64mb 限制允許重寫最小aof文件大小,也就是文件大小小於64mb的時候,不需要進行重寫。
aof重寫原理:
執行redis命令的時候,會把命令寫入到.aof文件中,.aof文件變得過大時后台會對它進行重寫。在將命令寫入到.aof時,即使重寫過程發生了停機,現有的.aof文件也不會丟失,所以它是絕對安全的。
同步磁盤數據:
redis在變更數據時,命令會實時記錄到.aof文件。但是由於操作系統的緩存機制,數據不是直接寫入到硬盤,而是寫入硬盤緩存,再通過硬盤緩存機制刷新保存到文件。
appendfsync always 每次執行寫入都會進行同步 , 這個是最安全但是是效率比較低的方式
appendfsync everysec 每一秒執行
appendfsync no 不主動進行同步操作,由操作系統去執行,這個是最快但是最不安全的方式
2.4 aof文件損壞以后如何修復:
例如在執行set name wulei時候,文件只記錄了set name ,這種不完整的命令是無法被寫入磁盤的。我們可以先將.aof文件備份,然后通過redis-check-aof --fix命令修復,它會將一些不合法的命令清理掉,這樣就能被持久化了。
如果要保證數據絕對安全: 可以使用 RDB+AOF 方式來持久化。如果同時使用的話, 那么Redis重啟時,會優先使用AOF文件來還原數據。
如果可以接受幾分鍾的數據丟失:可以使用RDB方式,因為AOF速度沒有RDB快。
============================ 手動分割線 ==============================
Redis的過期策略
redis過期是(定期刪除+惰性刪除+內存淘汰機制)來實現的。reids是用內存來緩存的,內存資源很寶貴。所以我們 set key value 的時候一般都會給它設置過期時間。假設redis里放了10萬個數據,redis默認會每隔100ms隨機抽取設置了過期時間的key來刪除(如果一次性檢查所有數據,cpu負載過高redis早就死了),這種方式就叫定期刪除。這種策略就會導致很多過期的key並沒有被刪除,所以還有惰性刪除, 也就是在你獲取某個key的時候,redis會檢查一下,這個key是否過期了,如果是就直接刪除不返回任何東西!
但是這種方式還是有問題的,比如10萬條全過期了,而redis只定期刪除了部分,而客戶端有沒有對剩下的key操作,key就不會被刪除,還是會造成內存的積壓。這時候就會走內存淘汰機制,例如:
allkeys-lru:當內存不足以容納新寫入數據時,移除最近最少使用的key。(最常用)
volatile-ttl:當內存不足以容納新寫入數據時,在設置了過期時間的鍵中,將更早過期時間的key優先移除。(還湊合吧)
noeviction:當內存不足以容納新寫入數據時,新寫入操作會報錯。(腦殘的策略,沒人用)
allkeys-random:當內存不足以容納新寫入數據時,隨機移除某個key。(腦殘的策略,沒人用)
volatile-lru:當內存不足以容納新寫入數據時,在設了過期時間的鍵中,移除最近最少使用的key。(腦殘的策略,沒人用)
volatile-random:當內存不足以容納新寫入數據時,在設了過期時間的鍵中,隨機移除某個key。(腦殘的策略,沒人用)
緩存淘汰算法LFU算法
lfu算法是根據數據的歷史訪問頻率來淘汰數據的,其核心思想是"如果數據過去被訪問多次,那么將來被訪問的頻率也更高".
缺點:
1. 有存儲成本,需要維護隊列的引用計數,2.最后添加的數據容易被移除
LFU算法原理剖析
1. 新加入的數據插入到隊列尾部(因為引用計數為1)
2. 隊列中的數據被訪問后,其引用計數增加,隊列重新排序
3. 當需要淘汰數據時,將已經排序的列表最后的數據塊刪除
緩存淘汰算法LRU算法
根據數據的歷史訪問記錄來進行淘汰數據,其核心思想是“如果數據最近被訪問過,那么將來被訪問的幾率也更高”
LRU算法原理剖析
1. lru按照一定順序維護着隊列
2. 隊列中的數據被訪問后,數據會被排列到最前面
3. 當需要淘汰數據時,將已經排序的列表最后的數據塊刪除
手寫LRU算法
import java.util.LinkedHashMap; import java.util.Map; // lru是有序的,所以這里繼承 LinkedHashMap public class LRUCache<K,V> extends LinkedHashMap<K,V> { private final int CACHE_SIZE; /** * 傳遞進來最多能緩存多少數據 * * @param cacheSize 緩存大小 */ public LRUCache(int cacheSize) { /* cacheSize是map的初始大小 0.75f是加載因子,容量使用率達到0.75就會擴容一倍 true 表示讓 linkedHashMap 按照訪問順序來進行排序,最近訪問的放在頭部,最老訪問的放在尾部。 */ super(cacheSize, 0.75f, true); CACHE_SIZE = cacheSize; } @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { // 當前map中的數據量大於指定的緩存個數的時候,就自動刪除最老的數據。 boolean b = size() > CACHE_SIZE; if(b){ System.out.println("清除緩存key:"+eldest.getKey()); } return b; } public static void main(String args[]){ LRUCache<String, String> lruCache = new LRUCache<String, String>(3); lruCache.put("1", "1"); lruCache.put("2", "2"); lruCache.put("3", "3"); lruCache.put("4", "4"); System.out.println("初始化完成:"+lruCache.keySet()); System.out.println(lruCache.get("3")); System.out.println("訪問3之后:"+lruCache.keySet()); System.out.println(lruCache.get("2")); System.out.println("訪問2之后:"+lruCache.keySet()); } }