Кэширование в Rust: Redis, Memcached и встроенные решения

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Кэширование в Rust: Redis, Memcached и встроенные решения

Кэширование — один из ключевых механизмов оптимизации производительности современных приложений. В экосистеме Rust, известной своей скоростью и безопасностью памяти, выбор правильного инструмента для кэширования может существенно повлиять на эффективность программы. В этой статье мы рассмотрим три основных подхода: внешние системы Redis и Memcached, а также встроенные решения, доступные в стандартной библиотеке и популярных крейтах.

Зачем кэшировать в Rust?

Rust уже сам по себе очень быстр, но при работе с базами данных, внешними API или сложными вычислениями кэширование позволяет:

  • Снизить задержки (latency) при повторных запросах
  • Уменьшить нагрузку на базы данных и внешние сервисы
  • Сэкономить вычислительные ресурсы и время CPU

1. Встроенные решения: HashMap и LRU-кэш

Самый простой способ кэширования в Rust — использование HashMap из стандартной библиотеки. Для многопоточных приложений потребуется обёртка Mutex<HashMap> или RwLock<HashMap>. Однако такой подход не имеет политики вытеснения (eviction policy), что может привести к бесконечному росту памяти.

Пример простого кэша на HashMap

use std::collections::HashMap;
use std::sync::Mutex;

struct SimpleCache {
    store: Mutex<HashMap<String, String>>,
}

impl SimpleCache {
    fn new() -> Self {
        SimpleCache {
            store: Mutex::new(HashMap::new()),
        }
    }

    fn get(&self, key: &str) -> Option<String> {
        self.store.lock().unwrap().get(key).cloned()
    }

    fn set(&self, key: String, value: String) {
        self.store.lock().unwrap().insert(key, value);
    }
}

LRU-кэш через крейт lru

Для ограничения размера кэша и автоматического вытеснения старых записей используйте крейт lru. Он реализует алгоритм Least Recently Used — удаляет реже всего используемые элементы.

use lru::LruCache;
use std::num::NonZeroUsize;

fn main() {
    let mut cache = LruCache::new(NonZeroUsize::new(3).unwrap());
    cache.put("apple", 10);
    cache.put("banana", 20);
    cache.put("cherry", 30);
    cache.put("date", 40); // "apple" будет вытеснен

    println!("{:?}", cache.get(&"apple")); // None
    println!("{:?}", cache.get(&"date"));  // Some(40)
}

Плюсы: нулевая задержка сети, простота, полный контроль. Минусы: кэш живёт только в рамках одного процесса, нет персистентности.

2. Кэширование с Redis через redis-rs

Redis — это in-memory хранилище типа ключ-значение, которое часто используется как распределённый кэш. В Rust для работы с Redis используется крейт redis (или redis-rs).

Подключение и базовая работа

use redis::Commands;

fn main() -> redis::RedisResult<()> {
    let client = redis::Client::open("redis://127.0.0.1:6379/")?;
    let mut con = client.get_connection()?;

    // Установка значения с TTL (время жизни)
    con.set_ex("user:42", "{\"name\": \"Alice\"}", 60)?;

    // Чтение
    let value: String = con.get("user:42")?;
    println!("{}", value);

    // Проверка существования
    let exists: bool = con.exists("user:42")?;
    println!("Exists: {}", exists);

    Ok(())
}

Продвинутые возможности Redis

  • TTL (Time To Live): автоматическое удаление устаревших записей
  • Структуры данных: списки, множества, хэши — для сложных сценариев
  • Pub/Sub: уведомления об изменениях кэша
  • Кластеризация: горизонтальное масштабирование

Плюсы: распределённость, персистентность (опционально), богатый набор команд. Минусы: сетевая задержка, необходимость запускать отдельный сервер.

3. Кэширование с Memcached через memcache-rs

Memcached — ещё одна популярная система кэширования, оптимизированная для простых операций ключ-значение. В Rust для работы с ней используется крейт memcache.

Пример работы с Memcached

use memcache;

fn main() -> memcache::MemcacheResult<()> {
    let client = memcache::connect("memcache://127.0.0.1:11211")?;

    // Установка значения с TTL (в секундах)
    client.set("session:abc", "data_payload", 30)?;

    // Чтение
    let result: Option<String> = client.get("session:abc")?;
    if let Some(data) = result {
        println!("Got: {}", data);
    }

    // Удаление
    client.delete("session:abc")?;

    Ok(())
}

Сравнение Redis vs Memcached

Характеристика Redis Memcached
Типы данных Строки, списки, множества, хэши Только строки
Персистентность Да (RDB/AOF) Нет
Кластеризация Встроенная Через прокси-серверы
Производительность (чтение) ~100k ops/sec (один поток) ~200k ops/sec (многопоточный)
Сложность настройки

Блоги

Рекомендаций по книгам