Einleitung: Warum in Rust cachen?
Rust ist eine systemnahe Programmiersprache, die für ihre Leistung und Speichersicherheit geschätzt wird. Selbst die schnellsten Programme können jedoch bei wiederholten I/O-Operationen, komplexen Berechnungen oder häufigen Datenbankabfragen langsamer werden. Caching ermöglicht es, Ergebnisse teurer Operationen zu speichern und sofort zurückzugeben, was für stark ausgelastete Webdienste, Spiele-Engines und Echtzeitsysteme entscheidend ist.
In diesem Artikel betrachten wir drei Ansätze zum Caching in Rust: externe Systeme (Redis, Memcached) und integrierte Lösungen (lru-rs, moka). Sie erfahren, wie Sie diese in Ihren Code integrieren, welche Aufgaben jedes Werkzeug löst und wie Sie die optimale Variante für Ihr Projekt auswählen.
1. Externes Caching: Redis und Memcached
Externe Cache-Systeme arbeiten als separate Dienste, die von der Anwendung über das Netzwerk angesprochen werden. Sie sind ideal für verteilte Systeme, bei denen mehrere Prozesse oder Server einen gemeinsamen Cache benötigen.
1.1. Redis über die Crate redis
Redis ist der beliebteste In-Memory-Store, der komplexe Datenstrukturen (Listen, Mengen, Hashes) unterstützt. Für die Verbindung zu Redis in Rust wird die Crate redis verwendet.
Installation: Fügen Sie in Cargo.toml hinzu:
[dependencies]redis = { version = "0.25", features = ["tokio-comp"] }tokio = { version = "1", features = ["full"] }Beispiel für einfaches Caching eines Strings:
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?;
// In Cache schreiben für 60 Sekunden let _: () = con.set_ex("user:42", "Alice", 60).await?; // Lesen let val: Option = con.get("user:42").await?; println!("Gecachter Wert: {:?}", val); Ok(())} Redis eignet sich hervorragend zum Cachen von Sessions, API-Antworten und Aufgabenwarteschlangen. Sein Nachteil ist die Notwendigkeit, einen separaten Server zu installieren und zu warten.
1.2. Memcached über die Crate memcache
Memcached ist eine leichtere Alternative zu Redis, die auf einfache Key-Value-Speicherung ausgerichtet ist. Die Crate memcache bietet eine synchrone und asynchrone API.
Beispiel mit der Crate memcache:
use memcache::{Client, MemcacheError};
fn main() -> Result<(), MemcacheError> { let client = Client::connect("memcache://127.0.0.1:11211")?; client.set("counter", 100, 30)?; // TTL = 30 Sekunden let val: Option = client.get("counter")?; println!("Wert: {:?}", val); Ok(())} Memcached ist bei einfachen get/set-Operationen schneller als Redis, unterstützt aber keine komplexen Datenstrukturen. Wählen Sie es, wenn Sie nur "Key-Value" mit minimalem Overhead benötigen.
2. Integrierte Caching-Bibliotheken
Für Einprozess-Anwendungen (CLI-Tools, Desktop-Programme, Spieleserver) ist externes Caching überflüssig. Integrierte Lösungen arbeiten im Speicher des Prozesses, benötigen kein Netzwerk und sind maximal schnell.
2.1. lru-rs — Einfacher LRU-Cache
Die Crate lru implementiert einen Cache mit Least Recently Used-Verdrängung. Ideal zum Cachen von Parsing-Ergebnissen, Konfigurationen oder kleinen Objekten.
use lru::LruCache;use std::num::NonZeroUsize;
fn main() { let mut cache: LruCache = LruCache::new(NonZeroUsize::new(3).unwrap()); cache.put("a", 1); cache.put("b", 2); cache.put("c", 3); cache.put("d", 4); // "a" wird verdrängt, da der Cache voll ist
println!("{:?}", cache.get(&"a")); // None println!("{:?}", cache.get(&"d")); // Some(4)} Vorteile: minimalistisch, vorhersagbare Laufzeit, thread-safe bei Verwendung