Кэширование в Rust: Redis и другие эффективные решения
В современной высоконагруженной разработке на Rust кэширование играет ключевую роль. Оно позволяет снизить нагрузку на базы данных, ускорить ответы API и оптимизировать работу с дорогостоящими вычислениями. В этой статье мы рассмотрим основные библиотеки для кэширования в экосистеме Rust: от мощного Redis до легковесных in-memory решений.
1. Redis через крейт redis-rs
redis-rs — это основная и наиболее зрелая библиотека для работы с Redis в Rust. Она поддерживает асинхронные операции (через tokio или async-std), кластеризацию, pub/sub и все популярные типы данных Redis.
Установка
[dependencies]
redis = { version = "0.25", features = ["tokio-comp"] }
tokio = { version = "1", features = ["full"] }
Базовый пример: кэширование строк
use redis::AsyncCommands;
#[tokio::main]
async fn main() -> redis::RedisResult<()> {
let client = redis::Client::open("redis://127.0.0.1:6379/")?;
let mut con = client.get_multiplexed_async_connection().await?;
// Устанавливаем кэш с TTL 60 секунд
let _: () = con.set_ex("user:123", "{\"name\":\"Alice\"}", 60).await?;
// Получаем значение
let cached: String = con.get("user:123").await?;
println!("Кэшированные данные: {}", cached);
// Проверяем существование ключа
let exists: bool = con.exists("user:123").await?;
println!("Ключ существует: {}", exists);
Ok(())
}
Сериализация сложных структур
Для кэширования структур данных используйте serde в паре с redis:
use serde::{Serialize, Deserialize};
use redis::AsyncCommands;
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: u32,
name: String,
email: String,
}
async fn cache_user(con: &mut redis::aio::MultiplexedConnection, user: &User) {
let json = serde_json::to_string(user).unwrap();
let _: () = con.set_ex(format!("user:{}", user.id), json, 300).await.unwrap();
}
async fn get_cached_user(con: &mut redis::aio::MultiplexedConnection, id: u32) -> Option<User> {
let json: Option<String> = con.get(format!("user:{}", id)).await.unwrap();
json.and_then(|data| serde_json::from_str(&data).ok())
}
2. In-memory кэширование: крейт moka
Moka — это высокопроизводительная библиотека для кэширования в памяти, вдохновленная Caffeine (Java). Она идеально подходит для сценариев, где не требуется внешний сервер, а нужна максимальная скорость.
Особенности moka:
- Автоматическая эвикция по времени (TTL, TTI) и по размеру
- Асинхронная синхронизация (background refresh)
- Безопасность для многопоточного использования (Arc + RwLock)
- Поддержка весовых политик (weighted cache)
Пример: кэш с TTL и ограничением размера
use moka::sync::Cache;
use std::time::Duration;
fn main() {
// Создаём кэш на 1000 элементов с TTL 10 минут
let cache: Cache<String, String> = Cache::builder()
.max_capacity(1000)
.time_to_live(Duration::from_secs(600))
.build();
// Вставка и получение
cache.insert("key1".to_string(), "value1".to_string());
if let Some(v) = cache.get("key1") {
println!("Значение: {}", v);
}
}
Асинхронный вариант (moka::future::Cache)
use moka::future::Cache;
#[tokio::main]
async fn main() {
let cache: Cache<u64, String> = Cache::new(10_000);
// Асинхронная вставка
cache.insert(42, "Hello async".to_string()).await;
// Асинхронное получение
if let Some(val) = cache.get(&42).await {
println!("{}", val);
}
}
3. Декларативное кэширование: крейт cached
Cached — это библиотека-макрос, которая автоматически кэширует результаты функций. Она поддерживает Redis, файловое кэширование и in-memory хранилища.
Пример: кэширование результата функции
use cached::proc_macro::cached;
#[cached(size = 100, time = 300)]
fn expensive_com