Menu
Nazad na Blog
1 min read
Backend

Redis Stack: In-Memory Vector Database für AI

Redis als Vector Database nutzen. RediSearch, RedisJSON, Vector Set und High-Performance AI-Anwendungen.

Redis StackVector DatabaseRediSearchRedisJSONIn-Memory DatabaseAI Cache
Redis Stack: In-Memory Vector Database für AI

Redis Stack: In-Memory Vector Database für AI

Meta-Description: Redis als Vector Database nutzen. RediSearch, RedisJSON, Vector Set und High-Performance AI-Anwendungen.

Keywords: Redis Stack, Vector Database, RediSearch, RedisJSON, In-Memory Database, AI Cache, Semantic Search


Einführung

Redis 8 vereint alle Module in einem Package: Vector Search, JSON, Full-Text Search und mehr. Als In-Memory Database bietet Redis ultra-niedrige Latenz für AI-Anwendungen – ideal für Caching, Session Management und Echtzeit-Suche.


Redis 8 Stack

┌─────────────────────────────────────────────────────────────┐
│                    REDIS 8 STACK                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Core Data Structures:                                      │
│  ├── Strings, Lists, Sets, Hashes, Sorted Sets             │
│  ├── Streams (Event Streaming)                             │
│  └── HyperLogLog, Bitmaps                                  │
│                                                             │
│  New in Redis 8:                                            │
│  ├── Vector Set (Beta) - Similarity Search                 │
│  ├── JSON - Native JSON Document Store                     │
│  ├── Time Series - Metrics & Monitoring                    │
│  └── Probabilistic Structures                              │
│      ├── Bloom Filter                                      │
│      ├── Cuckoo Filter                                     │
│      ├── Count-Min Sketch                                  │
│      ├── Top-K                                             │
│      └── T-Digest                                          │
│                                                             │
│  Search Capabilities:                                       │
│  ├── Full-Text Search                                      │
│  ├── Vector Search (FLAT, HNSW, SVS-VAMANA)               │
│  ├── Numeric/Tag Filtering                                 │
│  └── Geospatial Queries                                    │
│                                                             │
│  Performance:                                               │
│  └── Sub-Millisecond Latency (In-Memory)                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Setup

# Docker
docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest

# Redis Cloud (Managed)
# https://redis.com/try-free/
// lib/redis.ts
import { createClient } from 'redis';

const redis = createClient({
  url: process.env.REDIS_URL || 'redis://localhost:6379'
});

redis.on('error', (err) => console.error('Redis Client Error', err));

await redis.connect();

export default redis;

Vector Search mit Hash

import redis from '@/lib/redis';
import { SchemaFieldTypes, VectorAlgorithms } from 'redis';

// 1. Index erstellen
async function createVectorIndex() {
  try {
    await redis.ft.create('idx:docs', {
      '$.title': {
        type: SchemaFieldTypes.TEXT,
        AS: 'title'
      },
      '$.content': {
        type: SchemaFieldTypes.TEXT,
        AS: 'content'
      },
      '$.category': {
        type: SchemaFieldTypes.TAG,
        AS: 'category'
      },
      '$.embedding': {
        type: SchemaFieldTypes.VECTOR,
        AS: 'embedding',
        ALGORITHM: VectorAlgorithms.HNSW,
        TYPE: 'FLOAT32',
        DIM: 1536,
        DISTANCE_METRIC: 'COSINE'
      }
    }, {
      ON: 'JSON',
      PREFIX: 'doc:'
    });
    console.log('Index created');
  } catch (e) {
    if ((e as Error).message.includes('Index already exists')) {
      console.log('Index already exists');
    } else {
      throw e;
    }
  }
}

// 2. Document speichern (JSON)
async function saveDocument(
  id: string,
  title: string,
  content: string,
  category: string,
  embedding: number[]
) {
  await redis.json.set(`doc:${id}`, '$', {
    title,
    content,
    category,
    embedding
  });
}

// 3. Vector Search
async function searchSimilar(
  queryEmbedding: number[],
  limit: number = 5
) {
  const results = await redis.ft.search('idx:docs', '*=>[KNN $K @embedding $BLOB AS score]', {
    PARAMS: {
      K: limit.toString(),
      BLOB: Buffer.from(new Float32Array(queryEmbedding).buffer)
    },
    RETURN: ['title', 'content', 'score'],
    SORTBY: {
      BY: 'score',
      DIRECTION: 'ASC'  // Lower = more similar for COSINE
    },
    DIALECT: 2
  });

  return results.documents.map(doc => ({
    id: doc.id,
    title: doc.value.title,
    content: doc.value.content,
    score: 1 - parseFloat(doc.value.score as string)  // Convert to similarity
  }));
}

// 4. Hybrid Search (Vector + Filter)
async function searchWithFilter(
  queryEmbedding: number[],
  category: string,
  limit: number = 5
) {
  const results = await redis.ft.search(
    'idx:docs',
    `(@category:{${category}})=>[KNN $K @embedding $BLOB AS score]`,
    {
      PARAMS: {
        K: limit.toString(),
        BLOB: Buffer.from(new Float32Array(queryEmbedding).buffer)
      },
      RETURN: ['title', 'content', 'category', 'score'],
      DIALECT: 2
    }
  );

  return results.documents;
}

Vector Set (Redis 8 Beta)

// Neuer Datentyp in Redis 8 - Inspiriert von Sorted Sets

// Vector hinzufügen
await redis.sendCommand([
  'VADD', 'products',
  'VECTOR', ...embedding.map(v => v.toString()),
  'product:123'
]);

// Ähnliche Vektoren finden
const similar = await redis.sendCommand([
  'VSIM', 'products',
  'VECTOR', ...queryEmbedding.map(v => v.toString()),
  'COUNT', '10'
]);

// Vorteile von Vector Set:
// - Einfachere API als RediSearch
// - Optimiert für Similarity Search
// - Von Salvatore Sanfilippo (Redis Creator) entwickelt

Session Cache mit Vector Search

// Kombination: Session Management + Semantic Search

interface UserSession {
  userId: string;
  lastQuery: string;
  queryEmbedding: number[];
  searchHistory: string[];
  createdAt: number;
  expiresAt: number;
}

// Session speichern
async function saveSession(session: UserSession) {
  const key = `session:${session.userId}`;

  await redis.json.set(key, '$', session);
  await redis.expireAt(key, session.expiresAt);
}

// Ähnliche Queries aus History finden
async function findSimilarPastQueries(
  userId: string,
  currentQueryEmbedding: number[]
) {
  // Alle Sessions mit Query-History durchsuchen
  const results = await redis.ft.search(
    'idx:sessions',
    '*=>[KNN 5 @queryEmbedding $BLOB AS score]',
    {
      PARAMS: {
        BLOB: Buffer.from(new Float32Array(currentQueryEmbedding).buffer)
      },
      RETURN: ['userId', 'lastQuery', 'score'],
      DIALECT: 2
    }
  );

  return results.documents;
}

Full-Text + Vector Hybrid Search

// Index mit Text + Vector
await redis.ft.create('idx:articles', {
  '$.title': {
    type: SchemaFieldTypes.TEXT,
    AS: 'title',
    WEIGHT: 2.0
  },
  '$.body': {
    type: SchemaFieldTypes.TEXT,
    AS: 'body'
  },
  '$.tags': {
    type: SchemaFieldTypes.TAG,
    AS: 'tags'
  },
  '$.embedding': {
    type: SchemaFieldTypes.VECTOR,
    AS: 'embedding',
    ALGORITHM: VectorAlgorithms.HNSW,
    TYPE: 'FLOAT32',
    DIM: 1536,
    DISTANCE_METRIC: 'COSINE'
  }
}, {
  ON: 'JSON',
  PREFIX: 'article:'
});

// Hybrid Search: Text + Vector
async function hybridSearch(
  textQuery: string,
  queryEmbedding: number[],
  limit: number = 10
) {
  // Full-Text Search
  const textResults = await redis.ft.search(
    'idx:articles',
    `@title|body:(${textQuery})`,
    {
      RETURN: ['title', 'body'],
      LIMIT: { from: 0, size: limit }
    }
  );

  // Vector Search
  const vectorResults = await redis.ft.search(
    'idx:articles',
    '*=>[KNN $K @embedding $BLOB AS vector_score]',
    {
      PARAMS: {
        K: limit.toString(),
        BLOB: Buffer.from(new Float32Array(queryEmbedding).buffer)
      },
      RETURN: ['title', 'body', 'vector_score'],
      DIALECT: 2
    }
  );

  // Scores kombinieren (RRF - Reciprocal Rank Fusion)
  const combined = new Map<string, { doc: any; score: number }>();
  const k = 60;  // RRF constant

  textResults.documents.forEach((doc, rank) => {
    const score = 1 / (k + rank + 1);
    combined.set(doc.id, {
      doc: doc.value,
      score: score
    });
  });

  vectorResults.documents.forEach((doc, rank) => {
    const vectorScore = 1 / (k + rank + 1);
    const existing = combined.get(doc.id);

    if (existing) {
      existing.score += vectorScore;
    } else {
      combined.set(doc.id, {
        doc: doc.value,
        score: vectorScore
      });
    }
  });

  return Array.from(combined.values())
    .sort((a, b) => b.score - a.score)
    .slice(0, limit);
}

Caching für AI Embeddings

// Embedding Cache - vermeidet wiederholte API-Calls

async function getOrCreateEmbedding(
  text: string,
  generateFn: (text: string) => Promise<number[]>
): Promise<number[]> {
  // Hash als Cache Key
  const hash = await crypto.subtle.digest(
    'SHA-256',
    new TextEncoder().encode(text)
  );
  const cacheKey = `emb:${Buffer.from(hash).toString('hex').slice(0, 16)}`;

  // Cache Check
  const cached = await redis.get(cacheKey);
  if (cached) {
    return JSON.parse(cached);
  }

  // Generate & Cache
  const embedding = await generateFn(text);

  await redis.set(cacheKey, JSON.stringify(embedding), {
    EX: 60 * 60 * 24 * 7  // 7 Tage TTL
  });

  return embedding;
}

// RAG Response Cache
async function getCachedRAGResponse(
  queryEmbedding: number[],
  threshold: number = 0.95
) {
  // Suche nach sehr ähnlichen vorherigen Queries
  const results = await redis.ft.search(
    'idx:rag_cache',
    '*=>[KNN 1 @queryEmbedding $BLOB AS score]',
    {
      PARAMS: {
        BLOB: Buffer.from(new Float32Array(queryEmbedding).buffer)
      },
      RETURN: ['query', 'response', 'score'],
      DIALECT: 2
    }
  );

  if (results.documents.length > 0) {
    const similarity = 1 - parseFloat(results.documents[0].value.score as string);
    if (similarity >= threshold) {
      return results.documents[0].value.response;
    }
  }

  return null;
}

Pub/Sub für Real-Time Updates

// Vector Search + Real-Time Updates

// Publisher
async function publishNewDocument(doc: Document) {
  // In Redis speichern
  await saveDocument(doc.id, doc.title, doc.content, doc.category, doc.embedding);

  // Event publishen
  await redis.publish('documents:new', JSON.stringify({
    id: doc.id,
    title: doc.title,
    category: doc.category
  }));
}

// Subscriber
const subscriber = redis.duplicate();
await subscriber.connect();

await subscriber.subscribe('documents:new', (message) => {
  const doc = JSON.parse(message);
  console.log('New document:', doc.title);

  // UI Update, Cache Invalidation, etc.
});

Fazit

Redis Stack bietet:

  1. Sub-Millisecond Latency: In-Memory für Echtzeit-AI
  2. Unified Platform: Vector, JSON, Full-Text in einem System
  3. Vector Set: Neuer Datentyp für einfache Similarity Search
  4. Caching Layer: Ideal für Embedding-Caches

Perfekt als High-Performance Layer vor AI-Anwendungen.


Bildprompts

  1. "In-memory database with vectors flowing at high speed, performance concept"
  2. "Redis logo with vector arrows and AI neural network, modern database"
  3. "Cache layer between AI model and application, latency optimization"

Quellen