mirror of
https://github.com/kbenestad/mdcms.git
synced 2026-06-18 15:24:32 +00:00
221 lines
6.4 KiB
Markdown
221 lines
6.4 KiB
Markdown
---
|
||
title: Storage Config
|
||
sort: 120
|
||
section-id: configuration
|
||
keywords: storage, memory, disk, SSD tiers, compression, tablespace, IOPS
|
||
description: Configuring NeuralDB storage — memory tiers, disk layout, compression, and tablespaces
|
||
language: en
|
||
---
|
||
|
||
# Storage Config
|
||
|
||
NeuralDB's performance depends heavily on storage configuration. This page covers how to optimise disk layout, configure memory tiers, enable compression, and use tablespaces for data tiering.
|
||
|
||
## Disk Layout Recommendations
|
||
|
||
Separate the data directory, WAL, and vector files onto different physical disks (or at least different volumes with guaranteed IOPS):
|
||
|
||
| Directory | Recommended storage | IOPS requirement |
|
||
|-----------|--------------------|--------------------|
|
||
| `$DATADIR/base/` | NVMe SSD | High random read/write |
|
||
| `$DATADIR/wal/` | NVMe SSD (separate) | High sequential write |
|
||
| `$DATADIR/vectors/` | NVMe SSD or RAM disk | High random read |
|
||
| `$DATADIR/archive/` | HDD or object storage | Low (sequential write) |
|
||
|
||
Configure separate paths in `neuraldb.conf`:
|
||
|
||
```ini
|
||
# Separate WAL onto a dedicated volume
|
||
wal_directory = '/mnt/nvme-wal/neuraldb/wal'
|
||
|
||
# Separate vector storage
|
||
vector_data_directory = '/mnt/nvme-fast/neuraldb/vectors'
|
||
|
||
# Archive destination for WAL shipping
|
||
archive_status_directory = '/var/lib/neuraldb/archive_status'
|
||
```
|
||
|
||
## Memory Tiers
|
||
|
||
NeuralDB has three distinct memory pools:
|
||
|
||
### shared_buffers (Row Store Cache)
|
||
|
||
The page cache for relational data. Sized at 25% of available RAM for a dedicated database server:
|
||
|
||
```ini
|
||
shared_buffers = 32GB # for a 128 GB server
|
||
```
|
||
|
||
### vector_buffer (Vector Index Cache)
|
||
|
||
Holds the HNSW graph in memory. The entire active HNSW graph must fit in `vector_buffer` for optimal performance. When the graph doesn't fit, NeuralDB falls back to disk-based graph traversal, which is 10–50× slower.
|
||
|
||
Calculate the required size:
|
||
|
||
```
|
||
vector_buffer = num_vectors × dimensions × 4 bytes × hnsw_overhead_factor
|
||
```
|
||
|
||
Where `hnsw_overhead_factor` ≈ 1.3 for default HNSW parameters (m=16).
|
||
|
||
```sql
|
||
-- Check current vector index memory usage
|
||
SELECT table_name, index_name,
|
||
pg_size_pretty(hnsw_graph_size_bytes) AS graph_memory,
|
||
pg_size_pretty(vector_data_size_bytes) AS data_size
|
||
FROM neuraldb_stat_vector_indexes;
|
||
```
|
||
|
||
### work_mem (Per-Query Buffer)
|
||
|
||
Used for in-memory sorts, hash joins, and bitmap operations. Set conservatively — each query can allocate multiple `work_mem` buffers:
|
||
|
||
```ini
|
||
# For 200 connections with typical 2 buffers per query:
|
||
# Max memory consumption: 200 × 2 × 128MB = 51 GB
|
||
work_mem = 128MB
|
||
```
|
||
|
||
Override per-session for analytical queries:
|
||
|
||
```sql
|
||
SET work_mem = '2GB';
|
||
SELECT ... complex analytical query ...
|
||
```
|
||
|
||
## Compression
|
||
|
||
### Row Store Compression
|
||
|
||
NeuralDB compresses SSTables on disk. Choose the algorithm based on your priorities:
|
||
|
||
```ini
|
||
# Compression algorithm for row data
|
||
# 'none': no compression (fastest reads/writes, most disk)
|
||
# 'lz4': fast, moderate compression ratio (~2-3×) — default
|
||
# 'zstd': slower compression, better ratio (~3-5×)
|
||
# 'zstd-9': high compression for archival (slow, ~6-8×)
|
||
storage_compression = lz4
|
||
|
||
# Compression level (for zstd only), 1-19
|
||
storage_compression_level = 3
|
||
```
|
||
|
||
### Vector Compression
|
||
|
||
```ini
|
||
# Vector data compression
|
||
# 'none': full precision, largest storage
|
||
# 'lz4': fast, minimal precision loss
|
||
# 'scalar_quantize': reduce to 8-bit (4× smaller, ~1% recall loss)
|
||
# 'product_quantize': very high compression, higher recall loss
|
||
vector_compression = lz4
|
||
```
|
||
|
||
To enable scalar quantisation for a specific index:
|
||
|
||
```sql
|
||
CREATE INDEX ON documents
|
||
USING hnsw (embedding vector_cosine_ops)
|
||
WITH (quantization = 'scalar');
|
||
```
|
||
|
||
Scalar-quantised indexes use 4× less memory and may be faster due to better cache utilisation, at a typical recall cost of 0.5–2%.
|
||
|
||
## Tablespaces
|
||
|
||
Use tablespaces to store different tables or indexes on different volumes:
|
||
|
||
```sql
|
||
-- Create tablespaces pointing to different mount points
|
||
CREATE TABLESPACE fast_ssd LOCATION '/mnt/nvme-fast';
|
||
CREATE TABLESPACE bulk_hdd LOCATION '/mnt/hdd-storage';
|
||
|
||
-- Create a table on fast SSD
|
||
CREATE TABLE hot_documents (
|
||
id UUID PRIMARY KEY,
|
||
content TEXT,
|
||
embedding VECTOR(1536)
|
||
) TABLESPACE fast_ssd;
|
||
|
||
-- Move an index to a specific tablespace
|
||
CREATE INDEX ON hot_documents USING hnsw (embedding vector_cosine_ops)
|
||
TABLESPACE fast_ssd;
|
||
|
||
-- Move old partitions to cheaper storage
|
||
ALTER TABLE documents_2024_q1 SET TABLESPACE bulk_hdd;
|
||
```
|
||
|
||
## Table Partitioning
|
||
|
||
Partition large tables by time or tenant to improve query performance and manageability:
|
||
|
||
```sql
|
||
-- Partition documents by month
|
||
CREATE TABLE documents (
|
||
id UUID NOT NULL DEFAULT gen_random_uuid(),
|
||
content TEXT,
|
||
embedding VECTOR(1536),
|
||
tenant_id UUID,
|
||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||
) PARTITION BY RANGE (created_at);
|
||
|
||
CREATE TABLE documents_2026_01
|
||
PARTITION OF documents
|
||
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01')
|
||
TABLESPACE fast_ssd;
|
||
|
||
CREATE TABLE documents_2025_archive
|
||
PARTITION OF documents
|
||
FOR VALUES FROM ('2025-01-01') TO ('2026-01-01')
|
||
TABLESPACE bulk_hdd;
|
||
```
|
||
|
||
## VACUUM and Autovacuum
|
||
|
||
NeuralDB inherits PostgreSQL's MVCC-based VACUUM system:
|
||
|
||
```ini
|
||
# Autovacuum settings
|
||
autovacuum = on
|
||
autovacuum_naptime = 1min
|
||
autovacuum_vacuum_threshold = 50
|
||
autovacuum_vacuum_scale_factor = 0.05 # vacuum when 5% of rows are dead
|
||
autovacuum_analyze_threshold = 50
|
||
autovacuum_analyze_scale_factor = 0.02 # analyse when 2% of rows change
|
||
|
||
# For high-write tables, increase autovacuum aggressiveness
|
||
autovacuum_vacuum_cost_delay = 2ms # default: 20ms
|
||
autovacuum_vacuum_cost_limit = 400 # default: 200
|
||
```
|
||
|
||
Manually vacuum a table:
|
||
|
||
```sql
|
||
VACUUM ANALYZE documents; -- reclaim space + update statistics
|
||
VACUUM FULL documents; -- full rewrite (blocking, very thorough)
|
||
```
|
||
|
||
## Monitoring Disk Usage
|
||
|
||
```sql
|
||
-- Table sizes
|
||
SELECT table_name,
|
||
pg_size_pretty(pg_total_relation_size(quote_ident(table_name))) AS total,
|
||
pg_size_pretty(pg_relation_size(quote_ident(table_name))) AS table,
|
||
pg_size_pretty(pg_total_relation_size(quote_ident(table_name))
|
||
- pg_relation_size(quote_ident(table_name))) AS indexes
|
||
FROM information_schema.tables
|
||
WHERE table_schema = 'public'
|
||
ORDER BY pg_total_relation_size(quote_ident(table_name)) DESC;
|
||
|
||
-- Vector index sizes
|
||
SELECT * FROM neuraldb_stat_vector_indexes
|
||
ORDER BY hnsw_graph_size_bytes DESC;
|
||
|
||
-- Bloat estimate
|
||
SELECT tablename, n_dead_tup, last_vacuum, last_autovacuum
|
||
FROM pg_stat_user_tables
|
||
ORDER BY n_dead_tup DESC;
|
||
```
|