agentleFS
simba-gitsimba-git/redis-best-practices/AGENTS.md

redis-best-practices

AGENTS.md1 starsChanged 10 months agoMITView on GitHub

Version 1.0.0 Redis, Inc. January 2026 This document is optimized for AI agents and LLMs. Rules are prioritized by performance impact. Best practices for Redis including data structures, memory management, Redis Query Engine (RQE), vector search with RedisVL, semantic caching with LangCache, and performance optimization. Optimized for AI agents and LLMs. Impact: HIGH Impact: HIGH (Optimal memory usage and operation performance) Selecting the appropriate Redis data type for your use case is fundamental to performance and memory efficiency.

  • Reads credentials
  • Sends data out
  • Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.

    Of the AGENTS.md files we read, the 31,700 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.

  • In this repositoryOne other file it gives its agents.

The file

Show its rules and text

We split this AGENTS.md into the rules it gives an agent

62 rules. About 1 is common, 51 are distinctive, and 10 are very specific to this project.

  • ≈1

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈51

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 10

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Recommended: Use Hash for objects with fields.

    line 98
  2. Distinctive:

    Recommended: Use colons as separators with a consistent hierarchy.

    line 118
  3. Distinctive:

    Keep keys short but readable—they consume memory

    line 139
  4. Distinctive:

    Use TTL on cache keys to prevent unbounded growth

    line 140
  5. Very specific:

    Avoid KEYS * in production—use SCAN instead

    line 141
  6. Distinctive:

    Consider key prefixes for multi-tenant applications

    line 142
  7. Very specific:

    Always configure maxmemory and an eviction policy to prevent Redis from consuming all available memory.

    line 158
  8. Distinctive:

    Use Hashes for small objects (more memory-efficient than separate keys)

    line 182
  9. Very specific:

    Use OBJECT ENCODING key to check how Redis stores your data

    line 183
  10. Very specific:

    Use MEMORY USAGE key to check individual key memory consumption

    line 184
  11. Distinctive:

    Always set expiration times on cache keys to prevent unbounded memory growth.

    line 195
  12. Distinctive:

    Recommended: Use SCAN for iteration.

    line 244
  13. Distinctive:

    Always configure timeouts to prevent hanging connections.

    line 272
  14. Distinctive:

    Recommended: Use a connection pool.

    line 301
  15. Distinctive:

    Recommended: Use pipeline for multiple commands.

    line 330
  16. Distinctive:

    Recommended: Use JSON for nested structures.

    line 372
  17. Distinctive:

    Recommended: Use Hash for flat objects.

    line 385
  18. Distinctive:

    Use JSON path syntax to update specific fields without fetching the entire document.

    line 404
  19. Distinctive:

    Recommended: Use JSON paths for targeted updates.

    line 406
  20. Distinctive:

    Recommended: Use TAG for exact matching.

    line 469
  21. Distinctive:

    Create indexes with only the fields you need to search, filter, or sort on.

    line 497
  22. Distinctive:

    Start with the minimum required fields

    line 530
  23. Distinctive:

    Add fields as query patterns emerge

    line 531
  24. Very specific:

    Use FT.INFO to monitor index size

    line 532
  25. Distinctive:

    Use aliases to swap indexes without application changes.

    line 542
  26. Distinctive:

    Recommended: Use aliases for production indexes.

    line 544
  27. Very specific:

    Always specify DIALECT 2 (or higher) for modern query syntax and consistent behavior.

    line 586
  28. Distinctive:

    Recommended: Use specific filters and limit results.

    line 622
  29. Very specific:

    Add SORTABLE to fields used in SORTBY

    line 647
  30. Very specific:

    Use TAG SORTABLE UNF for best performance on tag fields

    line 648
  31. Very specific:

    Use NOSTEM if you don't need stemming

    line 649
  32. Distinctive:

    Recommended: Use HNSW for production workloads.

    line 677
  33. Distinctive:

    Recommended: Use FLAT for small datasets or when exact results required.

    line 697
  34. Distinctive:

    Start with HNSW defaults, tune M and EF based on benchmarks

    line 714
  35. Distinctive:

    Use filters to reduce the search space before vector comparison

    line 803
  36. Distinctive:

    Consider chunking long documents for better retrieval

    line 804
  37. Distinctive:

    Use TAG fields for category filters

    line 846
  38. Distinctive:

    Use NUMERIC fields for date/price ranges

    line 847
  39. Distinctive:

    Recommended: Use separate caches for different use cases.

    line 892
  40. Distinctive:

    Start with threshold 0.9, adjust based on your use case

    line 918
  41. Distinctive:

    Use custom attributes to filter results within a single cache

    line 919
  42. Distinctive:

    Use separate cache IDs for fundamentally different LLM tasks

    line 921
  43. Distinctive:

    If found (cache hit), returns the cached response instantly

    line 938
  44. Distinctive:

    If not found (cache miss), your app calls the LLM and stores the response

    line 939
  45. Distinctive:

    Recommended: Use the LangCache Python SDK.

    line 941
  46. Distinctive:

    Recommended: Use Streams when message durability matters.

    line 1027
  47. Distinctive:

    Recommended: Use hash tags for related keys.

    line 1065
  48. Very specific:

    Only the part between { and } is hashed

    line 1095
  49. Distinctive:

    Use meaningful identifiers like user ID or tenant ID

    line 1096
  50. Distinctive:

    Don't overuse - too many keys on one slot creates hotspots

    line 1097
  51. Distinctive:

    Recommended: Read from replicas with Sentinel.

    line 1109
  52. Distinctive:

    Don't read from replicas for data that was just written

    line 1141
  53. Distinctive:

    Use for read-heavy, slightly-stale-OK workloads

    line 1142
  54. Distinctive:

    Never run Redis without authentication in production environments.

    line 1158
  55. Common:

    Recommended: Use password and TLS.

    line 1160
  56. Distinctive:

    Recommended: Use firewall rules.

    line 1207
  57. Distinctive:

    Use TLS for connections

    line 1224
  58. Distinctive:

    Use firewall rules to restrict access

    line 1226
  59. Distinctive:

    Create users with only the permissions they need (principle of least privilege).

    line 1244
  60. Distinctive:

    Recommended: Create specific users with limited permissions.

    line 1246
  61. Distinctive:

    Use Redis Insight for visual monitoring, query profiling, and debugging.

    line 1310
  62. Distinctive:

    Recommended: Check SLOWLOG regularly.

    line 1351

≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
AGENTS.md

Redis Best Practices

Version 1.0.0 Redis, Inc. January 2026

This document is optimized for AI agents and LLMs. Rules are prioritized by performance impact.


Abstract

Best practices for Redis including data structures, memory management, Redis Query Engine (RQE), vector search with RedisVL, semantic caching with LangCache, and performance optimization. Optimized for AI agents and LLMs.


Table of Contents

  1. Data Structures & Keys — HIGH
    • 1.1 Choose the Right Data Structure
    • 1.2 Use Consistent Key Naming Conventions
  2. Memory & Expiration — HIGH
    • 2.1 Configure Memory Limits and Eviction Policies
    • 2.2 Set TTL on Cache Keys
  3. Connection & Performance — HIGH
    • 3.1 Avoid Blocking Commands in Production
    • 3.2 Configure Connection Timeouts
    • 3.3 Use Connection Pooling
    • 3.4 Use Pipelining for Bulk Operations
  4. JSON Documents — MEDIUM
    • 4.1 Choose JSON vs Hash Appropriately
    • 4.2 Use JSON Paths for Partial Updates
  5. Redis Query Engine — HIGH
    • 5.1 Choose the Correct Field Type
    • 5.2 Index Only Fields You Query
    • 5.3 Manage Indexes for Zero-Downtime Updates
    • 5.4 Use DIALECT 2 for Modern Query Syntax
    • 5.5 Write Efficient Queries
  6. Vector Search & RedisVL — HIGH
    • 6.1 Choose HNSW vs FLAT Based on Dataset Size
    • 6.2 Configure Vector Indexes Properly
    • 6.3 Implement RAG Pattern Correctly
    • 6.4 Use Hybrid Search for Better Results
  7. Semantic Caching — MEDIUM
    • 7.1 Configure Semantic Cache Properly
    • 7.2 Use LangCache for LLM Response Caching
  8. Streams & Pub/Sub — MEDIUM
    • 8.1 Choose Streams vs Pub/Sub Appropriately
  9. Clustering & Replication — MEDIUM
    • 9.1 Use Hash Tags for Related Keys
    • 9.2 Use Read Replicas for Read-Heavy Workloads
  10. Security — HIGH
  • 10.1 Always Use Authentication in Production
  • 10.2 Secure Network Access
  • 10.3 Use ACLs for Fine-Grained Access Control
  1. Observability — MEDIUM
  • 11.1 Monitor Key Redis Metrics
  • 11.2 Use Observability Commands for Debugging

1. Data Structures & Keys

Impact: HIGH

1.1 Choose the Right Data Structure

Impact: HIGH (Optimal memory usage and operation performance)

Choose the Right Data Structure

Selecting the appropriate Redis data type for your use case is fundamental to performance and memory efficiency.

Use CaseRecommended TypeWhy
Simple values, countersStringFast, atomic operations
Object with fieldsHashMemory efficient, partial updates
Queue, recent itemsListO(1) push/pop at ends
Unique items, membershipSetO(1) add/remove/check
Rankings, rangesSorted SetScore-based ordering
Documents for queryingJSONNested structures, RQE indexing
Event logs, messagingStreamPersistent, consumer groups
Similarity searchVector (in Hash/JSON)Use Redis Query Engine with HNSW/FLAT indexes

Avoid: Using strings for everything.

# Storing object as JSON string loses atomic field updates
redis.set("user:1001", json.dumps({"name": "Alice", "email": "alice@example.com"}))

# To update email, must fetch, parse, modify, and rewrite entire object
user = json.loads(redis.get("user:1001"))
user["email"] = "new@example.com"
redis.set("user:1001", json.dumps(user))

Recommended: Use Hash for objects with fields.

# Hash allows atomic field updates
redis.hset("user:1001", mapping={"name": "Alice", "email": "alice@example.com"})

# Update single field without touching others
redis.hset("user:1001", "email", "new@example.com")

Reference: Redis Data Types

1.2 Use Consistent Key Naming Conventions

Impact: MEDIUM (Improved maintainability and debugging)

Use Consistent Key Naming Conventions

Well-structured key names improve code maintainability, debugging, and enable efficient key scanning.

Recommended: Use colons as separators with a consistent hierarchy.

# Pattern: service:entity:id:attribute
user:1001:profile
user:1001:settings
order:2024:items
cache:api:users:list
session:abc123

Avoid: Inconsistent naming, spaces, or very long keys.

# These cause confusion and waste memory
User_1001_Profile
my key with spaces
com.mycompany.myapp.production.users.profile.data.1001

Key tips:

  • Keep keys short but readable—they consume memory
  • Use TTL on cache keys to prevent unbounded growth
  • Avoid KEYS * in production—use SCAN instead
  • Consider key prefixes for multi-tenant applications

Reference: Redis Keys


2. Memory & Expiration

Impact: HIGH

2.1 Configure Memory Limits and Eviction Policies

Impact: HIGH (Prevents out-of-memory crashes and unpredictable behavior)

Configure Memory Limits and Eviction Policies

Always configure maxmemory and an eviction policy to prevent Redis from consuming all available memory.

Recommended: Set explicit memory limits.

maxmemory 2gb
maxmemory-policy allkeys-lru
PolicyUse Case
volatile-lruEvict keys with TTL, least recently used first
allkeys-lruEvict any key, least recently used first
volatile-ttlEvict keys closest to expiration
noevictionReturn errors when memory is full (use for critical data)

Avoid: Running Redis without memory limits.

# No maxmemory set - Redis will use all available RAM
# Can cause OOM killer to terminate Redis or other processes

Memory optimization tips:

  • Use Hashes for small objects (more memory-efficient than separate keys)
  • Use OBJECT ENCODING key to check how Redis stores your data
  • Use MEMORY USAGE key to check individual key memory consumption
  • Enable compression in your client for large values

Reference: Redis Memory Optimization

2.2 Set TTL on Cache Keys

Impact: HIGH (Prevents unbounded memory growth)

Set TTL on Cache Keys

Always set expiration times on cache keys to prevent unbounded memory growth.

Recommended: Set TTL at write time.

# Good: TTL set atomically with the value
redis.setex("cache:user:1001", 3600, user_json)

# Good: For hashes, set TTL after
redis.hset("session:abc", mapping=session_data)
redis.expire("session:abc", 1800)

Avoid: Forgetting TTL on cache keys.

# Risk: This key may live forever
redis.set("cache:user:1001", user_json)

TTL strategies:

  • Cache data: 1-24 hours depending on freshness requirements
  • Sessions: 30 minutes to 24 hours
  • Rate limiting: Seconds to minutes
  • Temporary locks: Seconds with automatic release

Reference: Redis EXPIRE


3. Connection & Performance

Impact: HIGH

3.1 Avoid Blocking Commands in Production

Impact: HIGH (Prevents Redis from becoming unresponsive)

Avoid Blocking Commands in Production

Some Redis commands block the server while executing. Use non-blocking alternatives.

AvoidUse Instead
KEYS *SCAN with cursor
SMEMBERS on large setsSSCAN
HGETALL on large hashesHSCAN
LRANGE 0 -1 on large listsPaginate with LRANGE 0 100

Recommended: Use SCAN for iteration.

# Good: Non-blocking iteration
cursor = 0
while True:
    cursor, keys = redis.scan(cursor, match="user:*", count=100)
    for key in keys:
        process(key)
    if cursor == 0:
        break

Avoid: Using KEYS in production.

# Bad: Blocks Redis while scanning all keys
keys = redis.keys("user:*")

Reference: Redis SCAN

3.2 Configure Connection Timeouts

Impact: MEDIUM (Prevents hanging connections and improves reliability)

Configure Connection Timeouts

Always configure timeouts to prevent hanging connections.

Recommended: Set both socket and connect timeouts.

r = redis.Redis(
    host='localhost',
    socket_timeout=5.0,        # Read/write timeout
    socket_connect_timeout=2.0  # Connection timeout
)

Avoid: Using default (no) timeouts.

# Bad: No timeouts - can hang indefinitely
r = redis.Redis(host='localhost')

Reference: Redis Client Configuration

3.3 Use Connection Pooling

Impact: HIGH (Reduces connection overhead by 10x or more)

Use Connection Pooling

Reuse connections via a pool instead of creating new connections per request.

Recommended: Use a connection pool.

import redis

# Good: Connection pool
pool = redis.ConnectionPool(host='localhost', port=6379, max_connections=50)
r = redis.Redis(connection_pool=pool)

Avoid: Creating new connections per request.

# Bad: New connection every time
def get_user(user_id):
    r = redis.Redis(host='localhost', port=6379)  # Don't do this
    return r.get(f"user:{user_id}")

Reference: Redis Connection Pooling

3.4 Use Pipelining for Bulk Operations

Impact: HIGH (Reduces round trips, 5-10x faster for batch operations)

Use Pipelining for Bulk Operations

Batch multiple commands into a single round trip to reduce network latency.

Recommended: Use pipeline for multiple commands.

# Good: Single round trip for multiple commands
pipe = redis.pipeline()
for user_id in user_ids:
    pipe.get(f"user:{user_id}")
results = pipe.execute()

Avoid: Sequential commands in a loop.

# Bad: N round trips
results = []
for user_id in user_ids:
    results.append(redis.get(f"user:{user_id}"))

Reference: Redis Pipelining


4. JSON Documents

Impact: MEDIUM

4.1 Choose JSON vs Hash Appropriately

Impact: MEDIUM (Optimal data model for your use case)

Choose JSON vs Hash Appropriately

Redis JSON and Hash serve different purposes. Choose based on your data structure and query needs.

JSONHash
Nested structuresFlat key-value
Need to query with RQESimple field access
Partial updates at any depthTop-level field updates
Complex documentsSimple objects

Recommended: Use JSON for nested structures.

# JSON supports nested structures and deep updates
redis.json().set("user:1001", "$", {
    "name": "Alice",
    "preferences": {"theme": "dark", "notifications": True}
})

# Update nested field directly
redis.json().set("user:1001", "$.preferences.theme", "light")

Recommended: Use Hash for flat objects.

# Hash is simpler and more memory-efficient for flat data
redis.hset("session:abc", mapping={
    "user_id": "1001",
    "created_at": "2024-01-01",
    "ip": "192.168.1.1"
})

Reference: Redis JSON

4.2 Use JSON Paths for Partial Updates

Impact: MEDIUM (Avoids fetching and rewriting entire documents)

Use JSON Paths for Partial Updates

Use JSON path syntax to update specific fields without fetching the entire document.

Recommended: Use JSON paths for targeted updates.

# Store JSON document
redis.json().set("user:1001", "$", {
    "name": "Alice",
    "email": "alice@example.com",
    "preferences": {"theme": "dark", "notifications": True}
})

# Update nested field without fetching entire document
redis.json().set("user:1001", "$.preferences.theme", "light")

# Get specific field
theme = redis.json().get("user:1001", "$.preferences.theme")

Avoid: Storing JSON as a string and parsing client-side.

# Bad: Loses queryability and atomic updates
redis.set("user:1001", json.dumps(user_data))

# Must fetch, parse, modify, serialize, and rewrite
data = json.loads(redis.get("user:1001"))
data["preferences"]["theme"] = "light"
redis.set("user:1001", json.dumps(data))

Indexing JSON for search:

FT.CREATE idx:users ON JSON PREFIX 1 user:
    SCHEMA
        $.name AS name TEXT
        $.email AS email TAG
        $.preferences.theme AS theme TAG

Reference: Redis JSON Path


5. Redis Query Engine

Impact: HIGH

5.1 Choose the Correct Field Type

Impact: HIGH (Use TAG instead of TEXT for filtering to improve query speed 10x)

Choose the Correct Field Type

Each field type has different capabilities and performance characteristics.

Field TypeUse WhenNotes
TEXTFull-text search neededTokenized, stemmed
TAGExact match, filteringFaster than TEXT for filtering
NUMERICRange queries, sortingUse for prices, counts, timestamps
GEOLocation queriesLat/long coordinates
VECTORSimilarity searchHNSW or FLAT algorithm

Recommended: Use TAG for exact matching.

# Good: TAG for exact category matching
FT.CREATE idx:products ON HASH PREFIX 1 product:
    SCHEMA
        category TAG SORTABLE
        status TAG

Avoid: Using TEXT when you don't need full-text features.

# Overkill: TEXT for category adds unnecessary tokenization
FT.CREATE idx:products ON HASH PREFIX 1 product:
    SCHEMA
        category TEXT
        status TEXT

Reference: Redis Search Field Types

5.2 Index Only Fields You Query

Impact: HIGH (Reduces index size and improves write performance)

Index Only Fields You Query

Create indexes with only the fields you need to search, filter, or sort on.

Recommended: Index specific fields.

FT.CREATE idx:products ON HASH PREFIX 1 product:
    SCHEMA
        name TEXT WEIGHT 2.0
        description TEXT
        category TAG SORTABLE
        price NUMERIC SORTABLE
        location GEO

Avoid: Over-indexing or indexing unused fields.

# Bad: Indexing every field "just in case"
FT.CREATE idx:products ON HASH PREFIX 1 product:
    SCHEMA
        name TEXT
        description TEXT
        category TEXT
        subcategory TEXT
        brand TEXT
        sku TEXT
        price NUMERIC
        cost NUMERIC
        margin NUMERIC
        ...

Tips:

  • Start with the minimum required fields
  • Add fields as query patterns emerge
  • Use FT.INFO to monitor index size

Reference: Redis Search Indexing

5.3 Manage Indexes for Zero-Downtime Updates

Impact: MEDIUM (Use aliases for seamless index updates)

Manage Indexes for Zero-Downtime Updates

Use aliases to swap indexes without application changes.

Recommended: Use aliases for production indexes.

# Create versioned index
FT.CREATE idx:products_v2 ON HASH PREFIX 1 product:
    SCHEMA
        name TEXT
        category TAG SORTABLE
        price NUMERIC SORTABLE

# Point alias to new index
FT.ALIASADD products idx:products_v2

# Application queries use alias
FT.SEARCH products "@category:{electronics}"

# Later, swap to new version
FT.ALIASUPDATE products idx:products_v3

Useful management commands:

# Check index info
FT.INFO idx:products

# Drop and recreate (non-blocking)
FT.DROPINDEX idx:products
FT.CREATE idx:products ...

# List all indexes
FT._LIST

Reference: Redis Search Index Management

5.4 Use DIALECT 2 for Modern Query Syntax

Impact: MEDIUM (Enables modern query features and consistent behavior)

Use DIALECT 2 for Modern Query Syntax

Always specify DIALECT 2 (or higher) for modern query syntax and consistent behavior.

Recommended: Specify DIALECT 2.

FT.SEARCH idx:products "@name:laptop" DIALECT 2

FT.AGGREGATE idx:products "@category:{electronics}"
    GROUPBY 1 @category
    REDUCE COUNT 0 AS count
    DIALECT 2

Avoid: Relying on default dialect which may vary.

# Behavior may vary depending on server default
FT.SEARCH idx:products "@name:laptop"

Why DIALECT 2:

  • Consistent handling of special characters
  • Better NULL value handling
  • More predictable query parsing
  • Required for some newer features

Reference: Redis Search Dialects

5.5 Write Efficient Queries

Impact: HIGH (Proper filtering reduces query time by orders of magnitude)

Write Efficient Queries

Be specific and use filters to reduce the result set early.

Recommended: Use specific filters and limit results.

# Good: Specific query with filters
FT.SEARCH idx:products "@category:{electronics} @price:[100 500]"
    LIMIT 0 20
    RETURN 3 name price category

# Good: Use SORTBY and LIMIT
FT.SEARCH idx:products "@name:laptop"
    SORTBY price ASC
    LIMIT 0 10

Avoid: Broad queries returning large result sets.

# Bad: Wildcard prefix scans entire index
FT.SEARCH idx:products "*" LIMIT 0 10000

# Bad: Loading all fields from source document
FT.AGGREGATE idx:products "*" LOAD *

Performance tips:

  • Add SORTABLE to fields used in SORTBY
  • Use TAG SORTABLE UNF for best performance on tag fields
  • Use NOSTEM if you don't need stemming
  • Profile queries with FT.PROFILE
FT.PROFILE idx:products SEARCH QUERY "@category:{electronics}"

Reference: Redis Search Query Syntax


6. Vector Search & RedisVL

Impact: HIGH

6.1 Choose HNSW vs FLAT Based on Dataset Size

Impact: HIGH (HNSW is 100x faster for large datasets, FLAT is exact for small ones)

Choose HNSW vs FLAT Based on Dataset Size

Select the right algorithm based on your dataset size and accuracy requirements.

AlgorithmUse CaseTrade-off
HNSWLarge datasets (>10k vectors)Fast search, uses more memory
FLATSmall datasets, exact resultsSlower search, less memory

Recommended: Use HNSW for production workloads.

from redisvl.schema import IndexSchema

# HNSW for large datasets - fast approximate search
schema = IndexSchema.from_dict({
    "index": {"name": "idx:docs", "prefix": "doc:"},
    "fields": [
        {"name": "embedding", "type": "vector", "attrs": {
            "dims": 1536,
            "algorithm": "HNSW",
            "distance_metric": "COSINE",
            "M": 16,                  # Connections per node (higher = more accurate, more memory)
            "EF_CONSTRUCTION": 200    # Build-time accuracy (higher = better index, slower build)
        }}
    ]
})

Recommended: Use FLAT for small datasets or when exact results required.

# FLAT for small datasets - exact brute-force search
schema = IndexSchema.from_dict({
    "index": {"name": "idx:small", "prefix": "small:"},
    "fields": [
        {"name": "embedding", "type": "vector", "attrs": {
            "dims": 1536,
            "algorithm": "FLAT",
            "distance_metric": "COSINE"
        }}
    ]
})

Tips:

  • Start with HNSW defaults, tune M and EF based on benchmarks
  • Higher M = more memory but better recall
  • Set EF_RUNTIME at query time for accuracy/speed trade-off

Reference: Redis Vector Search

6.2 Configure Vector Indexes Properly

Impact: HIGH (Correct configuration is essential for vector search accuracy)

Configure Vector Indexes Properly

Set the correct dimensions, algorithm, and distance metric for your embeddings.

Recommended: Match index config to your embedding model.

from redisvl.index import SearchIndex
from redisvl.schema import IndexSchema

schema = IndexSchema.from_dict({
    "index": {"name": "idx:docs", "prefix": "doc:"},
    "fields": [
        {"name": "content", "type": "text"},
        {"name": "embedding", "type": "vector", "attrs": {
            "dims": 1536,           # Must match your embedding model
            "algorithm": "HNSW",
            "distance_metric": "COSINE"
        }}
    ]
})

index = SearchIndex(schema)
index.create(overwrite=True)

Avoid: Mismatched dimensions or wrong distance metric.

# Bad: Wrong dimensions for your model
{"dims": 768}  # But using OpenAI which outputs 1536

# Bad: Wrong metric for normalized embeddings
{"distance_metric": "L2"}  # When embeddings are normalized for COSINE

Reference: RedisVL Documentation

6.3 Implement RAG Pattern Correctly

Impact: HIGH (Proper RAG implementation improves LLM response quality)

Implement RAG Pattern Correctly

Store documents with embeddings, retrieve relevant context, and pass to LLM.

Recommended: Full RAG pipeline with RedisVL.

from redisvl.index import SearchIndex
from redisvl.query import VectorQuery

# 1. Store documents with embeddings
for doc in documents:
    embedding = embed_model.encode(doc["content"])
    index.load([{
        "content": doc["content"],
        "embedding": embedding.tolist(),
        "source": doc["source"]
    }])

# 2. Query with vector similarity
query_embedding = embed_model.encode(user_question)
results = index.search(VectorQuery(
    vector=query_embedding,
    vector_field_name="embedding",
    return_fields=["content", "source"],
    num_results=5
))

# 3. Pass context to LLM
context = "\n".join([r["content"] for r in results])
response = llm.generate(f"Context: {context}\n\nQuestion: {user_question}")

Best practices:

  • Normalize vectors if using COSINE distance
  • Batch inserts using index.load() with lists
  • Set appropriate M and EF_CONSTRUCTION for HNSW based on dataset size
  • Use filters to reduce the search space before vector comparison
  • Consider chunking long documents for better retrieval

Reference: Redis RAG Quickstart

6.4 Use Hybrid Search for Better Results

Impact: MEDIUM (Combining vector + filters improves relevance and reduces search space)

Use Hybrid Search for Better Results

Combine vector similarity with attribute filtering for more relevant results.

Recommended: Apply filters to reduce search space.

from redisvl.query import VectorQuery

query = VectorQuery(
    vector=query_embedding,
    vector_field_name="embedding",
    return_fields=["content", "category", "date"],
    num_results=10,
    filter_expression="@category:{technology} @date:[2024 2025]"
)

results = index.search(query)

Avoid: Searching entire vector space when filters apply.

# Bad: No filter - searches all vectors then filters client-side
results = index.search(VectorQuery(
    vector=query_embedding,
    vector_field_name="embedding",
    num_results=1000
))
# Client-side filtering - wasteful
filtered = [r for r in results if r["category"] == "technology"]

Tips:

  • Use TAG fields for category filters
  • Use NUMERIC fields for date/price ranges
  • Filters are applied before vector search, reducing computation

Reference: Redis Hybrid Queries


7. Semantic Caching

Impact: MEDIUM

7.1 Configure Semantic Cache Properly

Impact: MEDIUM (Correct threshold tuning balances hit rate vs accuracy)

Configure Semantic Cache Properly

Note: LangCache is currently in preview on Redis Cloud. Features and behavior may change.

Tune similarity threshold and cache separation for optimal LangCache results.

Recommended: Tune similarity threshold for your use case.

from langcache import LangCache

lang_cache = LangCache(
    server_url=f"https://{os.getenv('HOST')}",
    cache_id=os.getenv("CACHE_ID"),
    api_key=os.getenv("API_KEY")
)

# Stricter matching - fewer false positives (0.95 = very similar)
result = lang_cache.search(
    prompt="What is Redis?",
    similarity_threshold=0.95
)

# Looser matching - higher hit rate (0.8 = somewhat similar)
result = lang_cache.search(
    prompt="What is Redis?",
    similarity_threshold=0.8
)

Recommended: Use separate caches for different use cases.

# Create different cache IDs in Redis Cloud for different LLM tasks
support_cache = LangCache(
    server_url=server_url,
    cache_id="support-cache-id",
    api_key=api_key
)

code_cache = LangCache(
    server_url=server_url,
    cache_id="code-cache-id",
    api_key=api_key
)

Avoid: Using a single cache for all LLM tasks.

# All tasks share one cache - responses may not be relevant
result = lang_cache.search(prompt="How do I reset my password?")
# Could return a code snippet if someone asked a similar coding question

Best practices:

  • Start with threshold 0.9, adjust based on your use case
  • Use custom attributes to filter results within a single cache
  • Monitor cache hit rates to evaluate effectiveness
  • Use separate cache IDs for fundamentally different LLM tasks

Reference: LangCache Best Practices

7.2 Use LangCache for LLM Response Caching

Impact: HIGH (Reduces LLM API costs by 50-90% for similar queries)

Use LangCache for LLM Response Caching

Note: LangCache is currently in preview on Redis Cloud. Features and behavior may change.

LangCache is a fully-managed semantic caching service on Redis Cloud that reduces LLM costs and latency.

How it works:

  1. Your app sends a prompt to LangCache via POST /v1/caches/{cacheId}/entries/search
  2. LangCache generates an embedding and searches for similar cached responses
  3. If found (cache hit), returns the cached response instantly
  4. If not found (cache miss), your app calls the LLM and stores the response

Recommended: Use the LangCache Python SDK.

from langcache import LangCache
import os

lang_cache = LangCache(
    server_url=f"https://{os.getenv('HOST')}",
    cache_id=os.getenv("CACHE_ID"),
    api_key=os.getenv("API_KEY")
)

# Search for cached response
result = lang_cache.search(
    prompt="What is Redis?",
    similarity_threshold=0.9
)

if result:
    response = result[0]["response"]
else:
    response = llm.generate("What is Redis?")
    # Store for future queries
    lang_cache.set(
        prompt="What is Redis?",
        response=response
    )

LangCache REST API:

# Search cache
curl -X POST "https://$HOST/v1/caches/$CACHE_ID/entries/search" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "What is Redis?"}'

# Store a response
curl -X POST "https://$HOST/v1/caches/$CACHE_ID/entries" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "What is Redis?", "response": "Redis is an in-memory database..."}'

With custom attributes for filtering:

# Store with attributes
lang_cache.set(
    prompt="What is Redis?",
    response="Redis is an in-memory database...",
    attributes={"category": "database", "version": "v1"}
)

# Search with attribute filter
result = lang_cache.search(
    prompt="Tell me about Redis",
    attributes={"category": "database"},
    similarity_threshold=0.9
)

Reference: LangCache Documentation


8. Streams & Pub/Sub

Impact: MEDIUM

8.1 Choose Streams vs Pub/Sub Appropriately

Impact: MEDIUM (Wrong choice leads to lost messages or unnecessary complexity)

Choose Streams vs Pub/Sub Appropriately

Redis supports two messaging approaches for different use cases.

Avoid: Using Pub/Sub when messages must not be lost.

# Pub/Sub - messages lost if no subscribers connected
r.publish("orders", json.dumps(order))  # Fire and forget!

Recommended: Use Streams when message durability matters.

# Streams - messages persist and can be replayed
r.xadd("orders:stream", {"order": json.dumps(order)})

# Consumer group for reliable processing
r.xreadgroup("workers", "worker-1", {"orders:stream": ">"}, count=10)
r.xack("orders:stream", "workers", message_id)

When to Use Each

RequirementUse
Real-time notifications, OK to miss messagesPub/Sub
Messages must not be lostStreams
Need to replay/reprocess messagesStreams
Multiple workers processing same queueStreams (consumer groups)
Simple broadcast to connected clientsPub/Sub
Event sourcing or audit trailStreams

Reference: Redis Streams


9. Clustering & Replication

Impact: MEDIUM

9.1 Use Hash Tags for Related Keys

Impact: HIGH (Enables multi-key operations in Redis Cluster)

Use Hash Tags for Related Keys

In Redis Cluster, keys are distributed across slots. Use hash tags to ensure related keys are on the same slot.

Recommended: Use hash tags for related keys.

# These keys go to the same slot because {1001} is the hash tag
redis.set("user:{1001}:profile", "...")
redis.set("user:{1001}:settings", "...")
redis.set("user:{1001}:cart", "...")

# Now you can use MULTI/pipeline on them
pipe = redis.pipeline()
pipe.get("user:{1001}:profile")
pipe.get("user:{1001}:settings")
pipe.execute()

Avoid: Related keys without hash tags.

# Bad: These may be on different slots
redis.set("user:1001:profile", "...")  # No hash tag
redis.set("user:1001:settings", "...")

# This will fail in cluster mode
pipe = redis.pipeline()
pipe.get("user:1001:profile")
pipe.get("user:1001:settings")
pipe.execute()  # CROSSSLOT error

Hash tag rules:

  • Only the part between { and } is hashed
  • Use meaningful identifiers like user ID or tenant ID
  • Don't overuse - too many keys on one slot creates hotspots

Reference: Redis Cluster Key Distribution

9.2 Use Read Replicas for Read-Heavy Workloads

Impact: MEDIUM (Scales read throughput without adding primary nodes)

Use Read Replicas for Read-Heavy Workloads

For read-heavy workloads, distribute reads across replicas to reduce load on primaries.

Recommended: Read from replicas with Sentinel.

from redis import Redis
from redis.sentinel import Sentinel

# With Sentinel
sentinel = Sentinel([('sentinel1', 26379)], socket_timeout=0.1)
master = sentinel.master_for('mymaster')
replica = sentinel.slave_for('mymaster')

# Write to master
master.set("key", "value")

# Read from replica (eventually consistent)
value = replica.get("key")

Recommended: Configure replica reads in Redis Cluster.

from redis.cluster import RedisCluster

rc = RedisCluster(
    host='localhost',
    port=6379,
    read_from_replicas=True  # Distribute reads to replicas
)

Considerations:

  • Replica reads are eventually consistent
  • Don't read from replicas for data that was just written
  • Use for read-heavy, slightly-stale-OK workloads

Reference: Redis Replication


10. Security

Impact: HIGH

10.1 Always Use Authentication in Production

Impact: HIGH (Prevents unauthorized access to your data)

Always Use Authentication in Production

Never run Redis without authentication in production environments.

Recommended: Use password and TLS.

r = redis.Redis(
    host='localhost',
    port=6379,
    password='your-strong-password',
    ssl=True,
    ssl_cert_reqs='required'
)

Avoid: Connecting without authentication.

# Bad: No authentication
r = redis.Redis(host='localhost', port=6379)

Configuration:

# redis.conf
requirepass your-strong-password
tls-port 6380
tls-cert-file /path/to/redis.crt
tls-key-file /path/to/redis.key

Reference: Redis Security

10.2 Secure Network Access

Impact: HIGH (Reduces attack surface and prevents unauthorized access)

Secure Network Access

Restrict network access to Redis to only trusted sources.

Recommended: Bind to specific interfaces.

# redis.conf
bind 127.0.0.1 192.168.1.100
protected-mode yes

Recommended: Use firewall rules.

# Allow only application servers
iptables -A INPUT -p tcp --dport 6379 -s 192.168.1.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 6379 -j DROP

Avoid: Exposing Redis to the internet.

# Bad: Binds to all interfaces
bind 0.0.0.0
protected-mode no

Security checklist:

  • Use TLS for connections
  • Bind to specific interfaces, not 0.0.0.0
  • Use firewall rules to restrict access
  • Disable dangerous commands in production
# Disable dangerous commands
rename-command FLUSHALL ""
rename-command DEBUG ""
rename-command CONFIG ""

Reference: Redis Security

10.3 Use ACLs for Fine-Grained Access Control

Impact: HIGH (Limits blast radius if credentials are compromised)

Use ACLs for Fine-Grained Access Control

Create users with only the permissions they need (principle of least privilege).

Recommended: Create specific users with limited permissions.

# Read-only user for cache access
ACL SETUSER app_readonly on >password ~cache:* +get +mget +scan

# Writer that can't run dangerous commands
ACL SETUSER app_writer on >password ~* +@all -@dangerous

# Admin user (use sparingly)
ACL SETUSER admin on >strong-password ~* +@all

Avoid: Using the default user for everything.

# Bad: Single password for all access
requirepass shared-password

ACL categories:

  • @read - Read commands
  • @write - Write commands
  • @dangerous - Commands like FLUSHALL, DEBUG
  • @admin - Administrative commands

Reference: Redis ACL


11. Observability

Impact: MEDIUM

11.1 Monitor Key Redis Metrics

Impact: MEDIUM (Early detection of performance and capacity issues)

Monitor Key Redis Metrics

Track these metrics to catch issues before they impact users.

MetricWhat It Tells YouAlert When
used_memoryCurrent memory usage> 80% of maxmemory
connected_clientsNumber of connectionsSudden spikes or drops
blocked_clientsClients waiting on blocking ops> 0 sustained
instantaneous_ops_per_secCurrent throughputSignificant drops
keyspace_hits/missesCache hit ratioHit ratio < 80%
rejected_connectionsConnection limit issues> 0
rdb_last_save_timeLast persistence snapshotToo old

Recommended: Export metrics to your monitoring system.

# Get key metrics
info = redis.info()
print(f"Memory: {info['used_memory_human']}")
print(f"Connections: {info['connected_clients']}")
print(f"Ops/sec: {info['instantaneous_ops_per_sec']}")
print(f"Hit ratio: {info['keyspace_hits'] / (info['keyspace_hits'] + info['keyspace_misses']) * 100:.1f}%")

Redis Insight: Use Redis Insight for visual monitoring, query profiling, and debugging. It includes Redis Copilot for natural language queries.

Reference: Redis Insight

11.2 Use Observability Commands for Debugging

Impact: MEDIUM (Enables quick diagnosis of performance issues)

Use Observability Commands for Debugging

Redis provides built-in commands for monitoring and debugging.

Key commands:

# Slow query log - find slow commands
SLOWLOG GET 10
SLOWLOG LEN
SLOWLOG RESET

# Server info - comprehensive stats
INFO all
INFO memory
INFO stats
INFO replication
INFO clients

# Memory analysis
MEMORY DOCTOR
MEMORY STATS
MEMORY USAGE mykey

# Client connections
CLIENT LIST
CLIENT INFO

# Index info (RQE)
FT.INFO idx:products
FT.PROFILE idx:products SEARCH QUERY "@name:laptop"

Recommended: Check SLOWLOG regularly.

# Get recent slow queries
slow_queries = redis.slowlog_get(10)
for query in slow_queries:
    print(f"Duration: {query['duration']}μs, Command: {query['command']}")

Reference: Redis Monitoring

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.