共計 4334 個字符,預計需要花費 11 分鐘才能閱讀完成。
Redis 內存滿了如何解決,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
1、通過配置文件配置
通過在 Redis 安裝目錄下面的 redis.conf 配置文件中添加以下配置設置內存大小。
// 設置 Redis 最大占用內存大小為 100M maxmemory 100mb
redis 的配置文件不一定使用的是安裝目錄下面的 redis.conf 文件,啟動 redis 服務的時候是可以傳一個參數指定 redis 的配置文件的。
2、通過命令修改
Redis 支持運行時通過命令動態修改內存大小
// 設置 Redis 最大占用內存大小為 100M 127.0.0.1:6379 config set maxmemory 100mb // 獲取設置的 Redis 能使用的最大內存大小 127.0.0.1:6379 config get maxmemory
如果不設置最大內存大小或者設置最大內存大小為 0,在 64 位操作系統下不限制內存大小,在 32 位操作系統下最多使用 3GB 內存
Redis 的內存淘汰
既然可以設置 Redis 最大占用內存大小,那么配置的內存就有用完的時候。那在內存用完的時候,還繼續往 Redis 里面添加數據不就沒內存可用了嗎?
實際上 Redis 定義了幾種策略用來處理這種情況:
noeviction(默認策略):對于寫請求不再提供服務,直接返回錯誤(DEL 請求和部分特殊請求除外)
allkeys-lru:從所有 key 中使用 LRU 算法進行淘汰
volatile-lru:從設置了過期時間的 key 中使用 LRU 算法進行淘汰
allkeys-random:從所有 key 中隨機淘汰數據
volatile-random:從設置了過期時間的 key 中隨機淘汰
volatile-ttl:在設置了過期時間的 key 中,根據 key 的過期時間進行淘汰,越早過期的越優先被淘汰
當使用 volatile-lru、volatile-random、volatile-ttl 這三種策略時,如果沒有 key 可以被淘汰,則和 noeviction 一樣返回錯誤。
如何獲取及設置內存淘汰策略
獲取當前內存淘汰策略:
127.0.0.1:6379 config get maxmemory-policy
通過配置文件設置淘汰策略(修改 redis.conf 文件):
maxmemory-policy allkeys-lru
通過命令修改淘汰策略:
127.0.0.1:6379 config set maxmemory-policy allkeys-lru
LRU 算法
什么是 LRU?
上面說到了 Redis 可使用最大內存使用完了,是可以使用 LRU 算法進行內存淘汰的,那么什么是 LRU 算法呢?
LRU(Least Recently Used),即最近最少使用,是一種緩存置換算法。在使用內存作為緩存的時候,緩存的大小一般是固定的。當緩存被占滿,這個時候繼續往緩存里面添加數據,就需要淘汰一部分老的數據,釋放內存空間用來存儲新的數據。
這個時候就可以使用 LRU 算法了。其核心思想是:如果一個數據在最近一段時間沒有被用到,那么將來被使用到的可能性也很小,所以就可以被淘汰掉。
使用 java 實現一個簡單的 LRU 算法。
public class LRUCache k, v { // 容量 private int capacity; // 當前有多少節點的統計 private int count; // 緩存節點 private Map k, Node k, v nodeMap; private Node k, v head; private Node k, v tail; public LRUCache(int capacity) { if (capacity 1) { throw new IllegalArgumentException(String.valueOf(capacity)); } this.capacity = capacity; this.nodeMap = new HashMap (); // 初始化頭節點和尾節點,利用哨兵模式減少判斷頭結點和尾節點為空的代碼 Node headNode = new Node(null, null); Node tailNode = new Node(null, null); headNode.next = tailNode; tailNode.pre = headNode; this.head = headNode; this.tail = tailNode; } public void put(k key, v value) { Node k, v node = nodeMap.get(key); if (node == null) { if (count = capacity) { // 先移除一個節點 removeNode(); } node = new Node (key, value); // 添加節點 addNode(node); } else { // 移動節點到頭節點 moveNodeToHead(node); } } public Node k, v get(k key) { Node k, v node = nodeMap.get(key); if (node != null) { moveNodeToHead(node); } return node; } private void removeNode() { Node node = tail.pre; // 從鏈表里面移除 removeFromList(node); nodeMap.remove(node.key); count--; } private void removeFromList(Node k, v node) { Node pre = node.pre; Node next = node.next; pre.next = next; next.pre = pre; node.next = null; node.pre = null; } private void addNode(Node k, v node) { // 添加節點到頭部 addToHead(node); nodeMap.put(node.key, node); count++; } private void addToHead(Node k, v node) { Node next = head.next; next.pre = node; node.next = next; node.pre = head; head.next = node; } public void moveNodeToHead(Node k, v node) { // 從鏈表里面移除 removeFromList(node); // 添加節點到頭部 addToHead(node); } class Node k, v { k key; v value; Node pre; Node next; public Node(k key, v value) { this.key = key; this.value = value; } } }
上面這段代碼實現了一個簡單的 LUR 算法,代碼很簡單,也加了注釋,仔細看一下很容易就看懂。常用緩存淘汰算法(LFU、LRU、ARC、FIFO、MRU),這篇了解下。
LRU 在 Redis 中的實現
近似 LRU 算法
Redis 使用的是近似 LRU 算法,它跟常規的 LRU 算法還不太一樣。近似 LRU 算法通過隨機采樣法淘汰數據,每次隨機出 5(默認)個 key,從里面淘汰掉最近最少使用的 key。
可以通過 maxmemory-samples 參數修改采樣數量:
例:maxmemory-samples 10
maxmenory-samples 配置的越大,淘汰的結果越接近于嚴格的 LRU 算法
Redis 為了實現近似 LRU 算法,給每個 key 增加了一個額外增加了一個 24bit 的字段,用來存儲該 key 最后一次被訪問的時間。
Redis3.0 對近似 LRU 的優化
Redis3.0 對近似 LRU 算法進行了一些優化。新算法會維護一個候選池(大小為 16),池中的數據根據訪問時間進行排序,第一次隨機選取的 key 都會放入池中,隨后每次隨機選取的 key 只有在訪問時間小于池中最小的時間才會放入池中,直到候選池被放滿。當放滿后,如果有新的 key 需要放入,則將池中最后訪問時間最大(最近被訪問)的移除。
當需要淘汰的時候,則直接從池中選取最近訪問時間最小(最久沒被訪問)的 key 淘汰掉就行。
LRU 算法的對比
我們可以通過一個實驗對比各 LRU 算法的準確率,先往 Redis 里面添加一定數量的數據 n,使 Redis 可用內存用完,再往 Redis 里面添加 n / 2 的新數據,這個時候就需要淘汰掉一部分的數據,如果按照嚴格的 LRU 算法,應該淘汰掉的是最先加入的 n / 2 的數據。
生成如下各 LRU 算法的對比圖
圖片來源:segmentfault.com/a/1190000017555834
你可以看到圖中有三種不同顏色的點:
淺灰色是被淘汰的數據
灰色是沒有被淘汰掉的老數據
綠色是新加入的數據
我們能看到 Redis3.0 采樣數是 10 生成的圖最接近于嚴格的 LRU。而同樣使用 5 個采樣數,Redis3.0 也要優于 Redis2.8。
LFU 算法
LFU 算法是 Redis4.0 里面新加的一種淘汰策略。它的全稱是 Least Frequently Used,它的核心思想是根據 key 的最近被訪問的頻率進行淘汰,很少被訪問的優先被淘汰,被訪問的多的則被留下來。
LFU 算法能更好的表示一個 key 被訪問的熱度。假如你使用的是 LRU 算法,一個 key 很久沒有被訪問到,只剛剛是偶爾被訪問了一次,那么它就被認為是熱點數據,不會被淘汰,而有些 key 將來是很有可能被訪問到的則被淘汰了。如果使用 LFU 算法則不會出現這種情況,因為使用一次并不會使一個 key 成為熱點數據。
LFU 一共有兩種策略:
volatile-lfu:在設置了過期時間的 key 中使用 LFU 算法淘汰 key
allkeys-lfu:在所有的 key 中使用 LFU 算法淘汰數據
設置使用這兩種淘汰策略跟前面講的一樣,不過要注意的一點是這兩周策略只能在 Redis4.0 及以上設置,如果在 Redis4.0 以下設置會報錯。
關于 Redis 內存滿了如何解決問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注丸趣 TV 行業資訊頻道了解更多相關知識。