Polyglot Persistence in Practice: A Full-Pipeline Evaluation of a Ten-Database Memory System
Test environment: Mac Studio M3 Ultra · 96GB RAM · Apple Silicon MPS acceleration Data scale: 117 Markdown source files · 879 memory segments · 7,911 cross-database writes Embedding model: BGE-m3 (BAAI, 1024 dimensions, local inference)
Why Ten Databases?
In the design of an AI memory system, one core question is: what database should be used to store and search memories?
The traditional single-database approach has a fatal flaw: no single database can do vector semantic search, full-text keyword matching, relationship graph traversal, SQL aggregation queries, and sub-millisecond caching all at once. Just as you would not use a kitchen knife to open a can, each type of query needs the engine best suited to it.
"The core idea of Polyglot Persistence: it is not about storing ten copies of the data, but about understanding the same data in ten different ways."
MemoryHub v2.0 realizes this idea: text goes in → the BGE-m3 model embeds it once (a 1024-dimensional vector) → it is written to ten backends at the same time. Each backend preserves a different "view" of the same data, so every type of query can find its optimal solution.
The Ten Backends and Their Roles
| Backend | Type | Core Capability | Role in This System |
|---|---|---|---|
| 🧠 Qdrant | Dedicated vector store | Semantic similarity search | 🏆 Primary search engine |
| 📦 Chroma | Embedded vector store | Lightweight local vectors | 🪶 Backup engine |
| 🪶 LanceDB | Columnar vector store | Large-scale analytical queries | 📊 Analytical queries |
| 🗃️ SQLite-vec | SQL + vectors | Embedded SQL queries | 📝 Offline scenarios |
| 🔍 FAISS | Pure vector index | The ceiling of brute-force search | ⚡ King of speed |
| ⚡ Redis | In-memory key-value store | Sub-millisecond reads | 🚀 Hot data cache |
| 🐘 PostgreSQL | Relational + vector | SQL JOIN / aggregation | 🏛️ Structured queries |
| 🔎 Elasticsearch | Full-text search engine | Inverted-index keyword search | 📖 Keyword search |
| 🍃 MongoDB | Document database | Flexible schema | 📄 Semi-structured storage |
| 🔗 Neo4j | Graph database | Relationship graph traversal | 🕸️ Entity association queries |
Import Field Test: 117 Files → 7,911 Writes
Data Source
The test data comes from the complete memory store of the Junze Zhiku AI assistant (UltraClaw): daily work logs, the long-term memory MEMORY.md, project files, pitfall records, system configuration, and so on, 117 Markdown files in total.
Import Pipeline
Every piece of data goes through exactly the same processing flow, simulating the real MemoryHub capture pipeline:
Source file (.md)
↓ Segment by ## headings (each segment ≤1,800 characters)
BGE-m3 embedding (1024-dimensional vectors, accelerated by Apple Silicon MPS)
↓ Embed once, write to ten paths
Qdrant ✓ Chroma ✓ LanceDB ✓ SQLite-vec ✓ FAISS ✓
Redis ✓ PostgreSQL ✓ Elasticsearch ✗ MongoDB ✓ Neo4j ✓
| Metric | Value |
|---|---|
| Source files | 117 |
| Segments generated | 879 |
| Total writes | 7,911 (9 backends × 879) |
| Failures | 0 |
| Total time | 5.2 minutes |
| Steady-state speed | ~3 segments/second |
Elasticsearch OOM
Elasticsearch exited during the import due to a Docker container OOM (out of memory) (exit code 137). The Mac Studio was running 10 database containers plus the BGE-m3 embedding model at the same time, putting extreme pressure on memory. ES was temporarily disabled, and the other nine backends all succeeded.
⚠️ Lesson: when running 10 databases in a single-machine environment, Elasticsearch needs at least 1-2GB of heap memory configured (
-e ES_JAVA_OPTS="-Xms512m -Xmx512m"), otherwise the JVM is easily killed by the OOM Killer.
Search Evaluation: Nine Backends Head to Head
Test Method
Using the same query keywords "Hong Kong camera company Mr. Cheung M&A millennium health", we ran semantic search against each of the nine online backends. We recorded the highest relevance score, the number of hits, the query latency, and the content of the Top-1 result. All backends used the same BGE-m3 query vector.
Summary Table of Search Results
| # | Backend | Highest Score | Hits | Latency | Top-1 Content Summary |
|---|---|---|---|---|---|
| 🥇 | Qdrant | 0.738 | 5 | 66ms | "Mr. Cheung has shifted to the Hong Kong camera company M&A target" |
| 🥈 | FAISS | 0.707 | 5 | 3ms | Same as above |
| 🥉 | MongoDB | 0.707 | 5 | 107ms | Same as above |
| 4 | LanceDB | 0.587 | 5 | 197ms | Same as above |
| 5 | SQLite-vec | 0.707 | 5 | 260ms | Same as above |
| 6 | Chroma | 0.294 | 5 | 300ms | Same as above (but the score is halved) |
| 7 | Neo4j | 0.707 | 5 | 573ms | Same as above |
| 8 | Redis | 0.707 | 5 | 996ms | Same as above |
| 9 | PostgreSQL | 0.000 | 0 | 504ms | N/A (Bug) |
Core Finding One: Why Are the Scores Different?
This is the most critical finding of the evaluation. All backends store exactly the same vectors, and the search uses exactly the same query vector. Yet the relevance scores of the results differ wildly.
Three Layers Behind the Score Differences
-
Qdrant (0.738): native Cosine + HNSW index optimization Qdrant is a dedicated vector database with a built-in HNSW (Hierarchical Navigable Small World) index over Cosine Distance. Algorithm-level optimization makes it not only fast, but the most precise.
-
FAISS / SQLite / MongoDB / Redis / Neo4j (0.707): manual Cosine Similarity These backends do not have native vector search capability. In the evaluation, Cosine Similarity was computed manually with numpy (
np.dot / (norm1 * norm2)). All backends share the same score because they use the same formula. -
LanceDB (0.587) and Chroma (0.294): different internal distance metrics LanceDB defaults to L2/Euclidean distance rather than Cosine, which lowers the score. Chroma uses its own HNSW Cosine implementation, but with a clear loss of precision (the score is simply halved).
"The gap between Qdrant's 0.738 and Chroma's 0.294 is not a difference in data, but a difference in distance metric algorithms."
Core Finding Two: The Battle for Speed, Indexing vs Brute Force
Search Latency Comparison
| FAISS | 3ms | | Qdrant | 66ms | | MongoDB | 107ms | | LanceDB | 197ms | | SQLite-vec | 260ms | | Chroma | 300ms | | Neo4j | 573ms | | Redis | 996ms |
The secret to FAISS at 3ms: it is a pure C++ vector index library developed by Facebook, using IndexFlatIP (inner product) brute-force search. There is no network overhead, no JSON serialization, and no query parsing, just bare matrix computation. Searching among 3,245 1024-dimensional vectors, 3ms is the CPU-level ceiling.
The lesson from Redis at 996ms: Redis, as an in-memory database, should be the fastest, but in the evaluation it was the slowest. The reason is that each search had to traverse 500 keys, with a JSON.GET operation plus a numpy vector computation per key. Redis's advantage lies in point lookups with a known key (sub-millisecond), not full-table scans.
💡 Key insight: the speed bottleneck of vector search is not "computation" but "data transfer". FAISS is fastest because it has zero network overhead; Redis is slowest because of 500 separate requests.
The Best Scenario for Each Backend
| Usage Scenario | Recommended Backend | Reason |
|---|---|---|
| 🎯 Everyday semantic search | Qdrant | The highest relevance at 0.74 + 66ms latency |
| ⚡ High-throughput batch search | FAISS | 3ms, extremely fast, no Docker needed |
| 🔗 Relationship context queries | Neo4j | Graph traversal of "Mr. Cheung → which projects → which meetings" |
| 📖 Keyword full-text search | Elasticsearch | Inverted index (OOM must be resolved) |
| 🏛️ SQL aggregation reports | PostgreSQL | JOIN, GROUP BY, time-range filtering |
| 🗂️ Flexible schema storage | MongoDB | Mixed storage of memories in different formats, 107ms is practical |
| 🚀 Real-time hot data cache | Redis | Sub-millisecond point queries |
| 🪶 Offline/embedded scenarios | SQLite-vec / Chroma | One file gets it done, no Docker needed |
Pitfall Log
| # | Problem | Root Cause | Status |
|---|---|---|---|
| 1 | ES OOM exit | Insufficient JVM heap memory, killed by the system OOM Killer | ⚠️ Restart with a memory limit |
| 2 | PG vector stringification | psycopg2 returns a string rather than a Python list | ⚠️ Needs json.loads() |
| 3 | SQLite/FAISS path error | Config file path inconsistent with the code's default path | ✅ Fixed |
| 4 | Routing does not cover some backends | multi_save routing misses SQLite-vec and FAISS | ⚠️ Architecture-level bug |
Conclusion
"Polyglot Persistence is not an academic concept, but an engineering practice verified by 7,911 writes. Ten databases each do their own job: Qdrant is the king of semantics, FAISS the king of speed, and Neo4j the king of relationships."
Three Key Conclusions
-
The "relevance" of vector search is determined by the distance metric, not by the database brand. Qdrant's 0.738 versus Chroma's 0.294: the gap comes from the algorithm implementation, not from a difference in data. When choosing a vector database, you must test its actual distance-metric precision.
-
The "embed once, write many" architecture is viable, and the overhead is controllable. 879 pieces of data completed 7,911 writes within 5.2 minutes, at a steady-state speed of ~3 segments/second. The bottleneck is the write latency of each backend, not the embedding computation (which is only 18-42ms).
-
There is no silver bullet. Use the engine best suited to each query. Use Qdrant semantic search for everyday conversation, PostgreSQL SQL for report analysis, Neo4j graph traversal for relationship context, and Elasticsearch for full-text matching. This is not redundancy; it is putting the right tool in the right scenario.
Next Steps
The follow-up plan is to fix the PostgreSQL vector deserialization and Elasticsearch OOM issues, completing full coverage of the ten backends; and to test hybrid queries (Multi-Engine Fusion Search) in real user scenarios, calling multiple backends at once and merging and ranking the results to measure search quality.
Evaluation performed by: UltraClaw · Junze Zhiku AI assistant · May 22, 2026 Data source: MemoryHub v2.0 · BGE-m3 · Mac Studio M3 Ultra
More in Evidence
- A Reality Check on Decision Models: Why They Seem Miraculous Online but We Measured Only 54%: A Full Comparison of JEV / LAYA / KEV / CLM-8B and a Deployment Formula
- The "Non-Text-Generating Model": Jev and the New System One Category, and How Agent Architecture Changes When AI Only Answers Multiple Choice
- WeChat Open Source WeMM-Embedding Deep Dive: The Multimodal Embedding Model Topping MMEB-v2, Can It Run on Your Mac?
- A Source-Level Architectural Dissection of DeepSeek Harness: How an Everything-Is-a-Plugin Agent Framework Is Built