sqlite-optimization
SQLite Performance Optimization
Quick Start: Baseline Configuration
Set these PRAGMAs on every connection for most applications:
PRAGMA journal_mode = WAL; -- Non-blocking concurrent reads/writes
PRAGMA synchronous = NORMAL; -- Fast commits, safe for WAL mode
PRAGMA foreign_keys = ON; -- Data integrity
PRAGMA cache_size = -64000; -- 64MB cache (negative = KB)
PRAGMA temp_store = MEMORY; -- In-memory temp tables/sorting
PRAGMA busy_timeout = 5000; -- 5s retry on lock contention
PRAGMA mmap_size = 2147483648; -- 2GB memory-mapped I/O (benchmark first)
Run periodically:
PRAGMA optimize; -- Update query planner statistics
Optimization Workflow
1. Diagnose with EXPLAIN QUERY PLAN
EXPLAIN QUERY PLAN SELECT ...;
Key indicators:
SCAN TABLE→ Full table scan (O(N)) — needs index or query rewriteSEARCH TABLE USING INDEX→ Index lookup (O(log N)) — goodCOVERING INDEX→ Index-only scan, no table lookup — optimalUSE TEMP B-TREE→ Sorting not covered by index — consider index with ORDER BY columns
2. Schema Design
Use INTEGER PRIMARY KEY for rowid alias:
CREATE TABLE users (
id INTEGER PRIMARY KEY, -- Alias for rowid, no extra index
email TEXT NOT NULL,
created_at TEXT
);
Choose appropriate types:
INTEGERfor IDs, counters, booleansREALfor floatsTEXTfor strings- Avoid large BLOBs in hot tables; use separate table or external files
WITHOUT ROWID for junction tables:
CREATE TABLE user_roles (
user_id INTEGER,
role_id INTEGER,
PRIMARY KEY (user_id, role_id)
) WITHOUT ROWID; -- Clustered index, no rowid overhead
3. Indexing Strategy
Create indexes for real query patterns:
-- For: WHERE user_id = ? AND created_at >= ? ORDER BY created_at DESC
CREATE INDEX idx_orders_user_created
ON orders (user_id, created_at DESC);
Covering indexes eliminate table lookups:
-- For: SELECT user_id, total FROM orders WHERE user_id = ?
CREATE INDEX idx_orders_cover
ON orders (user_id, created_at DESC, total);
Partial indexes for subsets:
CREATE INDEX idx_active_users
ON users (email) WHERE status = 'active';
Expression indexes:
CREATE INDEX idx_users_lower_email
ON users (lower(email));
Avoid indexing:
- Small tables (< hundreds of rows)
- Low-cardinality columns alone (booleans)
- Columns rarely in WHERE/JOIN/ORDER BY
4. Write Optimization
Always batch writes in transactions:
BEGIN TRANSACTION;
INSERT INTO logs (...) VALUES (...);
-- ... many more inserts
COMMIT;
Throughput jumps from ~50 inserts/sec (autocommit) to 50,000+ inserts/sec.
Use prepared statements:
stmt = conn.prepare("INSERT INTO logs (ts, level, msg) VALUES (?, ?, ?)")
for row in data:
stmt.execute(row)
Optimal batch size: 1,000–10,000 rows balances throughput vs memory.
5. Concurrency Configuration
WAL mode enables concurrent readers + one writer:
- Readers don't block writers
- Writers don't block readers
- Uses
.waland.shmfiles alongside main DB
Handle contention:
PRAGMA busy_timeout = 5000; -- Retry for 5 seconds before SQLITE_BUSY
Architecture pattern: Single writer thread/process + many reader connections.
6. Maintenance
VACUUM after large deletes:
VACUUM; -- Rebuilds and defragments DB file
- Run during maintenance windows (blocking operation)
- Requires free disk space ≈ DB size
Auto-vacuum for incremental reclaim:
PRAGMA auto_vacuum = INCREMENTAL;
VACUUM; -- Apply change
-- Then periodically:
PRAGMA incremental_vacuum;
Keep statistics fresh:
PRAGMA optimize; -- Smart, selective ANALYZE
-- Or full:
ANALYZE;
Reference Files
- pragma-reference.md — Complete PRAGMA settings with tradeoffs
- indexing-guide.md — Advanced indexing patterns and diagnostics
- architecture.md — SQLite internals: B-trees, VDBE, Pager, concurrency mechanics
Common Anti-Patterns
| Problem | Solution |
|---|---|
SELECT * |
List only needed columns |
| Loop with single inserts | Batch in transaction |
| Correlated subquery per row | Rewrite as JOIN |
| Index on every column | Index only queried columns |
No busy_timeout |
Set appropriate timeout |
| Rollback journal mode | Switch to WAL |
| Never running ANALYZE | Run PRAGMA optimize periodically |
Environment-Specific Notes
Mobile (iOS/Android):
- WAL +
synchronous = NORMAL - Background maintenance jobs for VACUUM
- Local storage only (not SD card)
Server/Desktop:
- Centralize writes in single thread if possible
- Set PRAGMAs immediately after connection open
- Consider connection pooling with writer serialization
More from jrajasekera/claude-skills
pandoc-converter
Convert documents between formats using Pandoc. Use when the user asks to convert files between formats like markdown, docx, html, pdf, latex, epub, rtf, csv, xlsx, or pptx. Triggers on requests like "convert this to Word", "export as PDF", "turn this markdown into HTML", or "convert the CSV to a table".
48openrouter-api
OpenRouter API integration for unified access to 400+ LLM models from 70+ providers. Use when building applications that need to call OpenRouter's API for chat completions, streaming, tool calling, structured outputs, or model routing. Triggers on OpenRouter, model routing, multi-model, provider fallbacks, or when users need to access multiple LLM providers through a single API.
18venice-ai-api
Venice.ai API integration for privacy-first AI applications. Use when building applications with Venice.ai API for chat completions, image generation, video generation, text-to-speech, speech-to-text, or embeddings. Triggers on Venice, Venice.ai, uncensored AI, privacy-first AI, or when users need OpenAI-compatible API with uncensored models.
14z-ai-api
|
10codex-review
Use after creating design docs or implementation plans to get cross-agent review from Codex. Auto-triggers for non-trivial plans; asks first for simple changes. Captures feedback, addresses critical issues, presents minor concerns for user decision.
9article-extractor
Extract clean article content from URLs and save as markdown. Triggers when user provides a webpage URL and wants to download it, extract content, get a clean version without ads, capture an article for offline reading, save an article, grab content from a page, archive a webpage, clip an article, or read something later. Handles blog posts, news articles, tutorials, documentation pages, and similar web content. Supports Wayback Machine for dead links or paywalled content. This skill handles the entire workflow - do NOT use web_fetch or other tools first, just call the extraction script directly with the URL.
7