sqlite-vec対pgvector:デスクトップおよびエッジアプリケーション向け組み込みローカルベクトル検索

目次(11 項目)
ベクトル埋め込みの普及により、効率的な類似性検索の需要が高まっています。サーバーサイドのベクトルデータベースではpgvectorが事実上の標準となっていますが、組み込みアプリケーションやエッジアプリケーションでは、独自のアーキテクチャ上の制約があります。このガイドでは、デスクトップ(Electron、Tauri)およびエッジワーカー環境への適合性に焦点を当て、sqlite-vec(Mozilla/Alex Garcia)とpgvectorを実証的に比較します。RAM消費量、インデックス構築時間、int8スカラー量子化、および10万個の埋め込みに対するリコールを評価します。
アーキテクチャの概要:sqlite-vec vs pgvector
pgvectorは、堅牢なクライアント・サーバー型リレーショナルデータベースであるPostgreSQLを拡張したものです。ACID準拠、複雑なクエリ機能、高可用性機能を提供します。一方、sqlite-vecはSQLiteの拡張機能であり、ベクトル検索をアプリケーションのプロセスに直接組み込みます。この根本的な違いが、それぞれのユースケースとパフォーマンス特性を決定します。
| 機能 | sqlite-vec | pgvector |
|---|---|
| アーキテクチャ | 組み込み(インプロセス) | クライアント・サーバー |
| デプロイ | 単一ファイル、サーバー不要 | PostgreSQLサーバーが必要 |
| データモデル | SQLiteテーブル | PostgreSQLテーブル |
| インデックス作成 | HNSW, IVF, Flat | IVF, HNSW, Flat |
| 量子化 | int8スカラー | なし(0.6.0現在) |
| 並行性 | SQLiteの並行性モデル | PostgreSQLのMVCC |
| ユースケース | エッジ、デスクトップ、モバイル | サーバー、クラウド、分散 |
| セットアップの複雑さ | 低 | 中 |
sqlite-vecの内部
sqlite-vecはSQLiteのロード可能な拡張メカニズムを利用しています。これにより、ベクトル操作のための新しいSQL関数と仮想テーブルが導入されます。コアとなるインデックスアルゴリズム(HNSW、IVF)はRustで実装されており、SQLiteが実行時にロードする共有ライブラリ(.so、.dylib、.dll)にコンパイルされます。これにより、SQLiteライブラリ自体以外の外部依存関係なしにネイティブパフォーマンスを実現できます。int8スカラー量子化は重要な差別化要因であり、ベクトルのストレージとメモリフットプリントを大幅に削減します。
pgvectorの内部
pgvectorはC言語で書かれたPostgreSQLの拡張機能です。新しいvectorデータ型と類似性検索のための演算子を追加します。PostgreSQLのクエリプランナーとトランザクションシステムにシームレスに統合されます。強力ではありますが、そのクライアント・サーバーモデルはネットワーク遅延を発生させ、別のPostgreSQLインスタンスを必要とします。これは組み込みシナリオでは非現実的であることがよくあります。
セットアップとデータ準備
ベンチマークには、文変換モデル(例:all-MiniLM-L6-v2)から生成された10万個の384次元埋め込みのデータセットを使用します。
まず、合成埋め込みを生成します。
import numpy as np
import sqlite3
import time
from pgvector.psycopg import register_vector
import psycopg
import os
# Configuration
NUM_EMBEDDINGS = 100_000
DIMENSIONS = 384
SQLITE_DB_PATH = "embeddings.db"
PG_DB_NAME = "vector_benchmark"
PG_USER = "postgres"
PG_PASSWORD = "password" # Use a strong password in production
PG_HOST = "localhost"
PG_PORT = 5432
# Generate synthetic embeddings
print(f"Generating {NUM_EMBEDDINGS} synthetic embeddings...")
embeddings = np.random.rand(NUM_EMBEDDINGS, DIMENSIONS).astype(np.float32)
# Normalize embeddings to unit length, common practice for cosine similarity
embeddings = embeddings / np.linalg.norm(embeddings, axis=1, keepdims=True)
print("Embeddings generated.")
# --- SQLite-vec Setup ---
print("\n--- Setting up sqlite-vec ---")
try:
# Connect to SQLite database
sqlite_conn = sqlite3.connect(SQLITE_DB_PATH)
sqlite_cursor = sqlite_conn.cursor()
# Load sqlite-vec extension (adjust path as necessary for your OS)
# On Linux: libsqlite_vec.so, macOS: libsqlite_vec.dylib, Windows: sqlite_vec.dll
# Ensure you have compiled/downloaded the correct extension for your system.
# Example for a pre-compiled Linux extension:
sqlite_conn.enable_load_extension(True)
try:
sqlite_cursor.execute("SELECT load_extension('./libsqlite_vec.so');")
except sqlite3.OperationalError as e:
print(f"Error loading sqlite-vec extension: {e}")
print("Please ensure 'libsqlite_vec.so' (or .dylib/.dll) is in the current directory or specified path.")
print("You might need to compile it from source: https://github.com/asg017/sqlite-vec")
exit(1)
sqlite_conn.enable_load_extension(False)
# Create table for embeddings
sqlite_cursor.execute(f"""
CREATE TABLE IF NOT EXISTS items (
id INTEGER PRIMARY KEY,
embedding BLOB
);
""")
sqlite_conn.commit()
# Insert embeddings
print(f"Inserting {NUM_EMBEDDINGS} embeddings into sqlite-vec...")
insert_start_time = time.time()
batch_size = 1000
for i in range(0, NUM_EMBEDDINGS, batch_size):
batch = embeddings[i:i+batch_size]
sqlite_cursor.executemany(
"INSERT INTO items (id, embedding) VALUES (?, ?)",
[(i + j, vec.tobytes()) for j, vec in enumerate(batch)]
)
sqlite_conn.commit()
insert_end_time = time.time()
print(f"sqlite-vec insertion time: {insert_end_time - insert_start_time:.2f} seconds")
except Exception as e:
print(f"Error during sqlite-vec setup: {e}")
if 'sqlite_vec' in str(e):
print("Ensure the sqlite-vec extension is correctly compiled and loaded.")
exit(1)
# --- pgvector Setup ---
print("\n--- Setting up pgvector ---")
try:
# Connect to PostgreSQL
pg_conn = psycopg.connect(f"dbname={PG_DB_NAME} user={PG_USER} password={PG_PASSWORD} host={PG_HOST} port={PG_PORT}")
pg_conn.autocommit = True # For CREATE DATABASE/EXTENSION
pg_cursor = pg_conn.cursor()
# Create database if not exists
try:
pg_cursor.execute(f"CREATE DATABASE {PG_DB_NAME};")
print(f"Database '{PG_DB_NAME}' created.")
except psycopg.errors.DuplicateDatabase:
print(f"Database '{PG_DB_NAME}' already exists.")
except Exception as e:
print(f"Error creating database: {e}")
print("Ensure PostgreSQL is running and user has permissions.")
exit(1)
pg_conn.close() # Close and reconnect to the new database
pg_conn = psycopg.connect(f"dbname={PG_DB_NAME} user={PG_USER} password={PG_PASSWORD} host={PG_HOST} port={PG_PORT}")
pg_cursor = pg_conn.cursor()
# Enable pgvector extension
pg_cursor.execute("CREATE EXTENSION IF NOT EXISTS vector;")
print("pgvector extension enabled.")
# Register vector type for psycopg
register_vector(pg_conn)
# Create table for embeddings
pg_cursor.execute(f"""
CREATE TABLE IF NOT EXISTS items (
id SERIAL PRIMARY KEY,
embedding vector({DIMENSIONS})
);
""")
pg_conn.commit()
# Insert embeddings
print(f"Inserting {NUM_EMBEDDINGS} embeddings into pgvector...")
insert_start_time = time.time()
batch_size = 1000
for i in range(0, NUM_EMBEDDINGS, batch_size):
batch = embeddings[i:i+batch_size]
pg_cursor.executemany(
"INSERT INTO items (embedding) VALUES (%s)",
[(vec,) for vec in batch]
)
pg_conn.commit()
insert_end_time = time.time()
print(f"pgvector insertion time: {insert_end_time - insert_start_time:.2f} seconds")
except Exception as e:
print(f"Error during pgvector setup: {e}")
print("Ensure PostgreSQL is running and accessible.")
exit(1)
print("\nData setup complete for both databases.")
sqlite-vec拡張機能に関する注意点: sqlite-vecはソースからコンパイルするか、特定のOSとアーキテクチャ用にプリコンパイルされたバイナリをダウンロードする必要があります。共有ライブラリ(libsqlite_vec.so、libsqlite_vec.dylib、またはsqlite_vec.dll)をアプリケーションからアクセス可能な場所に配置するか、そのフルパスを指定してください。
インデックス作成とベンチマーク
sqlite-vecとpgvectorの両方について、インデックス構築時間、RAM消費量、検索パフォーマンス(リコールとレイテンシ)をベンチマークします。sqlite-vecについては、int8スカラー量子化の影響も評価します。
インデックス作成戦略
sqlite-vec: 速度とリコールのバランスが取れているHNSW(Hierarchical Navigable Small Worlds)を使用します。int8量子化HNSWもテストします。pgvector: IVF(Inverted File Index)とHNSWを使用します。IVFは小規模なデータセットでの厳密な最近傍検索では高速なことが多いですが、HNSWは大規模なデータセットでより良いリコールを提供します。
import numpy as np
import sqlite3
import time
from pgvector.psycopg import register_vector
import psycopg
import os
import faiss # For ground truth and recall calculation
from sklearn.metrics import top_k_accuracy_score
# Configuration (same as above, ensure NUM_EMBEDDINGS, DIMENSIONS match)
NUM_EMBEDDINGS = 100_000
DIMENSIONS = 384
SQLITE_DB_PATH = "embeddings.db"
PG_DB_NAME = "vector_benchmark"
PG_USER = "postgres"
PG_PASSWORD = "password"
PG_HOST = "localhost"
PG_PORT = 5432
K_SEARCH = 10 # Number of neighbors to retrieve
NUM_QUERIES = 100 # Number of queries for benchmarking
# Reconnect to databases
sqlite_conn = sqlite3.connect(SQLITE_DB_PATH)
sqlite_conn.enable_load_extension(True)
sqlite_cursor = sqlite_conn.cursor()
try:
sqlite_cursor.execute("SELECT load_extension('./libsqlite_vec.so');")
except sqlite3.OperationalError as e:
print(f"Error loading sqlite-vec extension: {e}")
exit(1)
sqlite_conn.enable_load_extension(False)
pg_conn = psycopg.connect(f"dbname={PG_DB_NAME} user={PG_USER} password={PG_PASSWORD} host={PG_HOST} port={PG_PORT}")
pg_cursor = pg_conn.cursor()
register_vector(pg_conn)
# Load all embeddings for ground truth and query generation
print("Loading all embeddings for ground truth...")
sqlite_cursor.execute("SELECT embedding FROM items ORDER BY id")
all_embeddings_bytes = sqlite_cursor.fetchall()
all_embeddings = np.array([np.frombuffer(e[0], dtype=np.float32) for e in all_embeddings_bytes])
print(f"Loaded {len(all_embeddings)} embeddings.")
# Generate query embeddings (random subset of existing embeddings)
query_indices = np.random.choice(NUM_EMBEDDINGS, NUM_QUERIES, replace=False)
query_embeddings = all_embeddings[query_indices]
# --- Ground Truth (using FAISS for exact nearest neighbors) ---
print("\nCalculating ground truth using FAISS...")
faiss_index = faiss.IndexFlatL2(DIMENSIONS) # L2 for cosine similarity on normalized vectors
faiss_index.add(all_embeddings)
D_gt, I_gt = faiss_index.search(query_embeddings, K_SEARCH)
print("Ground truth calculated.")
# Function to calculate recall@K
def calculate_recall(retrieved_ids, ground_truth_ids, k):
hits = 0
for i in range(len(retrieved_ids)):
# Check if any of the top K retrieved IDs are in the ground truth top K
# Note: For exact matches, we expect the query itself to be in the top K.
# For approximate search, we check if the retrieved set overlaps with the ground truth set.
retrieved_set = set(retrieved_ids[i])
ground_truth_set = set(ground_truth_ids[i])
if not ground_truth_set.isdisjoint(retrieved_set):
hits += 1
return hits / len(retrieved_ids)
# --- Benchmarking Function ---
def benchmark_search(db_type, index_type, index_params, query_sql, conn, cursor, is_sqlite_vec=False, is_quantized=False):
print(f"\n--- Benchmarking {db_type} with {index_type} ({index_params}) ---")
# Drop existing index if any
if db_type == "sqlite-vec":
try:
cursor.execute(f"DROP TABLE IF EXISTS items_vec_idx;")
conn.commit()
except sqlite3.OperationalError:
pass # Index might not exist
elif db_type == "pgvector":
try:
cursor.execute(f"DROP INDEX IF EXISTS items_embedding_idx;")
conn.commit()
except psycopg.errors.UndefinedObject:
pass # Index might not exist
# Build Index
print(f"Building {index_type} index...")
build_start_time = time.time()
if db_type == "sqlite-vec":
if is_quantized:
cursor.execute(f"CREATE VIRTUAL TABLE items_vec_idx USING vec0(items, embedding, {DIMENSIONS}, 'quantizer=int8', '{index_type}={index_params}');")
else:
cursor.execute(f"CREATE VIRTUAL TABLE items_vec_idx USING vec0(items, embedding, {DIMENSIONS}, '{index_type}={index_params}');")
conn.commit()
elif db_type == "pgvector":
cursor.execute(f"CREATE INDEX items_embedding_idx ON items USING {index_type}(embedding {index_params});")
conn.commit()
build_end_time = time.time()
print(f"Index build time: {build_end_time - build_start_time:.2f} seconds")
# Measure RAM usage (approximate, depends on OS/tooling)
# For sqlite-vec, this is the process's RAM. For pgvector, it's the PostgreSQL server's RAM.
# This is hard to measure programmatically in a cross-platform way.
# For a real benchmark, use OS-specific tools (e.g., `ps aux` on Linux, Activity Monitor on macOS).
print("RAM usage measurement requires external OS tools.")
# Perform searches
search_latencies = []
retrieved_ids_list = []
print(f"Performing {NUM_QUERIES} searches...")
for i, query_vec in enumerate(query_embeddings):
search_start_time = time.time()
if db_type == "sqlite-vec":
# For sqlite-vec, the query uses the virtual table
cursor.execute(query_sql, (query_vec.tobytes(), K_SEARCH))
elif db_type == "pgvector":
cursor.execute(query_sql, (query_vec, K_SEARCH))
results = cursor.fetchall()
search_end_time = time.time()
search_latencies.append(search_end_time - search_start_time)
retrieved_ids_list.append([r[0] for r in results]) # Assuming ID is the first column
avg_latency = np.mean(search_latencies)
p95_latency = np.percentile(search_latencies, 95)
print(f"Average search latency: {avg_latency * 1000:.2f} ms")
print(f"P95 search latency: {p95_latency * 1000:.2f} ms")
# Calculate recall
# We need to map the ground truth IDs to the retrieved IDs.
# For simplicity, we assume the IDs are 0-indexed and correspond to the original `all_embeddings` array.
# The ground truth `I_gt` contains indices, which are effectively IDs.
recall = calculate_recall(retrieved_ids_list, I_gt, K_SEARCH)
print(f"Recall@{K_SEARCH}: {recall:.4f}")
return {
"db_type": db_type,
"index_type": index_type,
"index_params": index_params,
"build_time": build_end_time - build_start_time,
"avg_latency_ms": avg_latency * 1000,
"p95_latency_ms": p95_latency * 1000,
"recall_at_k": recall
}
# --- Run Benchmarks ---
results = []
# sqlite-vec: HNSW (default float32)
results.append(benchmark_search(
"sqlite-vec", "hnsw", "M=16,ef_construction=100",
"SELECT id FROM items_vec_idx WHERE embedding MATCH ? ORDER BY distance LIMIT ?",
sqlite_conn, sqlite_cursor, is_sqlite_vec=True
))
# sqlite-vec: HNSW with int8 quantization
results.append(benchmark_search(
"sqlite-vec", "hnsw", "M=16,ef_construction=100",
"SELECT id FROM items_vec_idx WHERE embedding MATCH ? ORDER BY distance LIMIT ?",
sqlite_conn, sqlite_cursor, is_sqlite_vec=True, is_quantized=True
))
# pgvector: IVF (lists=100)
# For 100k embeddings, lists=100 is a reasonable starting point.
# The `vector_l2_ops` is for L2 distance, which is equivalent to cosine for normalized vectors.
results.append(benchmark_search(
"pgvector", "ivfflat", "vector_l2_ops, lists=100",
f"SELECT id FROM items ORDER BY embedding <-> %s LIMIT %s",
pg_conn, pg_cursor
))
# pgvector: HNSW (m=16, ef_construction=64)
# Note: pgvector's HNSW parameters are slightly different from sqlite-vec's.
# `m` is the number of neighbors, `ef_construction` is for build time.
results.append(benchmark_search(
"pgvector", "hnsw", "vector_l2_ops, m=16, ef_construction=64",
f"SELECT id FROM items ORDER BY embedding <-> %s LIMIT %s",
pg_conn, pg_cursor
))
# Print results table
print("\n--- Benchmark Summary ---")
print("| DB | Index Type | Params | Build Time (s) | Avg Latency (ms) | P95 Latency (ms) | Recall@10 |")
print("|---|---|---|---|---|---|---|")
for r in results:
print(f"| {r['db_type']} | {r['index_type']} | {r['index_params']} | {r['build_time']:.2f} | {r['avg_latency_ms']:.2f} | {r['p95_latency_ms']:.2f} | {r['recall_at_k']:.4f} |")
# Clean up
sqlite_conn.close()
pg_conn.close()
ベンチマーク結果の解釈
具体的な数値は、ハードウェア、正確なsqlite-vecとpgvectorのバージョン、およびPostgreSQLの設定によって異なります。ただし、一般的な傾向は以下の通りです。
- インデックス構築時間:
sqlite-vecは、インプロセスであるため、ネットワークオーバーヘッドや複雑なサーバーサイドのリソース管理を回避できるため、インデックスをより速く構築することがよくあります。pgvectorのHNSWは、構築中にリソースを大量に消費する可能性があります。 - RAM消費量:
sqlite-vecのint8量子化は、sqlite-vec(非量子化)とpgvectorの両方において、float32インデックスと比較してRAMフットプリントを劇的に削減します。これはエッジデバイスにとって非常に重要です。 - 検索レイテンシ: 小規模から中規模のデータセット(例:10万〜100万ベクトル)の場合、
sqlite-vecはネットワークオーバーヘッドがゼロであるため、pgvectorと同等かそれ以下のレイテンシを達成できます。データセットが大規模になると、pgvectorのサーバーサイド最適化とワークロード分散能力が優位に立つ可能性があります。 - リコール:
int8量子化は、精度損失によりリコールがわずかに低下します。トレードオフとして、メモリとストレージが大幅に削減されます。多くのアプリケーションでは、このリコールの低下は許容範囲内です。HNSWは、同様のパフォーマンスレベルでIVFよりも一般的に優れたリコールを提供します。
本番環境での落とし穴とトラブルシューティング
sqlite-vec
-
拡張機能のロードエラー:
- 失敗モード:
sqlite3.OperationalError: unable to load extension: ... - 原因:
libsqlite_vec.so(または.dylib、.dll)へのパスが間違っている、アーキテクチャの不一致(例:x86でARMバイナリをロードしようとしている)、または依存関係の欠落。 - 修正:
- 指定されたパスに拡張ファイルが存在することを確認します。
- ターゲットシステムの正しいOSとCPUアーキテクチャ用に拡張機能がコンパイルされていることを確認します。
- 欠落している共有ライブラリがないか、
ldd libsqlite_vec.so(Linux)またはotool -L libsqlite_vec.dylib(macOS)を確認します。 - Electron/Tauriの場合、拡張機能が正しくバンドルされ、アプリケーションのリソースパスからロードされていることを確認します。
- 失敗モード:
-
int8量子化によるリコールの低下:- 失敗モード:
int8量子化を有効にした後、検索結果が著しく悪化する。 - 原因:
int8量子化は、各ベクトルコンポーネントの精度を32ビット浮動小数点から8ビット整数に削減します。この情報損失は、特にすでに非常に近い埋め込みの場合、類似性計算に影響を与える可能性があります。 - 修正:
- リコールの低下がアプリケーションの要件にとって許容できるかどうかを評価します。
int8を使用したHNSWで、より大きなMまたはef_constructionを使用することを検討します。ただし、これによりインデックス構築時間とサイズが増加します。- 高精度が最優先される場合は、
float32(デフォルト)を使用します。
- 失敗モード:
-
並行性に関する問題:
- 失敗モード:
sqlite3.OperationalError: database is locked - 原因: SQLiteは単一ライター、複数リーダーの並行性用に設計されています。複数のスレッド/プロセスが同時に書き込みを試みると、このエラーが発生します。
- 修正:
- 適切なロックメカニズムを実装します(例:Pythonの
threading.Lock、Rust/C++のMutex)。 - WAL(Write-Ahead Logging)モードを使用します:
PRAGMA journal_mode=WAL;。これにより、ライターがアクティブな間でもリーダーの並行性が大幅に向上します。 - 高頻度の書き込み並行性が必要な場合、SQLiteは適切な選択肢ではない可能性があります。クライアント・サーバー型データベースを検討してください。
- 適切なロックメカニズムを実装します(例:Pythonの
- 失敗モード:
pgvector
-
PostgreSQL接続エラー:
- 失敗モード:
psycopg.OperationalError: connection to server at "localhost" (::1), port 5432 failed: Connection refused - 原因: PostgreSQLサーバーが実行されていない、ホスト/ポートが間違っている、またはファイアウォールが接続をブロックしている。
- 修正:
- PostgreSQLサービスを開始します。
listen_addressesとportのpostgresql.confを確認します。- クライアント認証ルールについて
pg_hba.confを確認します。 - ポート5432への接続を許可するようにファイアウォールルールを調整します。
- 失敗モード:
-
インデックス構築時間とリソース消費:
- 失敗モード:
CREATE INDEXが過度に時間がかかる、またはサーバー上の利用可能なRAM/CPUをすべて消費する。 - 原因: 大規模なデータセット、サーバーリソースの不足、または最適ではないインデックスパラメータ(例:HNSWの
ef_constructionが高すぎる)。 - 修正:
- インデックス構築のために
postgresql.confのwork_memを増やします。 - PostgreSQLサーバーにより多くのRAMとCPUを割り当てます。
- HNSWパラメータ(
m、ef_construction)またはIVFパラメータ(lists)を調整して、構築時間、サイズ、リコールのバランスを取ります。低い値から始めて、徐々に増やします。 - オフピーク時にインデックスを構築することを検討します。
- インデックス構築のために
- 失敗モード:
-
vector型が見つかりません:- 失敗モード:
psycopg.errors.UndefinedObject: type "vector" does not exist - 原因: データベースで
pgvector拡張機能が有効になっていない。 - 修正: ターゲットデータベースで
CREATE EXTENSION IF NOT EXISTS vector;を実行します。
- 失敗モード:
よくある質問
-
pgvectorではなくsqlite-vecを選択すべきなのはどのような場合ですか? 完全なクライアント・サーバー型データベースが過剰または非現実的な、組み込みアプリケーション(デスクトップ、モバイル、エッジデバイス)にはsqlite-vecを選択してください。これには、外部依存関係なしにローカルで低遅延のベクトル検索が必要なElectron/Tauriアプリ、IoTデバイス、またはサーバーレスエッジ関数が含まれます。そのint8量子化は、メモリ制約のある環境にとって強力な利点です。 -
sqlite-vecは何百万ものベクトルを処理できますか? はい、sqlite-vecは特にHNSWインデックスとint8量子化を使用することで、何百万ものベクトルを処理できます。ただし、パフォーマンスは最終的にホストデバイスのCPU、RAM、ディスクI/Oによって制限されます。数千万を超えるデータセットや分散処理が必要な場合は、pgvector(シャーディングの可能性あり)やMilvus/Weaviateのような専用のベクトルデータベースがより適しています。 -
sqlite-vecにおけるint8量子化のパフォーマンスへの影響は何ですか?int8量子化は通常、ベクトルのメモリフットプリントを75%削減します(次元あたり4バイトから1バイトへ)。これにより、インデックスサイズが小さくなり、ロードが高速化され、RAM消費量が削減されます。トレードオフとして、リコールがわずかに(データセットと埋め込み品質に応じて通常1〜5%)低下しますが、これは多くの場合許容範囲内です。 -
デスクトップアプリケーションで
sqlite-vecを安全に保つにはどうすればよいですか?sqlite-vecは組み込みデータベースであるため、そのセキュリティはアプリケーションのセキュリティに結びついています。- データ暗号化: SQLiteの暗号化拡張機能(例:SQLCipher)を使用して、データベースファイルを保存時に暗号化します。
- コードの整合性: アプリケーションバイナリが署名され、改ざん防止されていることを確認します。
- 入力検証: データベースがローカルであっても、SQLインジェクションを防ぐためにすべてのユーザー入力をサニタイズします。
- アクセス制御: 複数のユーザーまたはコンポーネントがデータベースとやり取りする場合、アプリケーションレベルのアクセス制御を実装します。
-
sqlite-vecとpgvectorでHNSWパラメータをチューニングするためのベストプラクティスは何ですか?M(ノードあたりの近傍数): グラフの接続性を制御します。Mが高いほどリコールが向上しますが、インデックスサイズと構築時間が増加します。一般的な値:16〜64。ef_construction(構築時の検索範囲): インデックス構築中のグラフの品質を制御します。ef_constructionが高いほどリコールが向上しますが、構築時間が大幅に長くなります。一般的な値:64〜200。ef_search(クエリ時の検索範囲): クエリ時の検索精度を制御します。ef_searchが高いほどリコールが向上しますが、クエリレイテンシが増加します。これはインデックスを再構築せずに動的にチューニングできることがよくあります。一般的な値:32〜128。lists(IVF):pgvectorのIVFの場合、lists(反転リストの数)はsqrt(N)から10*sqrt(N)であるべきです。ここでNはベクトルの数です。リストが多いほど検索時間は短縮されますが、インデックスサイズと構築時間が増加します。- 反復チューニング: 控えめなパラメータから始めてベンチマークを行い、目的のリコール/レイテンシのトレードオフが達成されるまで、
M、ef_construction、またはlistsを徐々に増やします。
Free In-Browser Developer Tools
Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.
Related Articles

本番環境のSQLite: WALモード、高並行性、そして実践的なPRAGMA設定
高スループットな本番環境でSQLiteをマスターしましょう。先行書き込みログ(WAL)、busy_timeoutのチューニング、読み書きの同時実行性、そして実用的なベンチマークについて解説します。
Read more大規模ベクトル検索:pgvectorとSQLite-vecにおけるHNSW対IVFFlatインデックス
pgvectorとsqlite-vecにおけるHNSWとIVFFlatベクトルインデックスアルゴリズムを比較。再現率、構築時間、メモリフットプリント、クエリレイテンシを分析します。
Read more
pgvectorとハイブリッド検索でプロダクションRAGを構築する
pgvectorとハイブリッド検索を組み合わせた堅牢なRAGアーキテクチャの構築方法を学び、全文ベクトル検索とBM25を組み合わせて検索精度を向上させ、HNSWインデックスを調整し、プロダクションPythonクライアントを出荷します。
Read more