一、Redis的記憶體配置
1,Redis配置記憶體為多少合適?
預設:如果不設定最大記憶體大小或者設定最大記憶體大小為0,在64為作業系統下不限制記憶體大小,在32位操作系統下最多使用3GB記憶體。
極限情況:留出一倍記憶體。比如你的redis資料佔用了8G記憶體,那麼你還需要再預留8G空閒記憶體。也就是記憶體需求是16G。記憶體佔用率低於50%是最安全的。
普通情況:正常情況下,在序列化週期內,不會更改所有資料,只會有部分資料更改,那麼,預留出可能產生的更改部分的空間,就行。如果實在要說一個資料的話,一般推薦Redis設定記憶體為最大實體記憶體的75%都是安全的。
2,如何修改記憶體
a)配置檔案修改
redis.conf中
#設定為100M,單位是byte maxmemory 104857600
b)命令列修改
config set maxmemory 104857600
3,檢視最大記憶體
config get maxmemory #或者使用 info memory
4,如果Redis的記憶體你打滿了會怎麼樣?
二、Redis的記憶體淘汰策略
1,Redis 過期策略是:定期刪除+惰性刪除。
所謂定期刪除,指的是 Redis 預設是每隔 100ms 就隨機抽取一些設定了過期時間的 key,檢查其是否過期,如果過期就刪除。
假設 Redis 裡放了 10w 個 key,都設定了過期時間,你每隔幾百毫秒,就檢查 10w 個 key,那 Redis 基本上就死了,cpu 負載會很高的,消耗在你的檢查過期 key 上了。注意,這裡可不是每隔 100ms 就遍歷所有的設定過期時間的 key,那樣就是一場效能上的災難。實際上 Redis 是每隔 100ms 隨機抽取一些 key 來檢查和刪除的。
惰性刪除:資料到達過期時間,不做處理。等下次訪問該資料時,如果未過期,返回資料;發現已過期,刪除,返回不存在。
但是實際上這還是有問題的,如果定期刪除漏掉了很多過期 key,然後你也沒及時去查,也就沒走惰性刪除,此時會怎麼樣?如果大量過期 key 堆積在記憶體裡,導致 Redis 記憶體塊耗盡了,咋整?實際上會走:記憶體淘汰機制。
2,記憶體淘汰機制
Redis記憶體淘汰機制有以下幾個:
- noeviction: 當記憶體不足以容納新寫入資料時,新寫入操作會報錯,這個一般沒人用吧,實在是太噁心了。
- allkeys-lru:當記憶體不足以容納新寫入資料時,在鍵空間中,移除最近最少使用的 key(這個是最常用的)。
- allkeys-random:當記憶體不足以容納新寫入資料時,在鍵空間中,隨機移除某個 key,這個一般沒人用吧,為啥要隨機,肯定是把最近最少使用的 key 給幹掉啊。
- volatile-lru:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,移除最近最少使用的 key(這個一般不太合適)。
- volatile-random:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,隨機移除某個 key。
- volatile-ttl:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,有更早過期時間的 key 優先移除。
- allkeys-lfu: 對所有key使用LFU演算法進行刪除。LFU:最不經常使用,如果一個資料在最近一段時間內使用次數很少,那麼在將來一段時間內被使用的可能性也很小。
- volatile-lfu: 對所有設定了過期時間的key使用LFU演算法進行刪除。
三、手寫LRU演算法
1,採用LinkedHashMap實現
public class Demo015_LRUCacheLinkedHashMap { private int capacity; private LinkedHashMap<Integer, Integer> linkedHashMap; public Demo015_LRUCacheLinkedHashMap(int capacity) { this.capacity = capacity; /** * 三個引數:capacity為容量,0.75位擴容因子,true為按照訪問排序false為按照插入排序 * 重寫刪除尾結點的方法,一旦發現當前linkhashmap的長度大於總容量就需要刪除*/ linkedHashMap = new LinkedHashMap<Integer, Integer>(capacity,0.75F,true){ @Override protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) { return super.size() > capacity; } }; } public void put(int key, int value) { linkedHashMap.put(key, value); } public int get(int key) { Integer value = linkedHashMap.getOrDefault(key,-1); return value; } }
2,自定義雙向連結串列
- 定義Node節點:key,val,next和prev
- 定義DoubleLinkedNode管理Node結點組成頭尾結點的雙向連結串列
- 定義hashmap儲存每個結點
- 插入時判斷當前值是否已經存在hashmap中
- 如果存在就更改當前值,刪除雙向連結串列中原來的這個值,新增新值到連結串列頭結點並修改hashmap中當前值
- 如果不存在當前值,判斷當前容器是否滿了,如果滿了就刪除連結串列尾部刪除hashmap中資料。並新增新結點到連結串列頭部和hashmap中
- 獲取時,直接從hashmap中獲取。如果不存在直接返回-1,如果存在就刪除連結串列尾部資料,更新連結串列頭部資料為當前node
public class Demo015_LRUCache { class Node<K, V> { K key; V val; Node next; Node prev; public Node(){ next = prev = null; } public Node(K key, V val) { this.key = key; this.val = val; next = prev = null; } } class DoubleLinkedNode<K,V>{ Node head; Node tail; public DoubleLinkedNode() { head = new Node(); tail = new Node(); head.next = tail; tail.prev = head; } public void addHead(Node<K,V> node) { node.prev = head; node.next = head.next; head.next.prev = node; head.next = node; } public void remove(Node<K,V> node) { if (node.prev == null || node.next==null) { return; } node.prev.next = node.next; node.next.prev = node.prev; node.next = null; node.prev = null; } public Node<K,V> getLast() { if (tail.prev == head) { return null; } return tail.prev; } } private int capacity; private HashMap<Integer, Node<Integer,Integer>> hashMap; private DoubleLinkedNode<Integer, Integer> doubleLinkedNode; public Demo015_LRUCache(int capacity) { this.capacity = capacity; hashMap = new HashMap<>(); doubleLinkedNode = new DoubleLinkedNode<>(); } public int get(int key) { Node<Integer,Integer> node = hashMap.get(key); if (node == null) { return -1; } doubleLinkedNode.remove(node); doubleLinkedNode.addHead(node); return node.val; } public void put(int key, int value) { Node<Integer, Integer> node = hashMap.get(key); if (node == null) { //沒有新增過 if (hashMap.size() == capacity) { //達到最大值狀態 //刪除最後結點 Node<Integer, Integer> last = doubleLinkedNode.getLast(); doubleLinkedNode.remove(last); hashMap.remove(last.key); } //新增頭結點 node = new Node<>(key, value); hashMap.put(key,node); doubleLinkedNode.addHead(node); }else { //如果新增過,刪除雙向連結串列的該節點,將其修改值之後新增到頭節點 doubleLinkedNode.remove(node); node.val = value; doubleLinkedNode.addHead(node); hashMap.put(key, node); } } }