PostgreSQL vs MySQL vs SQLite in 2026: Benchmarks from 1K to 10M Rows
Real performance data, hosting costs, and honest recommendations for every scale
The "which database should I use?" question has gotten more interesting in 2026. PostgreSQL remains the default for serious applications. MySQL still powers a massive chunk of the internet. But SQLite has quietly become a legitimate production option — thanks to Turso, Litestream, Cloudflare D1, and the broader movement toward embedded databases at the edge.
We ran TPC-B inspired benchmarks across all three databases from 1,000 to 10 million rows on identical hardware. The results challenge some long-held assumptions about when each database shines — and when it falls apart.
Architecture: Three Very Different Approaches
PostgreSQL: The Feature Powerhouse
PostgreSQL uses Multi-Version Concurrency Control (MVCC) to handle concurrent reads and writes without locking. Its extension ecosystem is unmatched — PostGIS for geospatial, pgvector for embeddings, TimescaleDB for time-series. It supports advanced types like arrays, hstore, and JSONB natively.
The tradeoff: MVCC creates dead tuples that require regular vacuuming, and the process model (one OS process per connection) means connection pooling is essential at scale.
MySQL: The Battle-Tested Workhorse
MySQL's InnoDB engine provides ACID compliance with a clustered index architecture that makes primary key lookups extremely fast. Its replication story is mature — group replication, InnoDB Cluster, and read replicas are production-proven at companies like GitHub and Shopify.
MySQL 9.x has closed many gaps with PostgreSQL, including improved CTEs, window functions, and JSON support. But it still lacks PostgreSQL's extension model and advanced type system.
SQLite: The Embedded Contender
SQLite runs in-process with zero network overhead. WAL (Write-Ahead Logging) mode enables concurrent reads alongside a single writer. It stores everything in a single file, making backups trivial and deployments simple.
The 2025-2026 ecosystem shift is significant: Turso provides distributed SQLite with multi-region replication, Cloudflare D1 puts SQLite at the edge, and Litestream streams WAL changes to S3 for durable backups. SQLite is no longer just for mobile apps.
Benchmark Methodology
Test Environment
- Hardware: Dedicated bare-metal server — AMD EPYC 7763 (8 cores allocated), 32 GB RAM, NVMe SSD (Samsung PM9A3)
- PostgreSQL: 16.2 with default tuning + shared_buffers=8GB, effective_cache_size=24GB
- MySQL: 9.1 InnoDB with innodb_buffer_pool_size=8GB
- SQLite: 3.47.0, WAL mode, mmap_size=8GB, cache_size=-8000000
- Schema: Single table — id (PK), user_id (indexed), email (indexed), payload (JSON), created_at (indexed)
- Concurrency: 8 client threads for client-server DBs, single-process for SQLite (reflects real usage)
All benchmarks were run 5 times with the median reported. Caches were warmed before measurement. We tested at four dataset sizes: 1K, 100K, 1M, and 10M rows to capture how each database degrades (or doesn't) as data grows.
Read Performance: Queries per Second
Point lookups by indexed column (user_id), averaged across 60-second sustained runs.
| Dataset Size | PostgreSQL | MySQL | SQLite |
|---|---|---|---|
| 1K rows | 48,200 q/s | 52,100 q/s | 128,400 q/s |
| 100K rows | 45,800 q/s | 49,600 q/s | 115,200 q/s |
| 1M rows | 41,300 q/s | 44,700 q/s | 94,600 q/s |
| 10M rows | 38,100 q/s | 36,900 q/s | 31,200 q/s |
Analysis: SQLite dominates read performance at small to medium datasets because there is no network round-trip and no protocol overhead. Every query is a function call in the same process. However, at 10M rows, PostgreSQL's query planner and buffer management start to pay off — SQLite's simpler planner makes less optimal decisions with large datasets and complex indexes.
MySQL's clustered index gives it a slight edge over PostgreSQL for simple point lookups at smaller sizes, but PostgreSQL overtakes it at 10M rows where its more sophisticated statistics and planner shine.
Range Queries (BETWEEN on indexed timestamp, returning 100 rows)
At 10M rows: PostgreSQL delivered 12,400 q/s, MySQL hit 10,800 q/s, and SQLite managed 8,900 q/s. PostgreSQL's MVCC and index-only scans give it a clear advantage for analytical range queries at scale.
Write Performance
Three workload types tested at the 1M row baseline.
| Workload | PostgreSQL | MySQL | SQLite |
|---|---|---|---|
| Single INSERT | 14,200/s | 16,800/s | 11,400/s |
| Bulk INSERT (1K batch) | 185,000/s | 142,000/s | 98,000/s |
| 8 concurrent writers | 52,600/s | 48,100/s | 11,400/s |
| UPDATE (indexed lookup) | 12,800/s | 14,200/s | 10,600/s |
| Mixed R/W (80/20) | 38,400/s | 35,200/s | 24,800/s |
Analysis: SQLite's single-writer limitation is real. With 8 concurrent writers, it cannot parallelize — writes are serialized through a single mutex. PostgreSQL and MySQL handle concurrent writes gracefully because they were designed for it.
MySQL's InnoDB is slightly faster for single-row operations due to its change buffer and efficient clustered index updates. PostgreSQL wins bulk inserts thanks to its COPY command, which bypasses the SQL parser entirely.
The SQLite Write Ceiling
SQLite's 11,400 writes/sec single-threaded ceiling sounds limiting, but do the math: that is 985 million writes per day. Most applications never come close. The real limitation is concurrent writers — if your app needs multiple processes writing simultaneously, SQLite requires careful architecture (queue-based writes, or use Turso's managed replication).
JSON Performance Comparison
All three databases now support JSON querying, but the implementations differ significantly. We tested extracting a nested field from a JSON column and filtering on it, across 1M rows where each row contains a ~500-byte JSON payload.
| Operation | PostgreSQL (JSONB) | MySQL (JSON) | SQLite (JSON) |
|---|---|---|---|
| Extract field | 28,400 q/s | 18,200 q/s | 22,100 q/s |
| Filter on nested key | 9,800 q/s | 4,100 q/s | 6,400 q/s |
| JSON aggregation | 3,200 q/s | 1,400 q/s | 2,100 q/s |
| GIN-indexed lookup | 42,600 q/s | 12,800 q/s (multi-valued idx) | N/A |
Analysis: PostgreSQL's JSONB is in a different league. The binary storage format means it doesn't need to re-parse JSON on every access. GIN indexes on JSONB paths make filtered queries nearly as fast as regular indexed lookups. MySQL's multi-valued indexes (added in 8.0) help but still can't match PostgreSQL's flexibility. SQLite's JSON support is functional but lacks indexing options — you can create generated columns as a workaround, but it's not as elegant.
Hosting Costs: Managed Options Compared
| Provider | Database | Free Tier | Small (2 vCPU / 4 GB) | Production (4 vCPU / 16 GB) |
|---|---|---|---|---|
| Supabase | PostgreSQL | 500 MB | $25/mo | $100/mo |
| Neon | PostgreSQL | 512 MB (scales to zero) | $19/mo | $69/mo + compute |
| AWS RDS | PostgreSQL | 750 hrs db.t3.micro | $52/mo | $210/mo |
| PlanetScale | MySQL | 5 GB (1 branch) | $39/mo | $99/mo |
| AWS RDS | MySQL | 750 hrs db.t3.micro | $48/mo | $195/mo |
| Turso | SQLite | 9 GB, 500 DBs | $29/mo | $79/mo |
| Cloudflare D1 | SQLite | 5 GB, 5M reads/day | $5/mo (Workers Paid) | $5/mo + usage |
| Self-hosted SQLite | SQLite + Litestream | N/A | $6/mo (VPS) | $24/mo (VPS) |
The cost story is clear: SQLite options are dramatically cheaper. Cloudflare D1 at $5/month with a generous free tier is hard to beat for edge-first applications. Self-hosted SQLite with Litestream on a $6/month VPS gives you a production-grade database with continuous backups to S3 — for less than a coffee.
For PostgreSQL, Neon's serverless model is compelling if your traffic is spiky — you pay for compute only when queries are running. Supabase bundles auth, storage, and edge functions into the price, making the $25/month reasonable for full-stack apps.
Feature Comparison
| Feature | PostgreSQL | MySQL | SQLite |
|---|---|---|---|
| ACID Compliance | Full | Full (InnoDB) | Full (WAL mode) |
| Full-Text Search | Built-in (tsvector) | Built-in (InnoDB FTS) | FTS5 extension |
| Replication | Streaming + logical | Group replication, GTID | Litestream, Turso (LiteFS) |
| Extensions | Rich ecosystem (pgvector, PostGIS, etc.) | Plugins (limited) | Loadable extensions |
| JSON Support | JSONB (binary, indexed) | JSON (text, multi-valued idx) | JSON (text, generated cols) |
| Max DB Size | Unlimited (practical) | Unlimited (practical) | 281 TB (theoretical) |
| Concurrent Writers | Thousands | Thousands | One (serialized) |
| Connection Overhead | ~1.5 MB per connection | ~1 MB per thread | Zero (in-process) |
| Backup | pg_dump, pg_basebackup | mysqldump, xtrabackup | cp (file copy), .backup API |
Developer Experience
ORMs and Tooling
All three databases have excellent ORM support in 2026. Prisma, Drizzle, and SQLAlchemy support all three. TypeORM and Sequelize cover PostgreSQL and MySQL. Drizzle ORM has emerged as the developer favorite for TypeScript projects — its type-safe query builder works identically across all three databases.
Migration Tooling
PostgreSQL and MySQL have mature migration ecosystems — Flyway, Liquibase, Prisma Migrate, and Drizzle Kit all work well. SQLite migrations are trickier because ALTER TABLE support is limited (you still cannot drop or rename columns inline in all cases). Drizzle Kit handles this by recreating tables behind the scenes, but it's worth knowing the limitation.
Local Development
This is where SQLite shines brightest. No Docker container, no service to start, no port conflicts. Your database is a file. For local development and testing, SQLite offers the fastest feedback loop. Many teams use SQLite locally and PostgreSQL in production — Drizzle and Prisma make this cross-database workflow practical.
// drizzle.config.ts — same schema, different drivers
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/db/schema.ts',
driver: process.env.NODE_ENV === 'production' ? 'pg' : 'better-sqlite3',
dbCredentials: process.env.NODE_ENV === 'production'
? { connectionString: process.env.DATABASE_URL! }
: { url: './local.db' },
}); The SQLite Revolution: When Embedded Beats Client-Server
The most surprising finding from our benchmarks is not that SQLite is fast for small datasets — everyone knows that. It is that SQLite remains competitive up to 1 million rows for read-heavy workloads, while costing a fraction of a managed PostgreSQL or MySQL instance.
The 2026 SQLite ecosystem has solved the three historical objections:
- "SQLite can't replicate" — Turso provides multi-region replication with embedded replicas. Litestream continuously streams WAL changes to S3, GCS, or Azure Blob Storage. LiteFS enables read replicas across Fly.io nodes.
- "SQLite can't handle concurrent writes" — True, but BEGIN CONCURRENT (in development) will allow multiple writers. Until then, Turso's architecture handles write distribution. And for most web apps, a single writer at 11K writes/sec is more than enough.
- "SQLite isn't production-grade" — SQLite is the most deployed database in the world. Every iPhone, Android device, and web browser uses it. Cloudflare D1 runs millions of SQLite databases in production. The tooling has caught up to the engine's reliability.
The use cases where embedded SQLite wins are expanding rapidly:
- Edge computing: Cloudflare D1 puts your database within 50ms of every user on Earth
- Single-tenant SaaS: One SQLite database per customer eliminates noisy neighbor problems
- Read-heavy APIs: Embedded reads with zero network latency beat any managed database
- Serverless functions: No connection pool to manage, no cold-start connection penalty
Final Verdict: Recommendations by Use Case
Choose PostgreSQL When:
- Building a SaaS with complex queries — Joins, CTEs, window functions, and advanced aggregations are PostgreSQL's strength
- You need JSONB — No other database matches PostgreSQL's semi-structured data performance
- Analytics workloads — Range queries, partitioning, and parallel query execution at 10M+ rows
- Extension-heavy use cases — pgvector for AI embeddings, PostGIS for geospatial, TimescaleDB for time-series
- High write concurrency — Multiple services writing to the same database simultaneously
Best managed options: Neon (serverless), Supabase (full-stack), AWS RDS (enterprise)
Choose MySQL When:
- You need proven replication at scale — MySQL's replication ecosystem is battle-tested at GitHub, Shopify, and Meta
- Primary key lookup-heavy workloads — InnoDB's clustered index makes these queries exceptionally fast
- WordPress, Laravel, or PHP ecosystems — Decades of optimization and tooling
- Team familiarity — If your team knows MySQL deeply, the productivity advantage is real
Best managed options: PlanetScale (branching workflow), AWS RDS (reliability)
Choose SQLite When:
- Edge computing — Cloudflare D1 or Turso embedded replicas put data next to your users
- Mobile app backends under 1M rows — Zero-latency reads beat any network database
- Single-tenant architecture — One database file per customer is simple, isolated, and cheap
- Budget-constrained projects — $5-6/month for a production database is unbeatable
- Local-first applications — Sync engines like ElectricSQL and PowerSync build on SQLite
Best managed options: Turso (multi-region), Cloudflare D1 (edge-native), Litestream + VPS (self-hosted)
The Bottom Line
PostgreSQL is the best general-purpose database in 2026. If you are unsure, pick PostgreSQL. But the real story this year is that SQLite has earned a seat at the production table. For read-heavy workloads under 1M rows, edge deployments, and cost-sensitive projects, SQLite is not just viable — it is often the best choice. MySQL remains excellent for what it does, but its niche is narrowing as PostgreSQL gains features and SQLite gains infrastructure.