您好,登录后才能下订单哦!
# LRU缓存算法怎么用
## 一、什么是LRU缓存算法
LRU(Least Recently Used)即"最近最少使用",是一种常用的缓存淘汰策略。其核心思想是:**当缓存空间不足时,优先淘汰最久未被访问的数据**。
### 1.1 基本工作原理
- 维护一个有序的数据结构(通常为双向链表+哈希表)
- 新访问的数据移动到"最近使用"端
- 长时间未被访问的数据逐渐向"最少使用"端移动
- 当需要淘汰数据时,从"最少使用"端移除
### 1.2 算法特点
| 特点 | 说明 |
|------|------|
| 时间复杂度 | 理想情况下O(1)的读写操作 |
| 空间复杂度 | O(n)的额外空间开销 |
| 适用场景 | 访问具有局部性特征的场景 |
## 二、LRU算法的典型应用场景
### 2.1 数据库缓存
MySQL等数据库的Buffer Pool使用改进版LRU算法管理内存中的页缓存。
```sql
-- MySQL配置示例
SET GLOBAL innodb_buffer_pool_size = 2147483648; -- 2GB缓冲池
浏览器使用LRU管理本地存储资源: - 最近访问的网页资源保留在内存 - 长时间未访问的资源被淘汰
CDN节点使用LRU管理热门内容缓存:
# 伪代码示例
def handle_request(url):
if url in cache:
move_to_front(url)
return cache[url]
else:
data = fetch_from_origin(url)
if cache.full():
evict_last_used()
cache.add(url, data)
return data
Linux内核的页面缓存机制采用类似LRU的时钟算法。
public class LRUCache {
class DLinkedNode {
int key;
int value;
DLinkedNode prev;
DLinkedNode next;
}
private Map<Integer, DLinkedNode> cache = new HashMap<>();
private int size;
private int capacity;
private DLinkedNode head, tail;
public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
head = new DLinkedNode();
tail = new DLinkedNode();
head.next = tail;
tail.prev = head;
}
public int get(int key) {
DLinkedNode node = cache.get(key);
if (node == null) return -1;
moveToHead(node);
return node.value;
}
public void put(int key, int value) {
DLinkedNode node = cache.get(key);
if (node == null) {
node = new DLinkedNode();
node.key = key;
node.value = value;
cache.put(key, node);
addToHead(node);
if (++size > capacity) {
DLinkedNode tail = removeTail();
cache.remove(tail.key);
--size;
}
} else {
node.value = value;
moveToHead(node);
}
}
private void addToHead(DLinkedNode node) {
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
private void removeNode(DLinkedNode node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void moveToHead(DLinkedNode node) {
removeNode(node);
addToHead(node);
}
private DLinkedNode removeTail() {
DLinkedNode res = tail.prev;
removeNode(res);
return res;
}
}
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity: int):
self.cache = OrderedDict()
self.capacity = capacity
def get(self, key: int) -> int:
if key not in self.cache:
return -1
self.cache.move_to_end(key)
return self.cache[key]
def put(self, key: int, value: int) -> None:
if key in self.cache:
self.cache.move_to_end(key)
self.cache[key] = value
if len(self.cache) > self.capacity:
self.cache.popitem(last=False)
class LRUCache extends LinkedHashMap<Integer, Integer> {
private int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75F, true);
this.capacity = capacity;
}
public int get(int key) {
return super.getOrDefault(key, -1);
}
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
return size() > capacity;
}
}
记录最近K次访问记录,解决”偶发访问污染”问题。
class LRUKCache:
def __init__(self, capacity, k):
self.history = defaultdict(deque) # 访问历史记录
self.cache = {} # 实际缓存
self.capacity = capacity
self.k = k
动态调整LRU和LFU的比例,IBM研究显示比LRU提高25%命中率。
// Go语言批量写入示例
func (c *LRUCache) BatchPut(entries map[int]int) {
c.lock.Lock()
defer c.lock.Unlock()
for key, value := range entries {
if node, exists := c.cache[key]; exists {
c.moveToFront(node)
node.value = value
} else {
node := &Node{key: key, value: value}
c.addToFront(node)
c.cache[key] = node
if len(c.cache) > c.capacity {
deleted := c.removeFromTail()
delete(c.cache, deleted.key)
}
}
}
}
// C++预分配示例
class LRUCache {
private:
struct Node {
int key;
int value;
Node* prev;
Node* next;
};
std::vector<Node> nodePool;
size_t poolIndex = 0;
Node* newNode(int key, int value) {
if (poolIndex >= nodePool.size()) {
nodePool.resize(nodePool.size() * 2);
}
Node* node = &nodePool[poolIndex++];
node->key = key;
node->value = value;
return node;
}
};
// Java分片锁示例
class ShardedLRUCache {
private final int SHARD_COUNT = 16;
private final LRUCache[] shards;
public ShardedLRUCache(int capacity) {
shards = new LRUCache[SHARD_COUNT];
int perShard = (capacity + SHARD_COUNT - 1) / SHARD_COUNT;
for (int i = 0; i < SHARD_COUNT; i++) {
shards[i] = new LRUCache(perShard);
}
}
private int getShardIndex(int key) {
return key % SHARD_COUNT;
}
public int get(int key) {
return shards[getShardIndex(key)].get(key);
}
}
Redis采用随机采样法实现近似LRU:
# Redis配置
maxmemory 1gb
maxmemory-policy allkeys-lru
采样过程: 1. 维护一个全局LRU时钟 2. 随机选取5个key 3. 淘汰最久未使用的key
InnoDB缓冲池使用”young/old”分区设计:
-- 查看相关参数
SHOW VARIABLES LIKE 'innodb_old%';
工作流程: 1. 新页加入old sublist头部 2. 再次访问时移动到young sublist 3. 淘汰总是从old sublist尾部进行
# 启动参数示例
memcached -m 64 -o modern # 使用现代LRU算法
特性: - 多级LRU队列(hot/warm/cold) - 惰性淘汰机制 - 动态item迁移
问题类型 | 基础LRU | 优化方案 | 改进效果 |
---|---|---|---|
缓存污染 | 敏感 | LRU-K | 提升30%+命中率 |
冷启动 | 严重 | 预加载 | 减少50%冷启动时间 |
扫描操作 | 无抵抗 | 扫描检测 | 避免90%无效淘汰 |
# Prometheus监控示例
lru_cache_hits_total{cache="user_profile"}
lru_cache_misses_total{cache="user_profile"}
lru_cache_evictions_total{cache="user_profile"}
lru_cache_size_bytes{cache="user_profile"}
# Locust性能测试脚本
from locust import HttpUser, task
class LRUCacheUser(HttpUser):
@task
def test_cache(self):
key = random.randint(1, 10000)
self.client.get(f"/api/data?key={key}")
@task(3)
def test_cache_miss(self):
key = random.randint(10001, 20000)
self.client.get(f"/api/data?key={key}")
“在计算机科学中,所有问题都可以通过增加一个间接层来解决,除了太多间接层导致的问题。” —— David Wheeler
通过合理应用LRU及其变种算法,可以显著提升系统性能。建议开发者根据实际场景选择合适的实现方案,并建立完善的监控体系。 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。