设计缓存系统
问题
如何用 Rust 设计一个高性能的内存缓存系统?
答案
架构设计
LRU + TTL 缓存实现
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::time::{Duration, Instant};
struct CacheEntry<V> {
value: V,
expires_at: Instant,
}
pub struct Cache<K, V> {
inner: Arc<RwLock<HashMap<K, CacheEntry<V>>>>,
default_ttl: Duration,
}
impl<K: Eq + std::hash::Hash + Clone, V: Clone> Cache<K, V> {
pub fn new(default_ttl: Duration) -> Self {
let cache = Self {
inner: Arc::new(RwLock::new(HashMap::new())),
default_ttl,
};
// 启动后台清理过期条目
cache.start_cleanup();
cache
}
pub fn get(&self, key: &K) -> Option<V> {
let map = self.inner.read().unwrap();
map.get(key)
.filter(|entry| entry.expires_at > Instant::now())
.map(|entry| entry.value.clone())
}
pub fn set(&self, key: K, value: V) {
let mut map = self.inner.write().unwrap();
map.insert(key, CacheEntry {
value,
expires_at: Instant::now() + self.default_ttl,
});
}
fn start_cleanup(&self) {
let inner = self.inner.clone();
tokio::spawn(async move {
loop {
tokio::time::sleep(Duration::from_secs(60)).await;
let mut map = inner.write().unwrap();
let now = Instant::now();
map.retain(|_, entry| entry.expires_at > now);
}
});
}
}
高性能优化:分片锁
use std::collections::HashMap;
use std::sync::RwLock;
use std::hash::{Hash, Hasher, DefaultHasher};
/// 分片缓存:减少锁竞争
pub struct ShardedCache<K, V> {
shards: Vec<RwLock<HashMap<K, V>>>,
shard_count: usize,
}
impl<K: Hash + Eq, V> ShardedCache<K, V> {
pub fn new(shard_count: usize) -> Self {
let shards = (0..shard_count)
.map(|_| RwLock::new(HashMap::new()))
.collect();
Self { shards, shard_count }
}
fn get_shard(&self, key: &K) -> &RwLock<HashMap<K, V>> {
let mut hasher = DefaultHasher::new();
key.hash(&mut hasher);
let index = hasher.finish() as usize % self.shard_count;
&self.shards[index]
}
pub fn get(&self, key: &K) -> Option<V> where V: Clone {
let shard = self.get_shard(key).read().unwrap();
shard.get(key).cloned()
}
pub fn insert(&self, key: K, value: V) {
let mut shard = self.get_shard(&key).write().unwrap();
shard.insert(key, value);
}
}
缓存策略对比
| 策略 | 适用场景 | Rust 实现 |
|---|---|---|
| LRU | 通用缓存 | lru crate |
| LFU | 热点数据 | moka crate |
| TTL | 时效性数据 | 自定义 + Instant |
| ARC | 自适应 | moka (内置) |
推荐库
moka 是 Rust 最佳的并发缓存库,基于 Java Caffeine 算法,支持 TTL、容量限制、异步加载。
常见面试问题
Q1: 缓存穿透、击穿、雪崩怎么处理?
答案:
| 问题 | 描述 | Rust 解决方案 |
|---|---|---|
| 穿透 | 查询不存在的 key | 布隆过滤器 / 缓存空值 |
| 击穿 | 热点 key 过期 | moka 的 get_with 自动加载(单线程加载) |
| 雪崩 | 大量 key 同时过期 | TTL 加随机偏移 |
moka 的 get_with 内置了防击穿:多个线程同时请求同一个 key,只有一个会执行加载函数,其他等待结果。