跪拜 Guibai
← All articles
Redis

Redis Data Structures and NestJS Integration for AI Chat Context

By 为你学会写情书 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Redis is the default session and cache store across most backend stacks, but developers often reach for it without understanding which data structure fits their access pattern. Picking the wrong type — Hash when you need a queue, or String when you need field-level updates — creates serialization overhead and loses atomicity. The List-plus-pipeline pattern shown here is a reusable blueprint for any bounded, append-heavy log, from chat history to audit trails.

Summary

Redis's String, Hash, and List types each solve distinct backend problems. String handles counters and cached blobs; Hash stores objects with field-level updates; List works as a high-performance queue for ordered message streams. Choosing between Hash and List comes down to access pattern: field-based lookups versus append-and-trim sequences.

A complete NestJS integration uses ioredis with a global provider module. The chat service stores each message as a JSON string in a List keyed by user ID, keeping only the last 20 messages via LTRIM and refreshing a 30-minute TTL on every interaction. Pipeline commands batch the RPUSH, LTRIM, and EXPIRE calls into a single atomic operation.

The resulting session store costs O(1) per append, holds constant memory per user, and self-destructs after inactivity. The same pattern extends to rate limiting, task queues, or any ordered log that needs bounded retention.

Takeaways
Redis's String type is binary-safe and supports atomic INCR, SETEX, and SETNX — the primitives behind counters, expiring caches, and distributed locks.
Hash stores objects with O(1) field-level reads and writes, automatically switching from a compact listpack to a hash table as field count grows.
List is a double-ended queue; RPUSH and LPUSH are O(1), while mid-list access is O(n), making it ideal for message queues and timelines.
Use Hash when you need to update individual fields of an object; use List when you need an ordered, append-heavy sequence with trimming.
TTL expiration combines lazy deletion (check on access) with periodic scanning (every 100ms) to balance performance and memory reclamation.
Sliding expiration — resetting TTL on each user action — keeps sessions alive only while active, preventing premature eviction.
In NestJS, a global Redis module built with ioredis exposes a REDIS_CLIENT token that any service can inject without re-registering the provider.
Pipeline commands batch multiple Redis operations into one atomic round-trip, avoiding race conditions when appending, trimming, and expiring a key.
The chat context pattern uses a List keyed by user ID, stores JSON messages, keeps the last 20 entries with LTRIM, and refreshes a 30-minute TTL on every interaction.
Conclusions

Many developers treat Redis as a dumb cache, but the List-plus-pipeline pattern for chat history shows it functioning as a lightweight, self-purging database — no separate cleanup job required.

The Hash-versus-List decision table is a useful heuristic, but the article's real insight is that production systems often combine both: Hash for user metadata, List for the message stream, each doing what it does best.

Sliding expiration is underused in practice. Resetting TTL on every user action turns a fixed window into an activity-based window, which matches how most session and context lifetimes should actually work.

Concepts & terms
SDS (Simple Dynamic String)
Redis's own string implementation that replaces C's null-terminated strings. It stores length explicitly for O(1) length retrieval and uses pre-allocation to reduce memory reallocations during appends.
listpack
A compact, contiguous memory structure Redis uses for small Hashes (and other types) before converting to a hash table. It saves memory when field counts are low.
Sliding expiration
A TTL pattern where the expiration timer resets on each user interaction — typically by calling EXPIRE again — so data lives exactly as long as the user remains active, then self-destructs after a fixed idle period.
Redis pipeline
A client-side batching mechanism that sends multiple commands to Redis in a single network round-trip without waiting for individual replies. It is not a transaction (no rollback), but it avoids interleaving from other clients when used on a single connection.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗