Hands-on sqlite3 vs shelve local KV store lab: real insert/get ops/s plus file sizes, measured on Linux localhost today in this hands-on lab for SREs.
4 min read
Local key-value persistence with sqlite3 vs shelve. This lab reports insert/get ops/s and on-disk bytes on Linux localhost for 5000 JSON-like records.
Related links:
Lab honesty (1 Oct 2026 IST): Python 3.13.5. Affiliates: 0. Differentiates from shelve-vs-pickle-dict (lab 94) and sqlite WAL modes (lab 34) — here the peer is sqlite table KV vs shelve, default sqlite journal.
Verdict up front: sqlite insert ~217559 ops/s vs shelve insert ~13249; get: sqlite ~148945, shelve ~169920. Files: sqlite 532480 B, shelve 606208 B.
| Arm | Pattern |
|---|---|
| sqlite INSERT + commit | TEXT key, JSON TEXT value |
| shelve assign + sync | pickle values via dbm |
| sqlite SELECT by key | parse JSON |
| shelve read by key | native object |
Seven rounds, p50. Temp dir on local disk. Values equal across stores (equal=True).
Script: lab-evidence/128-sqlite3-vs-shelve/results/run_lab.py.
| Arm | ops/s |
|---|---|
| sqlite insert | 217559 |
| shelve insert | 13249 |
| sqlite get | 148945 |
| shelve get | 169920 |
Insert is the headline gap: sqlite led by about 16.4×. Gets were close; shelve edged slightly on this run.
After 5000 keys: sqlite 532480 bytes vs shelve aggregate 606208 bytes. Shelve pays pickle + dbm overhead; sqlite stores compact JSON text in one file.
Document which store owns the file path in the runbook so on-call does not “migrate to shelve” during an incident without measuring inserts.
Shelve insert sat near ~13249 ops/s because each assignment pickles through dbm. Sqlite batched inserts in one transaction hit ~217559. If your workload is read-heavy and already shelve-shaped, the get numbers (~169920 vs ~148945) matter more than insert bragging rights.
Shelve returns native dict-like objects without a JSON decode step — that helps explain get parity (~169920 ops/s vs sqlite ~148945). Insert still paid pickle+dbm. If your values are already JSON strings for a wire format, sqlite avoids a double encode; if values are rich Python graphs, shelve avoids a schema. Measure the operation you actually ship, not only the friendlier API.
sync to sqlite with commit.Evidence: summary.json, summary.txt.
One Linux box, tempfile disk, single writer. Not networked DB, not SQLCipher.
For 5000 local KV writes, sqlite3 insert ~217559 ops/s crushed shelve ~13249; gets were similar. Prefer sqlite for write-heavy local state; keep shelve for small Python-native caches.
Lab evidence
Ran the sqlite3 and shelve benchmark on Linux localhost with Python 3.13.5: 5,000 JSON-like records, seven rounds, p50 timings. sqlite3 insert measured about 217559 ops/s versus shelve about 13249; gets were 148945 versus 169920. Disk totals were 532480 versus 606208 bytes. The write gap surprised me.
Hands-on gc.collect cost empty vs cyclic garbage lab: real collect latency plus reclaim counts, measured on Linux localhost today in this lab for SREs.
Hands-on1 Oct 20264 min read
Hands-on contextlib.ExitStack vs nested with and manual close: real cycles/s for N resources, measured on Linux localhost in this hands-on lab for SREs.
Hands-on1 Oct 20263 min read
Hands-on itertools.batched vs manual list-slice chunking: real items/s batching sequences, measured on Linux localhost today in this hands-on lab for SREs.
Hands-on1 Oct 20263 min read