Кэширование в Rust: Redis и другие инструменты для ускорения приложений
Кэширование — один из ключевых механизмов оптимизации производительности. В Rust, с его акцентом на скорость и безопасность, выбор правильного инструмента для кэширования может дать значительный прирост. В этой статье рассмотрим популярные библиотеки: redis-rs (с пулом соединений), moka (in-memory кеш) и cached (декораторы функций).
1. Redis: внешний кеш через redis-rs и deadpool
Redis — самый распространённый внешний кэш. Для работы с ним в Rust используется крейт redis-rs. Для production-нагрузок рекомендуется подключать пул соединений через deadpool-redis.
Установка
[dependencies]
redis = { version = "0.25", features = ["tokio-comp"] }
deadpool-redis = "0.14"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
Пример: базовая работа с Redis
use redis::AsyncCommands;
use deadpool_redis::{Config, Runtime};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Создаём пул соединений
let cfg = Config::from_url("redis://127.0.0.1:6379");
let pool = cfg.create_pool(Some(Runtime::Tokio1))?;
// Берём соединение из пула
let mut conn = pool.get().await?;
// Устанавливаем значение с TTL 60 секунд
conn.set_ex("user:42", "{\"name\":\"Alice\",\"role\":\"admin\"}", 60).await?;
// Читаем
let value: String = conn.get("user:42").await?;
println!("Получено: {}", value);
// Удаляем
conn.del("user:42").await?;
Ok(())
}
Сериализация структур
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct User {
id: u64,
name: String,
email: String,
}
async fn cache_user(conn: &mut redis::aio::Connection, user: &User) -> redis::RedisResult<()> {
let key = format!("user:{}", user.id);
let json = serde_json::to_string(user).unwrap();
conn.set_ex(key, json, 300).await
}
async fn get_cached_user(conn: &mut redis::aio::Connection, id: u64) -> Option<User> {
let key = format!("user:{}", id);
let result: Option<String> = conn.get(key).await.ok()?;
result.and_then(|json| serde_json::from_str(&json).ok())
}
2. Moka: быстрый in-memory кеш
Moka — высокопроизводительный in-memory кеш, вдохновлённый Caffeine (Java). Поддерживает TTL, ограничение размера и асинхронное обновление.
Установка
[dependencies]
moka = { version = "0.12", features = ["future"] }
tokio = { version = "1", features = ["full"] }
Пример: кеш с ограничением в 1000 элементов и TTL 10 минут
use moka::future::Cache;
use std::time::Duration;
#[tokio::main]
async fn main() {
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()).await;
// Чтение
if let Some(v) = cache.get(&"key1".to_string()).await {
println!("Найдено: {}", v);
}
// Удаление
cache.invalidate(&"key1".to_string()).await;
}
Автообновление по истечении TTL
let cache: Cache<String, String> = Cache::builder()
.time_to_live(Duration::from_secs(60))
.time_to_idle(Duration::from_secs(30)) // сброс TTL при чтении
.build();
3. Cached: декораторы для функций
Крейт cached позволяет кэшировать результаты функций с помощью атрибутов. Поддерживает различные бэкенды, включая Redis и in-memory.
Установка
[dependencies]
cached = { version = "0.53", features = ["async"] }
tokio = { version = "1", features = ["full"] }
Пример: кеширование асинхронной функции
use cached::proc_macro::cached;
use std::time::Duration;
#[cached