在高性能 Web 应用中,缓存是提升响应速度、降低数据库压力的关键。多级缓存(本地缓存 + 分布式缓存如 Redis)结合了两者的优势:本地缓存速度快,Redis 提供分布式一致性。本文将带你使用 Rust 实现一个多级缓存方案,结合本地缓存库(如 moka)和 Redis 客户端(如 redis-rs),打造高效、可靠的缓存系统。
本文适合有一定 Rust 基础的开发者,想了解如何在实际项目中应用缓存机制。
为什么需要多级缓存?在高并发场景下,直接查询数据库会导致性能瓶颈。缓存可以显著降低延迟,但单一缓存方案有局限:
多级缓存结合两者优势:
接下来,我们用 Rust 实现一个简单的多级缓存系统,用于缓存用户信息。项目初始化创建一个新的 Rust 项目:
cargo new multilevel-cache
cd multilevel-cache在 Cargo.toml 中添加依赖:
[dependencies]
moka = "0.12" # 本地缓存库
redis = { version = "0.27", features = ["tokio-comp"] } # Redis 客户端
tokio = { version = "1.40", features = ["full"] } # 异步运行时
serde = { version = "1.0", features = ["derive"] } # 序列化/反序列化
serde_json = "1.0" # JSON 序列化数据模型假设我们要缓存用户信息,定义一个 User 结构体:
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone)]
struct User {
id: u64,
name: String,
email: String,
}实现多级缓存我们将实现一个 Cache 结构体,封装本地缓存(moka)和 Redis 缓存的逻辑。1. 定义缓存结构体
use moka::future::Cache as MokaCache;
use redis::{AsyncCommands, Client};
use std::time::Duration;
struct Cache {
local: MokaCache<String, User>, // 本地缓存
redis: Client, // Redis 客户端
}
impl Cache {
async fn new(redis_url: &str) -> Self {
// 初始化本地缓存,设置 TTL 为 60 秒
let local = MokaCache::builder()
.time_to_live(Duration::from_secs(60))
.build();
// 初始化 Redis 客户端
let redis = Client::open(redis_url).expect("Failed to connect to Redis");
Cache { local, redis }
}
}2. 实现缓存查询逻辑添加 get_user 方法,实现多级缓存查询:
impl Cache {
async fn get_user(&self, user_id: u64) -> Option<User> {
let key = format!("user:{}", user_id);
// 1. 查本地缓存
if let Some(user) = self.local.get(&key) {
println!("Hit local cache for user: {}", user_id);
return Some(user);
}
// 2. 查 Redis 缓存
let mut conn = match self.redis.get_async_connection().await {
Ok(conn) => conn,
Err(_) => return None, // Redis 连接失败,返回 None
};
let redis_result: Option<String> = conn
.get(&key)
.await
.ok();
if let Some(json) = redis_result {
if let Ok(user) = serde_json::from_str(&json) {
println!("Hit Redis cache for user: {}", user_id);
// 回写本地缓存
self.local.insert(key.clone(), user.clone()).await;
return Some(user);
}
}
// 3. Redis 未命中,模拟查询数据库
let user = self.query_db(user_id).await?;
// 回写 Redis 和本地缓存
let json = serde_json::to_string(&user).unwrap();
conn.set_ex(&key, &json, 3600).await.ok(); // Redis TTL 为 3600 秒
self.local.insert(key, user.clone()).await;
println!("Fetched from DB for user: {}", user_id);
Some(user)
}
// 模拟数据库查询
async fn query_db(&self, user_id: u64) -> Option<User> {
// 这里模拟数据库查询,实际项目中替换为数据库操作
Some(User {
id: user_id,
name: format!("User{}", user_id),
email: format!("user{}@example.com", user_id),
})
}
}逻辑说明:
3. 主函数与测试在 main.rs 中编写主函数,测试缓存逻辑:
#[tokio::main]
async fn main() {
// 初始化缓存
let cache = Cache::new("redis://localhost:6379").await;
// 测试查询
for _ in 0..2 {
if let Some(user) = cache.get_user(1).await {
println!("User: id={}, name={}, email={}", user.id, user.name, user.email);
} else {
println!("User not found");
}
}
}运行程序(确保 Redis 服务运行):
bash
cargo run
第一次查询输出:
Fetched from DB for user: 1
User: id=1, name=User1, email=user1@example.com第二次查询输出:
Hit local cache for user: 1
User: id=1, name=User1, email=user1@example.com第二次查询命中本地缓存,速度更快。如果本地缓存过期(60 秒后),会尝试命中 Redis。
最佳实践
asyncfnget_user(&self, user_id:u64)->Result<User,String>{
// ... 错误处理逻辑
}
3.缓存一致性:
self.local.invalidate(&key).await;4. 性能优化:
let local = MokaCache::builder()
.time_to_live(Duration::from_secs(60))
.max_capacity(10_000)
.build();扩展:生产环境中的注意事项
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_cache_hit() {
let cache = Cache::new("redis://localhost:6379").await;
let user1 = cache.get_user(1).await.unwrap();
let user2 = cache.get_user(1).await.unwrap();
assert_eq!(user1.id, user2.id);
}
}总结通过 Rust、Moka 和 Redis,我们实现了一个高效的多级缓存系统,结合本地缓存的低延迟和 Redis 的分布式能力。本文展示了一个简单但实用的案例,涵盖了初始化、查询逻辑和最佳实践。在生产环境中,你可以根据需求调整 TTL、优化序列化或添加监控,构建更健壮的缓存系统。