

























在上一篇《rust高并发设计实践 》中,我们列举了三个实例,本地缓存,生产者消费者的两个模型,每个章节后面有一些进阶提示,
有小伙伴可能会问具体如何落地呢?这里带着疑问给出具体的实现,希望对大家有帮助。
为了彻底解决 Vec 带来的 $O(N)$ 插入与删除瓶颈,我们在精确清除部分引入标准最小堆(Min-Heap / 二叉堆)结构(在 Rust 中对应 std::collections::BinaryHeap)。
peek)永远是最早到期的条目。后台线程只需以 $O(1)$ 的时间复杂度检查堆顶是否到期。insert 时,将过期时间存入堆中,时间复杂度仅为 $O(\log N)$。以下是整合了分段锁、零拷贝 Guard 以及最小堆精准驱动的完整重构代码:
use std::collections::{BinaryHeap, HashMap};
use std::hash::{Hash, Hasher};
use std::sync::{Arc, Mutex, RwLock};
use std::time::{Duration, Instant};
use std::thread;
use std::cmp::Ordering;
// ==========================================
// 1. 内存分配优化:零拷贝核心存储结构
// ==========================================
struct CacheEntry<V> {
// 内部采用 Arc 内嵌数据,实现零拷贝读取
value: Arc<V>,
expires_at: Instant,
}
impl<V> CacheEntry<V> {
fn is_expired(&self) -> bool {
Instant::now() > self.expires_at
}
}
// ==========================================
// 2. 最小堆精确清除数据结构设计
// ==========================================
/// 堆内存储的定时器节点
struct DelayNode<K> {
expires_at: Instant,
key: K,
}
// 显式反转 Ordering 逻辑,将标准 BinaryHeap(最大堆)改造为最小堆
impl<K: Eq> Eq for DelayNode<K> {}
impl<K: Eq> PartialEq for DelayNode<K> {
fn eq(&self, other: &Self) -> bool {
self.expires_at == other.expires_at
}
}
impl<K: Eq> Ord for DelayNode<K> {
fn cmp(&self, other: &Self) -> Ordering {
// 反转比较:让最早过期的条目优先级最高(排在堆顶)
other.expires_at.cmp(&self.expires_at)
}
}
impl<K: Eq> PartialOrd for DelayNode<K> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
// ==========================================
// 3. 核心分段锁缓存结构
// ==========================================
struct CacheShard<K, V> {
store: RwLock<HashMap<K, CacheEntry<V>>>,
}
pub struct ConcurrentTtlCache<K, V> {
/// 32路分段锁,大幅分散万级并发下的全局写锁竞争压力
shards: Arc<Vec<CacheShard<K, V>>>,
shard_mask: usize,
/// 负责精确清除的时间调度最小堆
cleanup_heap: Arc<Mutex<BinaryHeap<DelayNode<K>>>>,
}
impl<K, V> ConcurrentTtlCache<K, V>
where
K: Eq + Hash + Clone + Send + Sync + 'static,
V: Send + Sync + 'static,
{
/// 创建高并发、精准清除、符合惯用法的并发缓存
pub fn new(cleanup_interval: Duration) -> Self {
let shard_count = 32;
let mut shards = Vec::with_capacity(shard_count);
for _ in 0..shard_count {
shards.push(CacheShard {
store: RwLock::new(HashMap::new()),
});
}
let cache = Self {
shards: Arc::new(shards),
shard_mask: shard_count - 1,
cleanup_heap: Arc::new(Mutex::new(BinaryHeap::new())),
};
let shards_clone = Arc::clone(&cache.shards);
let heap_clone = Arc::clone(&cache.cleanup_heap);
let mask = cache.shard_mask;
// 后台精准清理线程(基于最小堆驱动,非全表扫描,开销锁定在 O(1) ~ O(过期数))
thread::spawn(move || loop {
thread::sleep(cleanup_interval);
let now = Instant::now();
let mut expired_keys = Vec::new();
// 1. O(1) 快速探测堆顶
if let Ok(mut heap) = heap_clone.lock() {
while let Some(node) = heap.peek() {
if node.expires_at <= now {
if let Some(popped) = heap.pop() {
expired_keys.push(popped.key);
}
} else {
// 💡 核心设计:只要堆顶未过期,后续全未过期,立刻安全退出循环
break;
}
}
}
// 2. 精准定点移除,结合 try_write 彻底杜绝主业务线延迟突刺
for key in expired_keys {
let shard_idx = Self::get_shard_index(&key, mask);
if let Some(shard) = shards_clone.get(shard_idx) {
if let Ok(mut write_guard) = shard.store.try_write() {
// 双重校验:避免移除中途被用户 insert 覆盖并赋予了新生命周期的有效数据
if let Some(entry) = write_guard.get(&key) {
if entry.is_expired() {
write_guard.remove(&key);
}
}
}
}
}
});
cache
}
/// 插入数据
pub fn insert(&self, key: K, value: V, ttl: Duration) {
let expires_at = Instant::now() + ttl;
let entry = CacheEntry {
value: Arc::new(value),
expires_at,
};
let shard_idx = Self::get_shard_index(&key, self.shard_mask);
let mut write_guard = self.shards[shard_idx].store.write().unwrap();
write_guard.insert(key.clone(), entry);
if let Ok(mut heap) = self.cleanup_heap.lock() {
heap.push(DelayNode { expires_at, key });
}
}
/// 高频零拷贝读取路径
/// 🚀 重构关键:去除了自定义包装,直接返回 Option<Arc<V>>,完美对接标准库生态
pub fn get(&self, key: &K) -> Option<Arc<V>> {
let shard_idx =