Skip to main content

SQLite as a Blockchain State Store: Architecture, Performance, and the Case for Simplicity

· 8 min read
Prasad Kumkar
Founder & CEO, ChainScore Labs

When most engineers hear "production database," they think PostgreSQL or MySQL. When they hear "SQLite," they think "development only" or "mobile apps."

This is wrong. SQLite in WAL (Write-Ahead Logging) mode is a legitimate production database — it powers every iPhone, every Android device, every Chrome and Firefox browser, and most embedded systems. It processes millions of writes per day in some of the most demanding environments on the planet.

So why shouldn't it power a blockchain's world state?

Here's the architecture, the performance characteristics, and why using SQLite for blockchain state storage is a feature, not a compromise.

The Architecture

What Gets Stored

MiniLedger's SQLite database stores the world state — the current state of every key on the ledger. This is separate from the block store, which is a sequential append-only file of serialized blocks.

The distinction matters:

  • Block store: Immutable history. Write-once, read for verification or replay. Append-only. File-based.
  • World state: Current state. Frequently read and written. Must support complex queries. SQLite.
┌─────────────────────────────────────────────┐
│ MiniLedger Node │
│ │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ Block Store │ │ State Database │ │
│ │ (append-only)│ │ (SQLite + WAL) │ │
│ │ │ │ │ │
│ │ block-001.dat│ │ world_state table │ │
│ │ block-002.dat│ │ ┌───────────────┐ │ │
│ │ block-003.dat│ │ │ key │ value │ │ │
│ │ ... │ │ │───────────────│ │ │
│ │ │ │ │ tx:001 │ {...} │ │ │
│ │ Immutable │ │ │ tx:002 │ {...} │ │ │
│ │ Historical │ │ │ acct:A │ {...} │ │ │
│ └──────────────┘ │ └───────────────┘ │ │
│ │ │ │
│ │ Mutable, queryable │ │
│ └───────────────────┘ │
└─────────────────────────────────────────────┘

Schema

CREATE TABLE world_state (
key TEXT PRIMARY KEY,
value TEXT NOT NULL, -- JSON-encoded
version INTEGER DEFAULT 1,
updated_at INTEGER NOT NULL, -- Unix timestamp ms
updated_by TEXT NOT NULL, -- Public key of updater
block_height INTEGER NOT NULL
);

CREATE INDEX idx_world_state_updated_at ON world_state(updated_at);
CREATE INDEX idx_world_state_block_height ON world_state(block_height);

WAL Mode

SQLite defaults to a rollback journal — writers block readers and vice versa. WAL (Write-Ahead Logging) mode changes this:

┌──────────────────────────────────────────────────┐
│ WAL Mode Operation │
│ │
│ Reader 1 ──► Reads from database file │
│ Reader 2 ──► Reads from database file │
│ Reader 3 ──► Reads from database file │
│ (All concurrent, no blocking) │
│ │
│ Writer ──► Appends to WAL file │
│ (Single writer, no reader blocking) │
│ │
│ Checkpointer ──► Periodically merges WAL → DB │
│ │
│ files: │
│ ledger.db ← The main database │
│ ledger.db-wal ← Write-ahead log (active) │
│ ledger.db-shm ← Shared memory index │
└──────────────────────────────────────────────────┘

The key advantage: readers never block writers, and writers never block readers. For a blockchain node that's continuously processing API read requests while writing new blocks, this is critical.

Activation

WAL mode is activated on database open:

import Database from 'better-sqlite3';

const db = new Database('./ledger/miniledger.db');
db.pragma('journal_mode = WAL');
db.pragma('synchronous = NORMAL'); // Safe with WAL
db.pragma('foreign_keys = ON');
db.pragma('busy_timeout = 5000'); // 5 second busy wait

The SQLite Advantage

1. Single File, Portable

The entire blockchain state is one file: miniledger.db. Back it up by copying the file (during a checkpoint for consistency). Restore by copying it back. No pg_dump, no mysqlimport, no connection strings, no user management.

Disaster recovery becomes:

cp miniledger.db miniledger-backup-$(date +%Y%m%d).db

2. Zero Configuration

PostgreSQL requires configuration of connection limits, shared buffers, work memory, WAL settings, vacuum thresholds, and authentication. SQLite requires: nothing. The defaults are sensible. WAL mode is a one-line pragma.

3. No Network Overhead

The database is accessed via function calls, not TCP connections. Query latency is measured in microseconds, not milliseconds. For a blockchain node that queries state on every transaction validation, this matters.

PostgreSQL:  App → TCP → PG server → TCP → App  (~0.5-2ms latency)
SQLite: App → function call → App (~0.01-0.05ms latency)

4. Full SQL, Not a Subset

SQLite supports: SELECT, INSERT, UPDATE, DELETE, JOIN (inner, left, cross), GROUP BY, HAVING, ORDER BY, LIMIT/OFFSET, UNION, WITH (CTEs), window functions, JSON functions, full-text search (FTS5), and more.

The JSON functions are particularly useful for blockchain state:

-- Extract nested JSON fields
SELECT json_extract(value, '$.user.name') as name,
json_extract(value, '$.user.email') as email
FROM world_state;

-- Filter on JSON values
SELECT key FROM world_state
WHERE json_extract(value, '$.status') = 'active';

-- Update JSON fields
UPDATE world_state
SET value = json_set(value, '$.last_login', '2026-05-20')
WHERE key = 'user:alice';

-- Aggregate JSON data
SELECT
json_extract(value, '$.category') as category,
COUNT(*) as count,
SUM(CAST(json_extract(value, '$.amount') AS REAL)) as total
FROM world_state
WHERE key LIKE 'invoice:%'
GROUP BY category;

5. 35% Smaller Than PostgreSQL

For the same ledger state with 1 million records, SQLite's storage footprint is approximately 35% smaller than PostgreSQL. For a blockchain node that's deployed on modest hardware, this matters.

6. No Separate Process to Manage

PostgreSQL runs as a separate OS process (or Docker container). It needs to be started, stopped, monitored, and configured independently of the application. SQLite is embedded — it runs in-process. When the MiniLedger node starts, the database is ready. When it stops, the database is cleanly closed.


The Tradeoffs (Honesty Required)

SQLite is not the right choice for every scenario. Here's where it falls short:

1. Single Writer

SQLite allows only one writer at a time. If two transactions attempt to write simultaneously, one will wait (up to the busy timeout). For a blockchain, this is rarely an issue: the consensus mechanism serializes block writing already. The node processes one block at a time, sequentially.

Not a problem for: Blockchain nodes, embedded applications, single-writer workloads.

Problematic for: Multi-writer web applications, analytics databases with concurrent ETL pipelines.

2. No Native Replication

SQLite doesn't replicate. But for a blockchain node, replication is handled by the consensus layer (Raft), not the database. Each node writes to its own local SQLite database. Consistency between nodes is guaranteed by the consensus protocol, not by database replication.

Not a problem for: Blockchain nodes (consensus handles replication).

Problematic for: Traditional web applications that need read replicas.

3. Concurrency Limits

SQLite's maximum practical concurrent readers is around 100-200 on modern hardware. Beyond that, the WAL shared memory index becomes a bottleneck. For a blockchain node's REST API, this is rarely an issue — most consortium nodes serve a handful of API consumers, not thousands of concurrent web users.

Not a problem for: Blockchain API (low-to-medium concurrency), internal services.

Problematic for: High-traffic public websites, thousands of concurrent connections.

4. No Built-in Connection Pooling

SQLite is designed for single-connection or few-connection usage. Opening multiple connections to the same database is supported but doesn't provide the same benefits as PostgreSQL connection pooling. For most blockchain node workloads, a single connection with WAL mode provides sufficient concurrency.


Real-World SQLite Production Usage

If you think SQLite isn't a production database, consider who uses it in production:

PlatformUsageScale
iOSCoreData, app storage1+ billion devices
AndroidApp databases, system services3+ billion devices
ChromeHistory, bookmarks, cookies3+ billion users
FirefoxPlaces (history/bookmarks)200+ million users
macOSMail, Calendar, Spotlight100+ million devices
AdobeLightroom catalogMillions of users
Airbus A350Flight softwareThousands of aircraft
SkypeMessage storageHundreds of millions
WhatsApp (early)Message databaseBillions of messages

SQLite is likely the most deployed database engine in the world, running on more devices than all other databases combined. It's not a toy — it's an embedded database that's been hardened over 20+ years of development.


Performance Benchmarks

For a typical blockchain state workload (read-heavy, write-sequential, key-value pattern with JSON values):

Write Performance

OperationSQLite (WAL)PostgreSQL (local)Notes
Single INSERT0.05ms0.8msNo network overhead
Batch INSERT (100 rows)1.2ms3.5msSQLite transaction batching
UPDATE by primary key0.04ms0.6msIndexed lookup
DELETE by primary key0.03ms0.5ms

Read Performance

Query TypeSQLitePostgreSQL
Point lookup (primary key)0.01ms0.4ms
Range scan (100 rows)0.3ms0.8ms
JSON extraction query0.5ms1.2ms
Aggregation (100K rows)15ms25ms
JOIN (2 tables, 10K rows each)8ms12ms

Benchmarks on: Apple M2, 16GB RAM, NVMe SSD. PostgreSQL 16, local connection via Unix socket. SQLite 3.45 with WAL mode, better-sqlite3 synchronous API. Measurements are median of 1,000 runs.

For a blockchain node's workload pattern (frequent small writes during block commitment, frequent point reads and range scans for API queries, occasional aggregations for dashboards), SQLite performs 2-8x faster than a local PostgreSQL instance due to the elimination of network and IPC overhead.


When to Choose What

ScenarioBest Choice
Blockchain node (embedded)SQLite (WAL mode)
Single-server web app (<100 req/s)SQLite
Single-server web app (>100 req/s)PostgreSQL
Multi-server web appPostgreSQL
Analytics/reporting databasePostgreSQL or ClickHouse
Mobile/desktop appSQLite
Embedded/IoT deviceSQLite
Data warehouse (>100GB)PostgreSQL or specialized
Geographically distributedCockroachDB or YugabyteDB

Key Takeaways

  1. SQLite in WAL mode is production-grade. It powers billions of devices. Handling a blockchain's world state is well within its design envelope.

  2. The single-writer limitation is irrelevant for blockchains. Consensus already serializes writes. The database doesn't need concurrent write capability.

  3. Replication is handled by consensus, not the database. Each node has its own SQLite database. Raft ensures they agree on state.

  4. The simplicity is the point. No separate process. No configuration. Single-file backup. Zero operational overhead.

  5. SQL queryability is built in. Not an afterthought. Not an external index. The state store is the query engine.


Read the SQL queries guide for practical query patterns, or the storage architecture for the full database schema and migration strategy.


About the Author

Prasad Kumkar is the Founder & CEO of ChainScore Labs. Over the last 5+ years, he has worked with teams building exchanges, DeFi infrastructure, smart contracts, tokenization systems, and protocol-level blockchain products, helping founders make architecture, security, and go-live decisions for production Web3 systems.