一、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); } } }