7 ms·
Many Small Queries Are Efficient in SQLite
- rustybolt 10mo agoThis feels like a very elaborate way of saying that doing O(N) work is not a problem, but doing O(N) network calls is.
- jstummbillig 10mo agoIt being so obvious, why is sqlite not the de facto standard?
- skrebbel 10mo agoI haven't investigated this so I might be behind the times, but last I checked remotely managing an SQLite database, or having some sort of dashboarding tool run management reporting queries and the likes, or make a Retool app for it, was very messy. The benefit of not being networked becomes a downside. Maybe this has been solved though? Anybody here running a serious backend-heavy app with SQLite in production and can share? How do you remotely edit data, do analytics queries etc on production data?
- Sammi 10mo agoMy best answer so far is ssh and sqlite3 cli.
- chuckadams 10mo agoNo network, no write concurrency, no types to speak of... Where those things aren't needed, sqlite is the de facto standard. It's everywhere.
- mickeyp 10mo agoPerfect summary. I'll add: insane defaults that'll catch you unaware if you're not careful! Like foreign keys being opt-in; sure, it'll create 'em, but it won't enforce them by default!
- ogogmad 10mo agoIs it possible to fix some of these limitations by building DBMSes on top of SQLite, which might fix the sloppiness around types and foreign keys?
- Polizeiposaune 10mo agoUsing the API with discipline goes a long way. Always send "pragma foreign_keys=on" first thing after opening the db. Some of the types sloppiness can be worked around by declaring tables to be STRICT. You can also add CHECK constraints that a column value is consistent with the underlying representation of the type -- for instance, if you're storing ip addresses in a column of type BLOB, you can add a CHECK that the blob is either 4 or 16 bytes.
- BenjiWiebe 10mo agoSQLite did add 'STRICT' tables for type enforcement. Still doesn't have a huge variety of types though.
- mikeocool 10mo agoThe fact that they didn’t make STRICT default is really a shame. I understand maintaining backwards compatibility, but the non-strict behavior is just so insane I have a hard time imagine it doesn’t bite most developers who use SQLite at some point.
- sethops1 10mo agoNearly every default setting in sqlite is "wrong" from the outset, for typical use cases. I'm surprised packages that offer a sane configuration out of the box aren't more popular.
- sgbeal 10mo ago> The fact that they didn’t make STRICT default is really a shame. SQLite makes strong backwards-compatibility guarantees. How many apps would be broken if an Android update suddenly defaulted its internal copy of SQLite to STRICT? Or if it decided to turn on foreign keys by default? Those are rhetorical questions. Any non-0 percentage of affected applications adds up to a big number for software with SQLite's footprint. Software pulling the proverbial rug out from under downstream developers by making incompatible changes is one of the unfortunate evils of software development, but the SQLite project makes every effort to ensure that SQLite doesn't do any rug-tugging.
- andersmurphy 10mo agoI mean it has blob types. Which basically means you can implement any type you want. You can also trivially implement custom application functions to work on these blob types in your queries. [1] - [1] https://sqlite.org/appfunc.html https://sqlite.org/appfunc.html
- Cthulhu_ 10mo agoIt is for use cases like local application storage, but it doesn't do well in (or isn't designed for) concurrent use cases like any networked services. SQLite is not like the other databases.
- dahart 10mo agoPartly for the same reason it’s fast for small sites. In their words: “SQLite is not client/server”
- jerf 10mo agoIsn't SQLite a de facto standard? Seems like it to me. If I want an embedded SQL engine, it is the "nobody got fired for selecting" choice. A competitor needs to offer something very compelling to unseat it.
- jstummbillig 10mo agoI mean as in: Most web stacks do not default to sqlite over MySQL or postgres. Why not? Best default for most users, apparently.
- conradkay 10mo agoI think in the past it was more obvious. Rails switched to SQLite as the default somewhat recently
- jstummbillig 10mo agoYeah, that's the one prominent example but, like you said, also just rather recently. Since "the network is slow, duh" has always been true, I wonder why.
- Kerrick 10mo agoIt took a lot: https://fractaledmind.com/2023/12/23/rubyconftw/ https://fractaledmind.com/2023/12/23/rubyconftw/ and https://news.ycombinator.com/item?id=39835496 https://news.ycombinator.com/item?id=39835496
- conradkay 10mo agoMy guess would be that performance improvements (mostly hardware from Moore's law and the proliferation of SSDs, but also SQLite itself) have led to far fewer websites needing to run on more than 1 computer, and most are fine on a $5/month VPS And stuff like https://litestream.io/ https://litestream.io/ or SQLite adding STRICT mode
- Kerrick 10mo agoIt's becoming so! Rails devs are starting to ship SQLite to production. It's not just for their main database either... it's replacing Redis for them, too.
- password4321 10mo agoAs another example, a SQL Server optimization per https://learn.microsoft.com/en-us/sql/t-sql/statements/set-nocount-transact-sql#remarks https://learn.microsoft.com/en-us/sql/t-sql/statements/set-n...: > For stored procedures that contain several statements that don't return much actual data, or for procedures that contain Transact-SQL loops, setting SET NOCOUNT to ON can provide a significant performance boost, because network traffic is greatly reduced.
- Neywiny 10mo agoRather I think their point is that since O(N) is really X * N, it's not the N that gets you, it's the X.
- ahartmetz 10mo ago...and the difference between "a fancy hash table" (in-process SQLite) and doing a network roundtrip is a few orders of magnitude.
- direwolf20 10mo agoRight — the network database is also doing O(N) work to return O(N) results from one query but the multiplier is much lower because it doesn't include a network RTT.
- zffr 10mo agoIMO the page is concise and well written. I wouldn’t call it very elaborate. Maybe the page could have been shorter, but not my much.
- sodapopcan 10mo agoIt's inline with what I perceive as the more informal tone of the sqlite documentation in general. It's slightly wordier but fun to read, and feels like the people who wrote it had a good time doing so.
- hahahahhaah 10mo agoOne index scan beats 200 index lookups though surely? I.e. sometimes one query is cheaper. It is not network anymore. Also you can run your "big" DB like postgres on the same machine too. No law against that.
- Kinrany 10mo agoOne query isn't cheaper than two queries that do the same amount of IO and processing and operate in the same memory space
- silon42 10mo agoYes, (index) scans are rarely faster typical web apps. Unless you have toy amounts data... or doing batch operations which is not typical (and can be problematic for other transactions due to locking, etc...)
- hahahahhaah 10mo agoI admit it is rare. It is more likely if the app has search and DB has been optimised to bring the needed retrevied data onto the index. But it isn't like I haven't reached for a clustered index a few times.
- hahahahhaah 10mo agoHow is it the same IO? Each query needs to navigate the index then read. The two queries do that twice. Is it faster to read pages 30-50 of a book by: a) Go to page 30, read until 50 b) Go to page 30, read that page, close book, open book, go to page 31 and so on. Each page open you get to binary search to find the page.
- Kinrany 10mo agoIt needs to read the index twice, sure, but that's also likely to be cached? Guessing though.
- wenc 10mo ago
- philipodonnell 10mo agoI’ve been experimenting with LiveStoreJS which uses a custom SQLite WASM binary for event sync, so for simplicity I’ve also used it for regular application data in browser and found no issues (yet). It surprised me that using a full database engine in memory could perform well vs native JS objects at scale but perhaps at scale is when it starts to shine. Just be wary of size limits beyond 16-20mb.
- sgbeal 10mo ago> I’ve been experimenting with LiveStoreJS which uses a custom SQLite WASM binary for event sync i'm not sure whether this might be helpful to you, but 3.52 will include a revamped "kvvfs" which (A) also works (non-persistently) in Worker threads and (B) supports callbacks to asynchronously send all db page writes to the client. <https://sqlite.org/wasm/doc/trunk/kvvfs.md https://sqlite.org/wasm/doc/trunk/kvvfs.md>
- causalscience 10mo agoMake sure you click this link https://sqlite.org/src/timeline https://sqlite.org/src/timeline So the sqlite developers use their on versioning system which uses sqlite for storage. Funny.
- kmeisthax 10mo agoYes. Git is the same way: it uses the Linux kernel for storage, and the Linux kernel is managed with Git. :P
- deleted 10mo ago[deleted]
- nchmy 10mo agoThe article doesnt make it at all clear what it is comparing to - mysql running remotely or on the same server? I'm sure sqlite still has less "latency" than mysql on localhost or unix socket, but surely not meaningfully so. So, is SQLite really just that much faster at any SELECT query, or are they just comparing apples and oranges? Or am i mistaken in thinking that communicating to mysql on localhost is comparable latency to sqlite?
- Neywiny 10mo agoI think they're trying to not shame other services, but yes the comparison is vs networked whether that's local on loopback or not. For a small query, which is what they're talking about, it's not inconceivable that formatting into a network packet, passing through the userspace networking functions, into and through kernel, all back out the other side, then again for the response, is indeed meaningfully slower than a simple function call within the program.
- nchmy 10mo agoYes, I explicitly said localhost is slower. But the article doesn't say what it's comparing to nor provide any measurements.
- wild_egg 10mo agoConnecting to localhost still involves the network stack and a fair bit of overhead. SQLite is embedded in your program's address space. You call its functions directly like any other function. Depending on your language, there is probably some FFI overhead but it's a lot less than than an external localhost connection
- Cthulhu_ 10mo agoEven if you're on the same local server, you're still going over a socket to a different service, whereas with sqlite you remain in the same application / address space / insert words I don't fully understand here. So while client/server SQL servers are faster locally than on a remote server, they can (theoretically) never be as fast as SQLite in the same process. Of course, SQLite and client/server database servers have different use cases, so it is kind of an apples and oranges comparison.
- pmbanugo 10mo agoquite interesting. So SQL patterns can be optimised differently in SQLite
- cyanmagenta 10mo agoThere is some risk that, if you design your website to use a local database (sqlite, or a traditional database over a unix socket on the same machine), then switching later to a networked database is harder. In other words, once you design a system to do 200 queries per page, you’d essentially have to redesign the whole thing to switch later. It seems like it mostly comes down to how likely it is that the site will grow large enough to need a networked database. And people probably wildly overestimate this. HackerNews, for example, runs on a single computer.
- Kinrany 10mo agoThere's also the alternative of having a cluster with one local DB in each node
- direwolf20 10mo agoThen you have massive synchronization problems if your data isn't almost read–only.
- Gabrys1 10mo agoif your data isn't mostly read-only, then you're going to have an issue with SQLite. It doesn't nicely support parallel writers
- CuriouslyC 10mo agoNot if you're sharding correctly.
- bastawhiz 10mo agoYou're going to shard your data so requests end up on the server that has the local database that has the right data? Really? Are you going to write a custom load balancer to do this, just so you can use sqlite sharded onto each box? And what happens if you add a new server or scale down? You need to re-shard your data?
- 10mo ago
- daitangio 10mo agoI am using SQLite on paperless-ngx (an app to manage pdf [4]). It is quite difficult to beat SQLite if you do not have a very huge parallelism factor in writes. SQLite is an embedded database: no socket to open, you directly access to it via file system. If you do not plan to use BigData with high number of writers, you will have an hard time beating SQLite on modern hardware, on average use cases. I have written a super simple search engine [1] using python asyncio and SQLite is not the bottleneck so far. If you are hitting the SQLite limit, I have an happy news: PostgreSQL upgrade will be enough for a lot of use cases [2]: you can use it to play with a schemaless mongo-like database, a simple queue system [3] or a search engine with stemming. After a while you can decide if you need a specialized component (i.e. Kafka, Elastic Search, etc) for one of your services. [1]: https://github.com/daitangio/find https://github.com/daitangio/find [2]: https://gioorgi.com/2025/postgres-all/ https://gioorgi.com/2025/postgres-all/ [3]: https://github.com/daitangio/pque https://github.com/daitangio/pque [4]: https://docs.paperless-ngx.com https://docs.paperless-ngx.com
- CuriouslyC 10mo agoThe pattern I like to advocate for now is to do customer sharding with SQLite. Cloudflare makes this easy with D1, you can tie Durable Objects to a user as an afterthought. The nice thing about this pattern is that you can create foreign data wrappers for your customer SQLite databases and query them as if they were in postgres, cross customer aggregations are slow but individual customer analytics are quite fast, and this gives you near infinite scalability.
- storystarling 10mo agoYou hit those write limits surprisingly early if you use background workers though. I had a project with very little user traffic that choked on SQLite simply because a few Celery workers were updating job statuses concurrently. It wasn't the volume of data, just the contention from the workers that forced the switch to Postgres.
- liuliu 10mo agoAre you sure it is choked on writes not on reads and writes? SQLite default setup is inefficient in many ways (as well as it's default compilation options), and that often cause issues. (I am just asking: are you sure WAL is on?)
- charcircuit 10mo ago>For a 50-entry timeline, the latency is usually less than 25 milliseconds. Profiling shows that few of those milliseconds were spent inside the database engine. And instead were spent blocking on the disk for all of the extra queries that were made? Or is it trying to say that the concatenation a handful of strings takes 22 ms. Considering how much games can render with a 16 ms budget I don't see where that time is going rendering html.
- simonw 10mo agoYes, it's saying that the string concatenation and other outside-of-SQL business logic took 22ms, running in their custom TH1 scripting language. In 2016. Update: Actually it looks like I was wrong about TH1: https://fossil-scm.org/home/doc/tip/www/th1.md https://fossil-scm.org/home/doc/tip/www/th1.md The timeline appears to be constructed by C code instead: https://www.fossil-scm.org/home/file?name=src/timeline.c&ci=trunk https://www.fossil-scm.org/home/file?name=src/timeline.c&ci=... Update 2: Here's the timeline code from September 2016: https://www.fossil-scm.org/home/file?name=src/timeline.c&ci=a85eb7c6dcc616b9 https://www.fossil-scm.org/home/file?name=src/timeline.c&ci=... Back then it had some kind of special syntax for outputting HTML: sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i); zBr = P(zNm); if( zBr && zBr[0] ){ @ <p style='border:1px solid;background-color:%s(hash_color(zBr));'> @ %h(zBr) - %s(hash_color(zBr)) - @ Omnes nos quasi oves erravimus unusquisque in viam @ suam declinavit.</p> cnt++; } } That @ syntax is used in modern day Fossil too. Maybe that adds some extra overhead?
- sgbeal 10mo ago> That @ syntax is used in modern day Fossil too. Maybe that adds some extra overhead? (Long-time Fossil dev here.) The @ syntax is pre-processed, transformed to printf()-like calls, the destination of which depends on whether fossil is currently running (to simplify only slightly) from the CLI or as a CGI/server process. That is: @ itself has no runtime costs, but does transform into calls which do have runtime costs. (printf() and its ilk aren't cheap!)
- NorwegianDude 10mo agoI do t have time to test myself now, but it would be interesting to see a proper benchmark. We all know it's not suitable for high write concurrency, but SQLite should be a very good amount faster for reads because of the lack of overhead. But how much faster is it really?
- adzm 10mo agoas an in memory database, I got around 40,000,000 reads per second. Using WAL and a file rather than in memory, around 900,000 reads per second. This is single threaded, for a simple integer ID to string Value query, and a few years old at this point, and only minor config optimizations eg not even using memory mapped io and a ~3gb database with a million or so rows on a Windows machine. The performance really is amazing.
- NorwegianDude 10mo ago40 million reads per second, on a single core? 40 million reads/s is 25 ns per read, that is faster than any RAM I know of.
- yencabulator 10mo agoIt's not like every read would make a separate trip all the way to RAM, caches are a thing and SIMD pipelines/parallelizes comparisons within a hash bucket quite well. Lookups from a hash map should amortize to something like 5-20ns per lookup these days. Abseil's Swiss Tables for C++ and Rust's Hashbrown both should reach that.
- NorwegianDude 10mo agoIf you're looking up values from a 3 GB DB, most would have to hit RAM. Lookups form a hash map can be fast, but SQLite does quite a bit more than just a hash map lookup, and it would usually hit RAM, not L3 cache.
- 10mo ago
- flipped 10mo agoHas anyone tried using distributed versions of sqlite, such as rqlite? How reliable is it?
- otoolep 10mo agorqlite creator here, happy to answer any questions. As for reliability - it's a fault-tolerant, highly available system. Reliability is the reason it exists. :-) If you're asking about quality and test coverage, you might like to check out these resources: - https://rqlite.io/docs/design/ https://rqlite.io/docs/design/ - https://rqlite.io/docs/design/#blog-posts https://rqlite.io/docs/design/#blog-posts - https://philipotoole.com/how-is-rqlite-tested/ https://philipotoole.com/how-is-rqlite-tested/
- lifetimerubyist 10mo agoDefinitely was something surprising that I discovered when building with Sqlite recently. We're tought to avoid N+1 queries at almost any cost in RDBMs but in Sqlite, the N+1 can actually be the best option in most cases. I had to build some back-office tools and used Ruby on Rails with SQLITE and didn't bother with doing "efficient" joins or anything. Just index the foreign keys, do N+1s everywhere - you'll be fine. The app is incredibly easy to maintain and add features because of this and the db is super easy to backup - literally just scp the sqlite db file somewhere else. Couldn't be happier with this setup.
- beagle3 10mo agoscp works as long as the app is not making changes at the same time. If there's a chance someone is writing to the database during the copy, you should "sqlite3 database.sqlite .backup" (or ".dump") first; Or, alternatively, on a new enough sqlite3, you have a builtin sqlite3_rsync that is like rsync except it interacts with the sqlite3 updates to guarantee a good copy at the other end.
- lifetimerubyist 10mo agoGreat tips and you’re right. We just flip into an app-side maintenance mode before we run the backup so we know there’s no writes, scp the file and then flip it back. We only do nightlies so it’s not a problem. The shell script is super simple and we’ve only needed to do nightly backups so far so we run it in a cron at midnight when no one is working. Ezpz. Literally took us an hour to implement and been chugging along without issues for nearly 2 years now without fail. If we ever need more than that I’d probably just setup litestream replication.
- meken 10mo agoSide note - is this post accessible from the site somewhere? I don’t see where you’d find it (along with the C is Best post [1] shared here recently). [1] https://sqlite.org/whyc.html https://sqlite.org/whyc.html
- nefarious_ends 10mo agohttps://www.sqlite.org/doclist.html https://www.sqlite.org/doclist.html
- meken 10mo agoGreat, thanks!
- yomismoaqui 10mo agoAlso SQLite is 35% faster than the filesystem: https://sqlite.org/fasterthanfs.html https://sqlite.org/fasterthanfs.html
- Sesse__ 10mo agoWell, it depends. I vividly remember removing 200 small SQLite queries from a routing algorithm in a mobile app (by moving the metadata into a small in-memory data store instead) and roughly doubling its speed. :-) It was a pretty easy call after seeing sqlite3_step being the top CPU user by a large margin.
- maxpert 10mo agoA lot of skepticism in comments. Let me remind them doing N loops over local disk with in memory cached pages is absolutely different compared to doing RT over typical VPS network. Having said that there is no silver bullet for dumb code! So let's not conflate the argument the author is trying to make.
- ai-christianson 10mo agoProbably even more so if it can fully fit into CPU cache.
- hnlmorg 10mo agoThis might be true for SELECTs, but I found INSERTs are massively slower when compared to grouping in transactions. Which should be obvious. But I could see some reading this blog post and jumping to the wrong conclusion.
- PaulHoule 10mo agoIt's not the cost of protecting one transaction from another transaction so much as the cost of flushing a transaction to storage to survive a crash. In the bad old days you had to wait for a lever to move and for the disk to rotate at least once!
- hnlmorg 10mo ago> It's not the cost of protecting one transaction from another transaction I know it’s not and never suggested it was. I was making the point that writes contain more overhead than reads (which should be obvious) so people should bear that in mind when reading this blog post. Edit: is it “bear” or “bare”? I’m never sure with that phrase haha
- wussboy 10mo agoIt is “bear”, meaning to carry.
- delbronski 10mo agoHow does one go about deployment and backups with a local db? Like let’s say I have a web app hosted on a cloud service like App Engine or Elastic… if I redeploy my web app how do I make sure my current local db does not get get wiped? How are periodic backups handled? I can think of many hacks to do this, but is there a best practice for this kind of stuff? I’m curious how people do this.
- dtkav 10mo agosqlite+litestream [1] is fantastic, i highly recommend it. I use it with pocketbase and it is a delightful and very productive setup. This guide [2] is for an older version of pocketbase and litestream, but i can update it if would be helpful/interesting for anyone. [1] https://github.com/benbjohnson/litestream/ https://github.com/benbjohnson/litestream/ [2] https://notes.danielgk.com/Pocketbase/Pocketbase+on+Fly.io https://notes.danielgk.com/Pocketbase/Pocketbase+on+Fly.io
- delbronski 10mo agoThanks! I’ll look into this.
- dansult 10mo agoYou should update it, it benefits many users.
- gcbirzan 10mo agoWe shouldn't compare doing stupid stuff on sqlite vs doing stupid stuff on postgresql, we should compare doing stupid stuff on sqlite vs not doing stupid stuff on sqlite. How much faster are the better queries?
- polyrand 10mo agoDon't forget that if you're using SQLite on something like EBS, multiple queries may not be efficient. I'm saying this as a huge SQLite fan, but also beware of what kind of storage you're using in your instance.
- andersmurphy 10mo agoYeah, you really want directly connected NVME drives to your machine/VPS. It can make orders of magnitude difference.
- phendrenad2 10mo agoIf your database is running locally on your HTTP server, shouldn't it all be the same? SQLite, Postgres, MySQL, flat files, whatever.