SQLite vs PostgreSQL vs MySQL

Use SQLite when one process owns the data and writes are modest. Use PostgreSQL when concurrent writers, complex queries or extensions like pgvector matter, and you can afford to run it. Use MySQL or MariaDB when replication and online schema changes on huge tables matter more than SQL expressiveness. DuckDB is for analytics, not serving.

Last verified
2026-07-01 (22d ago)
Source confidence
62%
Re-verified
every 30 days
Tools
5
Fields
26
5 tools · verified 22d ago
DuckDBIn-process columnar analytics engine — SQLite's shape, a warehouse's execution model.

Engine & SQL

Embedded or server
Embedded (in-process)
Concurrency model
MVCC (single process)
Write concurrency
Single writer
Isolation levels
Snapshot
JSON support
Text functions only
Full-text search
Extension
Vector search
Extension
Type strictness
Static, strict
ALTER TABLE
Moderate
Max database size

Operations

Replication
None
PITR
No
Online schema change
No
Connection model
In-process (no connections)
Default max conns
Managed options
MotherDuck
Ops burden
1

Ecosystem

Extension ecosystem
4
Client libraries
3
Migration tooling
2
GitHub stars
33,000
Positioning
DuckDB is a fast in-process analytical database.

Licence & governance

Licence
MIT
Embed in closed source
Yes
Governance
Foundation

History

First release
2019-06-26
MariaDBGPL-only MySQL fork under a foundation, with Galera clustering and native vectors.

Engine & SQL

Embedded or server
Both
Concurrency model
MVCC (undo log)
Write concurrency
Concurrent writers (row-level)
Isolation levels
Read uncommittedRead committedRepeatable readSerializable
JSON support
Text functions only
Full-text search
Built-in
Vector search
Native type + ANN index
Type strictness
Static, coercing
ALTER TABLE
Moderate
Max database size
64 TB

Operations

Replication
Built-in async + synchronous
PITR
Yes
Online schema change
Yes
Connection model
Thread per connection
Default max conns
151
Managed options
Amazon RDSGoogle Cloud SQLMariaDB SkySQL
Ops burden
3

Ecosystem

Extension ecosystem
3
Client libraries
4
Migration tooling
3
GitHub stars
6,000
Positioning
The open source relational database, made by the original developers of MySQL.

Licence & governance

Licence
GPL-2.0
Embed in closed source
Partial
Governance
Foundation

History

First release
2009-10-29
MySQLThe relational server that ran the early web, now Oracle-stewarded and InnoDB-based.

Engine & SQL

Embedded or server
Server (client/server)
Concurrency model
MVCC (undo log)
Write concurrency
Concurrent writers (row-level)
Isolation levels
Read uncommittedRead committedRepeatable readSerializable
JSON support
Binary JSON type
Full-text search
Built-in
Vector search
Native type
Type strictness
Static, coercing
ALTER TABLE
Moderate
Max database size
64 TB

Operations

Replication
Built-in async + synchronous
PITR
Yes
Online schema change
Yes
Connection model
Thread per connection
Default max conns
151
Managed options
Amazon RDSAmazon AuroraGoogle Cloud SQLAzure Database+1
Ops burden
3

Ecosystem

Extension ecosystem
2
Client libraries
5
Migration tooling
4
GitHub stars
11,000
Positioning
The world's most popular open source database.

Licence & governance

Licence
GPL-2.0 or commercial
Embed in closed source
No
Governance
Single vendor

History

First release
1995-05-23
PostgreSQLMVCC relational server with the widest SQL surface and a deep extension ecosystem.

Engine & SQL

Embedded or server
Server (client/server)
Concurrency model
MVCC (heap row versions)
Write concurrency
Concurrent writers (row-level)
Isolation levels
Read committedRepeatable readSerializable
JSON support
Binary JSONB + indexes
Full-text search
Built-in
Vector search
Extension
Type strictness
Static, strict
ALTER TABLE
Minimal
Max database size

Operations

Replication
Built-in async + synchronous
PITR
Yes
Online schema change
Partial
Connection model
Process per connection
Default max conns
100
Managed options
NeonSupabaseAmazon RDSAmazon Aurora+3
Ops burden
4

Ecosystem

Extension ecosystem
5
Client libraries
5
Migration tooling
5
GitHub stars
18,000
Positioning
The world's most advanced open source relational database.

Licence & governance

Licence
PostgreSQL Licence (BSD-style)
Embed in closed source
Yes
Governance
Community project

History

First release
1997-01-29
SQLiteEmbedded SQL engine that runs inside your process — a file, not a server.

Engine & SQL

Embedded or server
Embedded (in-process)
Concurrency model
Reader/writer locking
Write concurrency
Single writer
Isolation levels
Serializable
JSON support
Text functions only
Full-text search
Extension
Vector search
Extension
Type strictness
Dynamic (per-value)
ALTER TABLE
Severe (rebuild required)
Max database size
256 TB

Operations

Replication
Third-party only
PITR
Partial
Online schema change
No
Connection model
In-process (no connections)
Default max conns
Managed options
TursoCloudflare D1Fly.io LiteFS
Ops burden
1

Ecosystem

Extension ecosystem
4
Client libraries
5
Migration tooling
3
GitHub stars
8,000
Positioning
Small. Fast. Reliable. Choose any three.

Licence & governance

Licence
Public domain
Embed in closed source
Yes
Governance
Independent team

History

First release
2000-08-17
yespartialnounknown◆ measured · ▸ vendor-claimed · ▪ community · · inferredExpand a row for the source and date behind every cell.

Should I use SQLite, PostgreSQL or MySQL?

This page has no score column, and it never will. Every other comparison on toolweight ranks vendors selling roughly the same thing at different prices. These five are engines with different shapes: SQLite is a file format with a query planner attached, PostgreSQL is a process-per-connection MVCC server, MySQL and MariaDB are thread-per-connection MVCC servers tuned for replication, and DuckDB is a columnar analytics engine that happens to speak SQL. Weighting them against each other would produce a number that says more about the weights than the engines. So instead the table is descriptive, and the decision lives in prose.

PostgreSQL is the correct default for most teams, and the reason is boring: it has the widest SQL surface, the best extension ecosystem, and the most people who already know it. pgvector made it the default vector store too, which killed a whole category of standalone databases. What the marketing never mentions is the operational tax. Postgres is the heaviest engine here to run — that is the killer field on this page. Autovacuum tuning, transaction-ID wraparound, bloat, a process per connection so PgBouncer stops being optional past a few hundred clients, and major-version upgrades that mean pg_upgrade or a logical-replication cutover. Managed Postgres exists precisely because self-hosting it well is a job.

SQLite is the most under-used engine here and the most over-claimed by the people who love it. The claims are true: WAL mode gives many concurrent readers, and one machine with an NVMe disk serves an astonishing amount of traffic with zero network hops. The constraints get skipped: one writer at a time, no built-in replication, a twelve-step table rebuild for most schema changes beyond ADD COLUMN, and a backup story that means adopting Litestream or Turso. That is a straight trade, not a warning. One process, short writes, and you are buying a great deal of simplicity.

MySQL and MariaDB are still the right answer more often than the Postgres-shaped internet admits, for one specific reason: changing the schema of a very large table without taking writes offline. Online DDL, INSTANT ADD COLUMN, and a decade of gh-ost and pt-online-schema-change in production make that routine in a way it is not on Postgres. Thread-per-connection also forgives a badly behaved pool. Against that: a narrower SQL surface, a thin extension ecosystem, and a licence question. MySQL is GPLv2-or-commercial under Oracle, MariaDB is GPLv2 under a foundation. Shipping the server inside a product makes that difference the whole decision; running a website makes it noise.

The tree, then. Does more than one process write to this data concurrently? If no, and the working set fits on one disk, use SQLite and turn on WAL mode. If yes: does a meaningful share of your query load look like aggregation over columns rather than fetching rows by key? If so, that half of the workload belongs in DuckDB or a warehouse, not in your OLTP engine, and the remaining half is a smaller problem. Then: do you need extensions — pgvector, PostGIS, TimescaleDB — or window-heavy, CTE-heavy SQL? If yes, PostgreSQL, and budget for the operations. If no, and your actual fear is altering a 500 GB table without downtime, MySQL or MariaDB. If none of that discriminates, it is a genuine tie, and a tie means pick the engine somebody on the team has already debugged at three in the morning.

Axis 1 — deployment shape

The first question is not which SQL dialect you prefer, it is whether the database is a library or a server. SQLite and DuckDB run inside your process: no daemon, no port, no credentials to rotate, no network hop, no connection pool, and no second thing to monitor. A query is a function call. That collapses an entire layer of your architecture, and the cost is that the database's availability is now your process's availability, and adding a read replica means shipping the file — Litestream, LiteFS, Turso — rather than pointing a replica at a primary.

Server engines charge you a daemon and pay you back with a network boundary. Several app instances, an analyst with psql, a read replica in another region, a bastion host, a migration that runs from CI rather than from whichever container won the race. The detail that catches teams out is the connection model. PostgreSQL forks a process per connection, so a thousand idle Lambda connections is a memory problem and PgBouncer stops being optional; MySQL and MariaDB use a thread per connection and are far more forgiving of a badly configured pool. If your compute is serverless and bursty, that difference matters more than any feature in the table.

Axis 2 — concurrency profile

SQLite in WAL mode gives you many concurrent readers and exactly one writer. Writes are serialised, so the arithmetic is simple: write transaction duration multiplied by writes per second must stay comfortably below one. Short transactions at a few hundred writes per second are fine on commodity hardware. Long transactions are what kill you, and holding a write transaction open across a network call is the classic way to turn a healthy app into a locked one.

MVCC engines let writers proceed in parallel by keeping old row versions, and then hand you the bill for those versions. On PostgreSQL it arrives as autovacuum, table and index bloat, and transaction-ID wraparound warnings. On InnoDB it arrives as undo-log growth and a history list length that climbs when a long-running read transaction refuses to end. Neither is hard once you know it exists; both are surprises the first time.

One concurrency detail deserves more attention than it gets: the default isolation levels differ. PostgreSQL defaults to READ COMMITTED, MySQL and MariaDB default to REPEATABLE READ, SQLite is effectively serialisable, and DuckDB gives snapshot isolation. Application code written against one engine's default can change behaviour silently on another — read-modify-write races that were impossible become possible, or vice versa. Port the isolation assumptions, not just the SQL.

Axis 3 — query complexity

PostgreSQL has the widest SQL surface here by a distance: lateral joins, materialised and non-materialised CTEs, partial and expression indexes, generated columns, exclusion constraints, window functions that keep gaining features, JSONB with GIN indexes, and an extension mechanism that turned it into a geospatial database, a time-series database and a vector database without forking. If your queries are analytical in shape but transactional in access pattern, this is the engine that will not run out of room.

MySQL and MariaDB trade surface area for a very fast primary-key path and predictable clustered-index behaviour. Most application queries are exactly that shape, which is why so much of the web runs on them. You feel the ceiling when the reporting queries arrive and someone starts writing five-way joins with correlated subqueries.

SQLite's planner is small and honest about it. It handles ordinary plans well, does less well on many-way joins, and has no parallelism — one query, one core. DuckDB inverts every one of these assumptions: vectorised columnar execution, multi-core by default, reads Parquet, CSV, S3 and live PostgreSQL tables directly, and will aggregate a billion rows on a laptop while your OLTP engine is still planning. The right architecture for many teams is not choosing between them but running two: rows in Postgres or SQLite, columns in DuckDB, over the same object storage.

What this page deliberately leaves out

There is no pricing group, because these engines are free and the money is entirely in who runs them. The comparison people need next — Neon against Supabase against RDS against Aurora, or PlanetScale and Vitess against managed MySQL — is a different page with different fields: cold start, branching, connection pooling, egress, backup retention, region coverage. Hosted-Postgres and hosted-MySQL are planned as their own categories for exactly that reason, and they will have score columns, because those are vendors selling the same thing at different prices.

Until then, the adjacent pages are the useful ones. Convex alternatives covers the layer above this decision, where the database, the API and the sync engine ship together and the engine choice is made for you. S3-compatible storage covers where the blobs go — the other half of every schema on this page, and the substrate DuckDB reads from. LLM APIs covers the retrieval side of the pgvector column, since the embedding model constrains dimensionality and therefore the index you can afford.

What is sqlite vs postgresql vs mysql vs mariadb vs duckdb?

SQLite vs PostgreSQL vs MySQL vs MariaDB vs DuckDB

On toolweight, SQLite vs PostgreSQL vs MySQL vs MariaDB vs DuckDB means the 5 tools benchmarked on this page — SQLite, PostgreSQL, MySQL, MariaDB, DuckDB — judged on the same 26 fields, from the same sources, on the same date. The question it exists to answer: Should I use SQLite, PostgreSQL or MySQL?

How does toolweight compare these?

Every cell here comes from the engine's own reference manual rather than a vendor pricing page, which is why this is the best-sourced page on toolweight: these projects document their limits precisely and change them slowly. Where a claim is a judgement rather than a documented fact — extension ecosystem, migration tooling, client-library maturity, and the operational burden score — it is marked inferred and the ladder is spelled out in the column help so you can disagree with the placement rather than the number. Where a project genuinely documents no limit, the cell is null and renders as an em dash instead of a fabricated ceiling.

The killer field is operational burden, scored 1–5 where lower is better. It measures what a competent team must do to keep the engine healthy in production, not how hard it is to install: 1 is a file your process opens, 5 is a cluster with a full-time owner. It is the field that inverts the usual ranking — PostgreSQL has the strongest feature set on this page and the highest burden, and pretending otherwise is how teams end up with a vacuum problem eighteen months in. One finding is visible only by absence: SQLite appears nowhere in the timeline below. It ships several releases a year, all backwards compatible, and none of them change a decision. That stability is a feature and it does not generate news.

Full methodology and sourcing policy →

Frequently asked questions

Is SQLite production-ready for a real web application?

Yes, with conditions. Turn on WAL mode, set a busy timeout, keep write transactions short, and adopt a replication or backup tool such as Litestream on day one rather than after the first disk failure. The limits that bite are single-writer serialisation and the lack of built-in replication — not throughput, correctness or size. SQLite is more tested than almost any code you will ship alongside it.

When does SQLite's single writer actually become a problem?

Multiply your write transaction duration by your write rate. Five-millisecond transactions at 200 writes per second use about a second of writer time per second of wall clock, and you are already near the wall. Long transactions are the real killer: one 300 ms write blocks every other writer for 300 ms. If you cannot bound transaction duration, you want an MVCC engine.

MySQL or MariaDB in 2026?

For a normal application either works and the drivers are largely interchangeable. Choose MariaDB if you want a foundation rather than Oracle, GPLv2 without a commercial upsell, native vector search in the community server, or Galera clustering in the box. Choose MySQL if your managed host, ORM or replication tooling assumes it, or if you need Oracle's commercial licence to embed the server in a product.

Do I need a dedicated vector database, or is pgvector enough?

For almost everyone pgvector is enough. It gives you HNSW and IVFFlat indexes inside the same transaction as your rows, so embeddings cannot drift out of sync with the records they describe. Reach for something specialised only when index build time, memory-resident recall at very high dimensionality, or billion-scale corpora become the bottleneck — which is later than most teams assume.

Can DuckDB be my application database?

No, and it does not try to be. DuckDB is an in-process columnar engine built for scans and aggregation, with a single-process write path and no replication or point-in-time recovery. It is superb as the analytics half of a system — querying Parquet on object storage, or reading straight from your Postgres — and a poor fit for many clients writing single rows.

Is PostgreSQL always the safe default?

It is the safest default, not a free one. You get the widest SQL surface, transactional DDL and the deepest extension ecosystem; you also get autovacuum, bloat, transaction-ID wraparound and a process per connection. If nobody on the team will own those, use managed Postgres or pick a lighter engine deliberately rather than discovering the burden during an incident.

How painful are schema migrations on each engine?

MySQL and MariaDB are best: online DDL plus gh-ost and pt-online-schema-change make altering a huge table routine. PostgreSQL runs DDL inside a transaction, which is a genuine advantage, but type changes rewrite the table under an exclusive lock, so teams reach for pgroll or pg_repack. SQLite is worst: most changes require the documented twelve-step table rebuild, though modern tooling automates it.