October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk6 min

Reaching 5,000+ Inserts/sec in SQLite: Thread-Safe Connection Pooling and WAL Mode

Batching, WAL mode, a single writer queue and the right synchronous setting are what get SQLite past 5,000 inserts per second. Here is how they fit together.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For most workloads, 5,000 inserts per second is a modest target for SQLite. What decides whether you hit it is how you batch rows into transactions, which durability setting you choose, and how your threads share connections. It is rarely the pool size. SQLite’s own FAQ says it can handle “50,000 or more” INSERT statements per second on an average desktop. A 2024-11-19 update to that answer says modern SQLite does far more. That is an official statement, not a benchmark of your schema or disk, so treat 5,000/sec as a target to measure rather than a guarantee.

This guide shows a design that gets there safely: one serialized write path, a few reader connections, WAL mode, batched transactions, and a clear view of what each setting costs you.

The short answer: what actually produces the throughput

  1. Batch inserts in transactions. The SQLite FAQ says: “Putting multiple operations inside a single transaction can improve performance dramatically by avoiding the overhead of transaction control after each individual operation.” Committing once per row is the usual reason SQLite looks “really slow.”
  2. Use WAL mode so readers and the writer overlap.
  3. Funnel writes through one connection (or one at a time) and keep each write transaction short.
  4. Pick a synchronous level on purpose, knowing what failure it protects against.

A pool does not make simultaneous writes run in parallel. SQLite allows one writer at a time per database. A pool controls how connections are used and how much they contend.

Threading modes: what “thread-safe” means here

SQLite’s threading documentation (last updated 2023-12-05) describes three modes:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Single-thread: mutexes are disabled. Only safe if the whole process uses SQLite from one thread.
  • Multi-thread: safe across threads as long as no connection, or statement derived from it, is used by two threads at the same time.
  • Serialized: safe to share connections, because access is serialized with mutexes. The documentation states, “The default mode is serialized.”

Practical consequences:

  • Check that your build or language binding has not selected single-thread mode. Many bindings set this for you, so read your library’s documentation.
  • In multi-thread mode, give each worker its own connection. Never share a connection or prepared statement between threads at the same moment.
  • Serialized mode makes sharing safe, but it does not make it fast. Threads sharing one connection take turns.

These are design recommendations based on SQLite’s connection rules. They are not an official prescription for any particular language’s pool library.

Recommended pool design

One writer, several readers

Because WAL still permits only one writer, the simplest dependable design is:

Rank #2
  • A dedicated write connection owned by one thread or task, fed by a queue. Producers push rows; the writer drains the queue and commits in batches.
  • A small read pool of separate connections, one per concurrent reader.

This removes writer-versus-writer lock contention from the hot path. If you must allow several writer connections, route them deliberately, keep their transactions short, and handle SQLITE_BUSY.

Batching sketch (Python)

The pattern below is illustrative. Adapt it to your language and binding.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import sqlite3, queue

def open_db(path):
    con = sqlite3.connect(path, timeout=5.0, isolation_level=None)
    mode = con.execute("PRAGMA journal_mode=WAL").fetchone()[0]
    assert mode.lower() == "wal", mode
    con.execute("PRAGMA synchronous=NORMAL")  # see durability section
    return con

def writer(path, q, batch_size=1000):
    con = open_db(path)
    while True:
        rows = [q.get()]
        while len(rows) < batch_size:
            try: rows.append(q.get_nowait())
            except queue.Empty: break
        con.execute("BEGIN IMMEDIATE")
        con.executemany("INSERT INTO events(ts, payload) VALUES (?, ?)", rows)
        con.execute("COMMIT")

Notes on this sketch: BEGIN IMMEDIATE takes the write lock up front, so a busy condition shows up at the start of the batch rather than midway. The connection timeout acts as a busy timeout. Batch size is a tuning knob, not a magic number: larger batches amortize commit cost but hold the write lock longer and raise latency for each row.

WAL mode: what it gives you and what it doesn’t

Enable it with PRAGMA journal_mode=WAL and confirm the statement returns wal. The setting is persistent in the database file, so you don’t need to repeat it on every open, though checking is cheap.

SQLite’s WAL documentation says: “The second advantage of WAL-mode is that writers do not block readers and readers do not block writers. This is mostly true.” The “mostly” matters. The documentation lists exceptions, including recovery and cleanup situations, where SQLITE_BUSY can still occur. Code every write path to handle it with a busy timeout and a bounded retry.

Checkpoints and WAL growth

  • Automatic checkpoints normally trigger at around 1000 pages of WAL.
  • A long-running reader, or a very large write transaction, can prevent a checkpoint from completing, and the WAL file then keeps growing. Keep read transactions short and don’t leave cursors open.
  • Monitor WAL file size during load tests. A steadily growing WAL means checkpoints aren’t keeping up.

Keep the files together

When copying or moving a live database, keep the WAL file (and its shared-memory file) with the main database file. Separating them can lose committed transactions or corrupt the database. Use SQLite’s backup mechanisms instead of copying files from under a running process.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Durability settings change what “fast” means

From SQLite’s pragma documentation, for WAL mode:

synchronous Behavior in WAL mode Risk
FULL Syncs the WAL on every commit Strongest power-loss durability; slowest commits
NORMAL Database stays consistent A recent committed transaction may be lost after a system crash or power loss
OFF No syncing Added risk of corruption after an OS crash or power loss

NORMAL is a common choice for high-ingest workloads where losing the last few moments of data is tolerable. If each acknowledged insert must survive power loss, use FULL and rely on larger batches to recover throughput. Do not present OFF as a free speedup: it trades integrity protection for speed.

How to measure your own 5,000/sec

No published benchmark matches an arbitrary setup, so measure yours. Record these variables, because each changes the result:

  • Rows and bytes inserted, schema, and number of indexes (every index adds work per row)
  • Single-row versus multi-row statements, and transaction batch size
  • Writer connections and threads; concurrent reader load
  • SQLite version and compile options
  • journal_mode and synchronous settings
  • Storage device and filesystem; cache state; warm-up and run length
  • Whether the rate counts committed rows or attempted statements

Report rows/sec and transactions/sec separately, and look at tail latency, not just the average. Don’t compare an in-memory or unsynced run against a durable on-disk run without labeling the difference. Fast local storage such as an NVMe SSD can help, because commit syncs hit the disk, but the drive alone does not guarantee a target. Batching and durability settings usually matter more.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Check your SQLite version

SQLite documents a WAL-reset bug fixed in 3.51.3 and later, with backports in 3.44.6 and 3.50.7. It needs multiple connections to one WAL database and tightly timed concurrent writes and checkpoints, which is exactly what a multi-connection pool can produce. Verify the library version you actually ship (for example SELECT sqlite_version();), since language runtimes often bundle their own copy.

Troubleshooting

  • Under target, one commit per row: wrap rows in transactions and batch them.
  • Frequent SQLITE_BUSY: too many writer connections, or long transactions. Move to a single writer queue and set a busy timeout.
  • WAL file keeps growing: a long-lived reader is blocking checkpoints, or write transactions are too large.
  • journal_mode did not return wal: the database may be on a filesystem or configuration where WAL is unavailable; check what the pragma returns rather than assuming.
  • Corruption or missing data after a copy: the WAL file was separated from the database.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.