← Back to list

Redis 常用的缓存淘汰策略

LRU(Least Recently Used,最近最少使用)

sumingcheng · 2025-07-29 04:21 · 0 claps · 5.7 min read
#redis #lru #lfu
Open on Medium ↗

Redis 常用的缓存淘汰策略

LRU(Least Recently Used,最近最少使用

Redis原生支持,通过设置maxmemory-policyallkeys-lruvolatile-lru


package main

import (
 "container/list"
 "fmt"
)

// LRUCache 实现了最近最少使用缓存策略
type LRUCache struct {
 capacity int                      // 缓存容量
 cache    map[string]*list.Element // 哈希表存储键到链表节点的映射
 list     *list.List               // 双向链表按访问顺序存储元素
}

// entry 表示缓存中的键值对
type entry struct {
 key   string
 value interface{}
}

// NewLRUCache 创建指定容量的LRU缓存
func NewLRUCache(capacity int) *LRUCache {
 return &LRUCache{
  capacity: capacity,
  cache:    make(map[string]*list.Element),
  list:     list.New(),
 }
}

// Get 获取键对应的值,同时将该键移到最近使用位置
// 返回值和是否命中缓存
func (c *LRUCache) Get(key string) (interface{}, bool) {
 if element, found := c.cache[key]; found {
  // 将访问的元素移动到链表头部,表示最近使用
  c.list.MoveToFront(element)
  return element.Value.(*entry).value, true
 }
 return nil, false // 缓存未命中
}

// Put 向缓存添加或更新键值对
func (c *LRUCache) Put(key string, value interface{}) {
 // 如果键已存在,更新值并移到最近使用位置
 if element, found := c.cache[key]; found {
  c.list.MoveToFront(element)
  element.Value.(*entry).value = value
  return
 }

 // 如果缓存已满,删除最久未使用的元素(链表尾部)
 if c.list.Len() >= c.capacity {
  oldest := c.list.Back()
  if oldest != nil {
   delete(c.cache, oldest.Value.(*entry).key)
   c.list.Remove(oldest)
  }
 }

 // 添加新元素到链表头部
 element := c.list.PushFront(&entry{key, value})
 c.cache[key] = element
}

LFU(Least Frequently Used,最不经常使用

Redis 4.0+原生支持,设置maxmemory-policyallkeys-lfuvolatile-lfu

package main

import (
 "container/heap"
 "fmt"
)

// freqEntry 表示带有访问频率的缓存项
type freqEntry struct {
 key      string      // 键
 value    interface{} // 值
 freq     int         // 访问频率
 heapIdx  int         // 在堆中的索引位置
}

// freqHeap 实现一个最小堆,按频率排序
type freqHeap []*freqEntry

// 实现heap.Interface需要的方法
func (h freqHeap) Len() int { return len(h) }
func (h freqHeap) Less(i, j int) bool { return h[i].freq < h[j].freq } // 按频率升序
func (h freqHeap) Swap(i, j int) {
 h[i], h[j] = h[j], h[i]
 h[i].heapIdx = i // 更新索引位置
 h[j].heapIdx = j
}
func (h *freqHeap) Push(x interface{}) { *h = append(*h, x.(*freqEntry)) }
func (h *freqHeap) Pop() interface{} {
 old := *h
 n := len(old)
 x := old[n-1]
 *h = old[0 : n-1]
 return x
}

// LFUCache 实现了最不经常使用缓存策略
type LFUCache struct {
 capacity int                  // 缓存容量
 cache    map[string]*freqEntry // 哈希表存储键到缓存项的映射
 heap     freqHeap              // 最小堆维护频率顺序
}

// NewLFUCache 创建指定容量的LFU缓存
func NewLFUCache(capacity int) *LFUCache {
 return &LFUCache{
  capacity: capacity,
  cache:    make(map[string]*freqEntry),
  heap:     make(freqHeap, 0),
 }
}

// Get 获取键对应的值,同时增加访问频率
// 返回值和是否命中缓存
func (c *LFUCache) Get(key string) (interface{}, bool) {
 if entry, found := c.cache[key]; found {
  entry.freq++ // 增加访问频率
  heap.Fix(&c.heap, entry.heapIdx) // 调整堆以维持正确顺序
  return entry.value, true
 }
 return nil, false // 缓存未命中
}

// Put 向缓存添加或更新键值对
func (c *LFUCache) Put(key string, value interface{}) {
 // 如果键已存在,更新值并增加频率
 if entry, found := c.cache[key]; found {
  entry.value = value
  entry.freq++
  heap.Fix(&c.heap, entry.heapIdx) // 调整堆位置
  return
 }

 // 如果缓存已满,删除访问频率最低的元素
 if len(c.cache) >= c.capacity {
  evicted := heap.Pop(&c.heap).(*freqEntry)
  delete(c.cache, evicted.key)
 }

 // 添加新元素,初始频率为1
 entry := &freqEntry{key: key, value: value, freq: 1}
 c.cache[key] = entry
 heap.Push(&c.heap, entry)
 entry.heapIdx = len(c.heap) - 1 // 更新堆索引
}

메타데이터
post_id
55ba29137bcd
slug
redis-常用的缓存淘汰策略-55ba29137bcd
url
https://medium.com/@sumingcheng/redis-%E5%B8%B8%E7%94%A8%E7%9A%84%E7%BC%93%E5%AD%98%E6%B7%98%E6%B1%B0%E7%AD%96%E7%95%A5-55ba29137bcd
canonical_url
https://medium.com/@sumingcheng/redis-%E5%B8%B8%E7%94%A8%E7%9A%84%E7%BC%93%E5%AD%98%E6%B7%98%E6%B1%B0%E7%AD%96%E7%95%A5-55ba29137bcd
author_url
https://medium.com/@sumingcheng
status
ok
fetched_at
2026-06-17 08:20:12