ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
About

Plate 28

  1. Blog
  2. /Observability & SRE

sqlite3 vs shelve Local KV: Localhost Lab

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.

Aditya Challa·1 October 2026·4 min read

Hands-on
On this page
  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table (p50 ops/s)
  5. Disk footprint
  6. Reading it for SRE work
  7. Write amplification note
  8. API shape vs speed
  9. Pitfalls
  10. Reproduce
  11. Limits
  12. Takeaway

Intro — what this post promises

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:

  • fractions vs float localhost lab
  • math fsum vs sum localhost lab
  • itertools batched vs chunk localhost lab
  • exitstack vs nested with localhost lab
  • xml etree vs json localhost lab
  • logging formatter vs fstring localhost lab
  • copy copy vs dict copy localhost lab
  • html parser vs regex localhost lab

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.


Arms

ArmPattern
sqlite INSERT + commitTEXT key, JSON TEXT value
shelve assign + syncpickle values via dbm
sqlite SELECT by keyparse JSON
shelve read by keynative object

Seven rounds, p50. Temp dir on local disk. Values equal across stores (equal=True).


Lab topology

n=5000 keys · 7 rounds · p50
metric: ops/s = n / p50_s

Script: lab-evidence/128-sqlite3-vs-shelve/results/run_lab.py.


Lead table (p50 ops/s)

Armops/s
sqlite insert217559
shelve insert13249
sqlite get148945
shelve get169920

Insert is the headline gap: sqlite led by about 16.4×. Gets were close; shelve edged slightly on this run.


Disk footprint

After 5000 keys: sqlite 532480 bytes vs shelve aggregate 606208 bytes. Shelve pays pickle + dbm overhead; sqlite stores compact JSON text in one file.


Reading it for SRE work

  • Bulk load / agent checkpoint writes → sqlite3 (insert throughput).
  • Occasional Python-object cache on disk → shelve can be fine if write volume is low.
  • Need SQL, indexes, concurrent readers → sqlite wins on features, not just speed.
  • Lab 94 compared shelve to an in-process pickle dict; lab 34 compared WAL — this post is cross-API KV.

Document which store owns the file path in the runbook so on-call does not “migrate to shelve” during an incident without measuring inserts.


Write amplification note

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.



API shape vs speed

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.


Pitfalls

  • Comparing shelve without sync to sqlite with commit.
  • Assuming shelve is multi-process safe (it is not).
  • Ignoring JSON encode cost on the sqlite path (included here on purpose).
  • Treating lab 34 WAL timings as interchangeable with this default-journal run.

Reproduce

python3 lab-evidence/128-sqlite3-vs-shelve/results/run_lab.py

Evidence: summary.json, summary.txt.


Limits

One Linux box, tempfile disk, single writer. Not networked DB, not SQLCipher.


Takeaway

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.

sqlite3shelvepythonkey-valueperformancebenchmarkdisk-storagepersistence

Lab evidence

What I found running this

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.

Notes when a lab post goes up

Occasional email for new hands-on reviews. No sequence and no sponsors.

Related links

  • Plate 68

    gc.collect Cost Empty vs Cycles: Localhost Lab

    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.

    Observability & SRE · 1 Oct 2026

  • Plate 16

    ExitStack vs Nested with Resources: Localhost Lab

    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.

    Observability & SRE · 1 Oct 2026

  • Plate 40

    itertools.batched vs Manual Chunking: Localhost Lab

    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.

    Observability & SRE · 1 Oct 2026

On this page

  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table (p50 ops/s)
  5. Disk footprint
  6. Reading it for SRE work
  7. Write amplification note
  8. API shape vs speed
  9. Pitfalls
  10. Reproduce
  11. Limits
  12. Takeaway
All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove