12 min read
Redis 8 in Three Layers: Key-Value Cache, Vector Search, and Semantic Caching
Redis stopped being just a cache. Redis 8 folded the Query Engine into core, shipped a native vector data type, and Redis Cloud added a managed semantic cache. This post maps all three layers to the exact version that supports them, with the caveats nobody puts in the marketing page.

TL;DR
- Key-value caching works on every Redis version, but 7.4 through 8.10 added the pieces that make cache code shorter: hash field TTLs,
HGETEX/HSETEX/HGETDEL, conditionalSET IFEQ,MSETEX, and least-recently-modified eviction. - Vector search needs Redis 8.0+ for the built-in Query Engine, 8.2+ for
SVS-VAMANAcompression, and 8.4+ forFT.HYBRID. Vector sets (VADD/VSIM) arrived in 8.0 as beta — patch to 8.8+ before trusting them. - Semantic caching is not a server feature. It is a pattern you build on the Query Engine, or a managed service (LangCache) on Redis Cloud that is still in preview.
- Every capability below is tagged with the minimum version and the caveat that will bite you.
Why this post exists
Search "Redis vector search" today and you get three answers that contradict each other: install Redis Stack, load the RediSearch module, or just use Redis 8. All three were true at some point in the last two years. The module era ended with 8.0, and the feature set has moved every quarter since.
The result is that people ship code against documentation written for a version they are not running. FT.HYBRID examples on a 8.0 server return an unknown-command error. SVS-VAMANA compression benchmarks quoted from an Intel blog silently degrade on your ARM Graviton nodes. Semantic caching tutorials assume a Python service you do not have.
So this is a version map, not a feature tour.
Layer 1 — Redis as a key-value cache
This is the layer everyone already uses: SET, GET, EXPIRE, done. What changed is how much boilerplate you can delete.
The traditional way
// Read-through cache, the version most codebases still run
$key = "user:profile:{$userId}";
$cached = $redis->get($key);
if ($cached === false) {
$profile = $this->repository->findProfile($userId);
$redis->setex($key, 3600, json_encode($profile));
return $profile;
}
return json_decode($cached, true);Nothing wrong with it. The problems show up at scale: one key per field means N round trips, and refreshing a TTL means a second command.
The newer way
Hash field expiration landed in 7.4, and Redis 8.0 added three commands that build on it:
# Redis 8.0+
HSETEX session:abc123 EX 1800 FIELDS 2 cart_id 991 step "checkout"
# Fetch a field AND slide its TTL in one round trip
HGETEX session:abc123 EX 1800 FIELDS 1 cart_id
# Read and delete atomically — one-time tokens, idempotency keys
HGETDEL session:abc123 FIELDS 1 otpRedis 8.4 added compare-and-set semantics on strings, which kills a whole category of Lua scripts:
# Only overwrite if the current value matches — optimistic locking, no WATCH/MULTI
SET job:4821:status "running" IFEQ "queued"
# Set several keys with expiry in one command
MSETEX 60 k1 v1 k2 v2Breaking it down:
IFEQ/IFNE— set only if the current value does (or does not) equal the given valueIFDEQ/IFDNE— the same comparison against the digest, for large valuesMSETEX— multi-key set with conditional expiry management in a single operation
Version map for the cache layer
| Capability | Minimum version | Why you care |
|---|---|---|
| Hash field TTL | 7.4 | Per-field expiry inside one hash |
HGETEX, HSETEX, HGETDEL |
8.0 | Session and token patterns in one round trip |
SET IFEQ / IFNE / IFDEQ / IFDNE, MSETEX |
8.4 | Optimistic locking without Lua |
volatile-lrm / allkeys-lrm eviction |
8.6 | Evict least recently modified, not least recently used |
HOTKEYS |
8.6 | Find your hot keys without sampling in a sidecar |
INCREX |
8.8 | Window-counter rate limiting as one command |
Compact hashes + HIMPORT |
8.10 | Field names stored once across same-schema hashes |
💡 Tip: Compact hashes in 8.10 are the cheapest win on this list if you store millions of same-shape hashes — the field names stop being duplicated per key.
Caveats
⚠️ Warning: Redis 8.0 changed ACL behaviour. Search, JSON, time series, and probabilistic commands were folded into existing categories, so a user with
+@readcan now runFT.SEARCH. Audit your ACL rules before upgrading a shared cluster.
If you are upgrading from 7.x with modules loaded, remove the loadmodule directives first. The modules are built in now, and the commands and data formats are unchanged.
Layer 2 — Vector search
Redis 8 gives you two ways to do similarity search, and picking the wrong one costs you a rewrite.
Option A — Vector sets (Redis 8.0+, beta)
A native data type built by Salvatore Sanfilippo, extending the sorted-set idea: elements have a vector instead of a score.
# Add elements with embeddings and JSON attributes
VADD movies VALUES 1536 0.12 0.44 ... "inception" SETATTR '{"year":2010,"genre":"scifi"}'
# Similarity search with an attribute filter
VSIM movies VALUES 1536 0.11 0.42 ... COUNT 10 WITHSCORES \
FILTER '.year >= 2000 and .genre == "scifi"'Breaking it down:
Q8is the default quantization — roughly 4x memory reduction with minimal recall lossBINgives around 32x reduction, faster, noticeably lower recallNOQUANTkeeps full float precisionREDUCE dimapplies random projection before indexingMdefaults to 16 (HNSW connectivity),EFdefaults to 200 at build time
Scores are not cosine distance. Vectors are normalised on insert and the score is (cosine + 1) / 2, rescaled into [0, 1] where 1 means identical.
| Command | Available since |
|---|---|
VADD, VSIM, VREM, VCARD, VDIM, VEMB, VSETATTR, VGETATTR, VINFO, VLINKS, VRANDMEMBER |
8.0 |
VISMEMBER |
8.2 |
VRANGE |
8.4 |
Caveats — read these before you build on vector sets:
⚠️ Warning: There is no built-in sharding. The official scaling guidance is manual partitioning across keys and nodes, fanning
VSIMout to every shard and merging client-side. Writes scale linearly; reads do not.
- FP32 blobs must be little-endian. On mixed-endian platforms, use the
VALUESsyntax instead — it takes floats as strings and is platform-independent. - Elements with missing attribute fields or invalid JSON are silently excluded from filtered results. No error, just fewer rows.
- Filtered search explores
COUNT * 100candidates by default — a tight filter on a large set can return less than you asked for. node-redisrequiresRESP: 3on the client for theV*commands. Predis exposesvadd/vsim/vsetattrnatively, so Laravel works without raw command calls.- Redis 8.8 patched three memory-safety bugs in vector sets — an RDB node-validation gap, a use-after-free when
VREMmutates the HNSW graph while backgroundVSIMthreads run, and an unsigned-count overflow. 8.4.3 fixed aVADDcrash on largeREDUCEvalues. Run 8.8+ before anything else.
Option B — The Query Engine (Redis 8.0+)
This is the production path for anything document-shaped.
FT.CREATE docs ON HASH PREFIX 1 doc: SCHEMA
title TEXT WEIGHT 2.0
tenant TAG
published NUMERIC SORTABLE
embedding VECTOR HNSW 6 TYPE FLOAT32 DIM 1536 DISTANCE_METRIC COSINEThree index types are supported: FLAT, HNSW, and SVS-VAMANA.
SVS-VAMANA (Redis 8.2+) combines the Vamana graph algorithm with Intel's compression:
FT.CREATE docs ON HASH PREFIX 1 doc: SCHEMA
embedding VECTOR SVS-VAMANA 12
TYPE FLOAT32 DIM 768 DISTANCE_METRIC COSINE
GRAPH_MAX_DEGREE 64
CONSTRUCTION_WINDOW_SIZE 200
COMPRESSION LVQ4x8| Metric | SVS-VAMANA vs HNSW |
|---|---|
| Total memory savings | 26–37% |
| Index memory alone | 51–74% reduction |
| Query throughput (FP32) | up to +144% QPS |
| p50 / p95 latency | up to 60% lower |
| Recall | matches HNSW precision levels |
Source: Redis Query Engine quantization benchmarks
⚠️ Warning: Those numbers depend on Intel. LVQ and LeanVec need Intel platforms with SVS optimizations enabled. On AMD, ARM, and plain Redis Open Source builds, compression falls back to SQ8. The Intel LVQ/LeanVec binaries are also closed-source and license-incompatible with AGPLv3 and SSPLv1 — RSALv2 only. And
SVS-VAMANAaccepts FLOAT16 or FLOAT32 only.
FT.HYBRID (Redis 8.4+) is the headline change. Before it, combining full-text relevance with vector similarity meant two queries and hand-rolled score merging. Now it is one execution plan:
FT.HYBRID docs
SEARCH "caregiver medication schedule" YIELD_SCORE_AS text_score
VSIM @embedding $vec KNN 10 EF_RUNTIME 200
COMBINE RRF 2 CONSTANT 60 WINDOW 20 YIELD_SCORE_AS hybrid
LOAD 3 title url body
PARAMS 2 vec <binary_blob>Breaking it down:
COMBINE RRF— Reciprocal Rank Fusion, the default.WINDOWdefaults to 20,CONSTANTto 60COMBINE LINEAR— weighted sum withALPHAandBETALOAD— required if you want fields back. Without it the command returns only document IDs and scores that the ACL user can read
| Query Engine feature | Minimum version |
|---|---|
Built-in FT.* (no module) |
8.0 |
INT8 / UINT8 vector types |
8.0 |
SVS-VAMANA index type |
8.2 |
SHARD_K_RATIO for KNN |
8.2 |
FT.HYBRID |
8.4 |
FT.ALIASLIST |
8.10 |
Layer 3 — Semantic caching
Here is the thing the marketing pages blur: there is no semantic cache command in Redis Open Source. Not in 8.0, not in 8.10. It is a pattern, and you get it one of three ways.
Path 1 — Build it on the Query Engine (Redis 8.0+)
Works on any Redis 8, self-hosted, any license.
FT.CREATE llmcache ON HASH PREFIX 1 cache: SCHEMA
prompt TEXT
response TEXT
tenant TAG
model TAG
prompt_ver TAG
embedding VECTOR HNSW 6 TYPE FLOAT32 DIM 1536 DISTANCE_METRIC COSINE# Hard filters first, similarity second
FT.SEARCH llmcache "(@tenant:{acme} @model:{gpt-4o} @prompt_ver:{v7})=>[KNN 1 @embedding $vec AS dist]" \
PARAMS 2 vec <blob> \
SORTBY dist \
RETURN 2 response dist \
DIALECT 2If dist exceeds your threshold, it is a miss: call the model, then HSET the entry and EXPIRE it.
Path 2 — RedisVL SemanticCache (Python)
The same pattern with embedding generation, thresholds, TTL, and metadata filters already wired. Python-only — from Laravel or Node you are writing Path 1 by hand or calling out to a small Python service.
Path 3 — LangCache (Redis Cloud, preview)
A fully managed semantic cache behind a REST API: embeddings generated for you, configurable distance thresholds, automatic eviction, built-in metrics, no index to manage.
⚠️ Warning: LangCache is Redis Cloud only and still labelled preview — public preview on Cloud, private preview elsewhere. Features and behaviour are subject to change. Do not put it on a critical path you cannot rewrite.
In May 2026 Redis folded LangCache into Redis Iris, its context engine, alongside Redis Search, Redis Data Integration, and two new preview pieces (Context Retriever and Agent Memory). RDI went GA on 18 May 2026. For response caching specifically, Iris is packaging — LangCache is the same service under a bigger name.
RedisVL also ships a LangCacheSemanticCache wrapper with the same check / store surface as the self-hosted class, so you can start on Path 1 or 2 and swap backends later without touching call sites.
| DIY on Query Engine | RedisVL SemanticCache |
LangCache | |
|---|---|---|---|
| Minimum Redis | 8.0 (any deployment) | 8.0 (any deployment) | Redis Cloud only |
| Status | Stable | Stable | ✅ Preview |
| Language | Any client | Python | Any (REST) |
| Embeddings | You generate | Handled | Handled |
| Threshold tuning | You | You | Configurable |
| Metrics | You build | Partial | Built in |
The part that actually breaks in production
Threshold tuning is the entire problem. Too loose and you serve a confidently wrong answer to a differently-worded question. Too tight and hit rate collapses to near zero and you have paid for embeddings for nothing.
Redis's own guidance is to pair soft similarity with hard metadata boundaries — tenant, locale, model version, safety flags — so reuse stays inside limits you defined.
Three rules I would not ship without:
- Tenant is a TAG pre-filter, never a distance assumption. In a multi-tenant product, cross-tenant leakage is not a relevance bug, it is an incident.
- Model and prompt-template version belong in the filter. Otherwise a prompt change keeps serving answers generated under the old system prompt, invisibly.
- Understand the cost model. You save the output token cost on a hit; input token cost is roughly offset by embedding and storage cost. The win scales with response length and repeat rate. Long answers to FAQ-shaped questions cache beautifully. Short answers to long unique prompts do not.
When to use what
| Use case | Reach for | Minimum version |
|---|---|---|
| Session store, rate limits, hot rows | Plain key-value + hash TTLs | 7.4 / 8.0 |
| "Find similar items" with no metadata | Vector sets | 8.8 (for the fixes) |
| RAG over documents with filters | Query Engine + HNSW | 8.0 |
| Same, but memory-bound on Intel | Query Engine + SVS-VAMANA | 8.2 |
| Text relevance and semantic ranking | FT.HYBRID |
8.4 |
| Cutting LLM spend on repeat questions | Semantic cache pattern | 8.0 |
| Managed semantic cache, no ops | LangCache | Redis Cloud |
Production checklist
- Pin your Redis version in CI. Feature detection at runtime beats a 3am
unknown command 'FT.HYBRID'. - Run 8.8 or later if you touch vector sets. Three memory-safety CVEs were fixed there and one more in 8.4.3.
- Audit ACLs before upgrading past 8.0 —
+@readnow grantsFT.SEARCHand friends. - Strip
loadmoduledirectives when upgrading from 7.x with RediSearch/RedisJSON loaded. - Benchmark SVS-VAMANA on your actual hardware. The published gains assume Intel with SVS optimizations; everywhere else you get the SQ8 fallback.
- Check your license. LVQ/LeanVec binaries are RSALv2 only — incompatible with AGPLv3 and SSPLv1 builds.
- Always
LOADfields inFT.HYBRIDunless IDs and scores are genuinely all you need. - Put tenant, model, and prompt version in TAG filters on any semantic cache, before you tune a single distance threshold.
- Instrument cache hit rate and false-hit rate separately. Hit rate alone will happily go up while quality goes down.
Conclusion
I treat these as three separate decisions, not one Redis decision. The key-value layer is a solved problem and every version since 7.4 has just made the code shorter. The vector layer is where version matters most — 8.0 gets you in the door, 8.2 gets you compression, 8.4 gets you hybrid ranking, and each step is a real capability gap rather than a nice-to-have.
The semantic cache is the one I would build last. Get retrieval right first, measure how repetitive your traffic actually is, then decide whether the pattern earns its threshold-tuning cost. Start with Path 1 on a Redis you already run, keep the check/store interface narrow, and swap in LangCache later only if the metrics and ops burden justify it.
FAQ
Do I need Redis Stack for vector search in 2026?
No. Redis 8.0 merged Redis Search, JSON, time series, and the probabilistic structures into Redis Open Source. If you are on Redis 8+, FT.CREATE and FT.SEARCH work with no loadmodule directive.
What is the difference between vector sets and the Redis Query Engine?
Vector sets are a native data type with a small V* command surface (VADD, VSIM) for pure similarity lookups. The Query Engine indexes hashes and JSON documents and supports full-text, tags, numerics, geo, aggregations, and hybrid ranking. Use vector sets for a similarity lookup, the Query Engine for document retrieval.
Which Redis version do I need for FT.HYBRID?
Redis Open Source 8.4.0 or later. Below that, you have to run a text query and a KNN query separately and merge the scores in your application.
Is semantic caching built into Redis Open Source?
No. There is no semantic cache command in any Redis 8.x server release. You either build the pattern yourself on the Query Engine, use RedisVL's SemanticCache class in Python, or use LangCache, the managed service on Redis Cloud.
Are Redis vector sets safe for production?
They shipped as beta in 8.0 and the API has kept expanding since. Redis 8.8 also patched three memory-safety bugs specific to vector sets, so run 8.8 or later if you use them, and check the release notes for the version you deploy.
Can I use SVS-VAMANA compression on AMD or ARM servers?
You can use the SVS-VAMANA index type, but the LVQ and LeanVec compression paths need Intel platforms with SVS optimizations. Elsewhere it falls back to 8-bit scalar quantization (SQ8).
How do I stop a semantic cache from serving the wrong answer?
Pair the similarity threshold with hard metadata filters — tenant, locale, model version, prompt template version — as TAG pre-filters. Never rely on vector distance alone to enforce a boundary that must not be crossed.