frontpage.
newsnewestaskshowjobs

Open Source @Github

fp.

Open in hackernews

The only scalable delete in Postgres is DROP TABLE

https://planetscale.com/blog/the-only-scalable-delete
47•hollylawly•2d ago

Comments

sgarland•1h ago
The same is true to a lesser extent in MySQL / MariaDB. It does better since it doesn’t do oldest-to-newest tuple chains, but it’s still adding non-trivial work to the DB, much of which is effectively wasted if you don’t care about the visibility of the deleted (or soon-to-be deleted) tuples to other transactions.

I sincerely hope that Planetscale’s efforts succeed long-term to shift devs’ understanding and acceptance of RDBMS operations. Their blog posts and docs are generally quite good. IME, devs (and even ops-ish teams) simply do not care about all of this, and will create elaborate bespoke tooling to run DELETEs in bulk, because they either don’t understand the capabilities of the database, or don’t want to deal with the [minor] increased complexity that a partitioned schema brings, and will happily pay the extra cost / latency for deletions.

pstuart•57m ago
Yep, partitions are the way to go there.
awinter-py•49m ago
^ this

been exploring clickhouse and while it is definitely not a general purpose DB, for time-series shaped data that can survive some insert latency, the automatic partition-based TTL is very nice and, at least so far, requires zero attention to maintain

which I guess is solved by `pg_partman` at the bottom of the post

crazygringo•56m ago
Only by a weird definition of "scalable". The first sentence says:

> Counterintuitively, large DELETEs add work to the database.

There is nothing counterintuitive about this. It takes just as much work to delete a row as it takes to insert a row. Why wouldn't it? Obviously you have to do almost all the same operations: write a log, write the deletion, update indices, replicate it, etc.

And yes, it's a well-known trick for all major relational databases (not just Postgres) that if you want to delete 90% of rows from a large table, it's much faster to just copy the rows you want to keep to a new table, run DROP TABLE on the old table, and rename the new table to the old table. Since DROP TABLE is ~instantaneous, mainly involving table-level metadata.

DELETE scales just fine, in the sense that if you are constantly inserting and deleting individual rows, DELETE scales the same as INSERT.

Basic database functionality is designed around the assumption of lots of small transactions. Whenever you have to do something involving millions of rows at once, you generally need to investigate solutions that work well in "bulk". E.g. loading rows directly from a file rather than with SQL, adding indices only after the data has been loaded rather than before, disabling foreign key checks on large operations (if you know by design that the keys are valid)... and yes, taking advantage of DROP TABLE instead of DELETE. This doesn't mean small transactions aren't scalable, it just means bulk operations are qualitatively different and benefit from their own solutions. And DELETE is no different from INSERT in this regard.

echoangle•54m ago
> And yes, it's a well-known trick for all major relational databases (not just Postgres) that if you want to delete 90% of rows from a large a table, it's much faster to just copy the rows you want to keep to a new table, run DROP TABLE on the old table, and rename the new table to the old table.

Dumb question but why does the optimizer not just do that in secret then? Seems like something that should be detectable with some heuristics.

mordae•48m ago
jandrewrogers•55m ago
This generalizes to most (all?) databases. Selective deletion is largely an unsolved problem at scale in databases to the extent it doesn't release the deleted resources. Under the hood databases try to turn this into selective resource truncation, which scales much better, but in most cases that is not possible without careful design of your data model.

Similarly, you often have to remind devs that in many databases an UPDATE is just an INSERT + DELETE, with all of the scaling issues implied.

levkk•54m ago
CRUD apps don't usually delete in bulk. It's also hard to structure partitions in a way that doesn't wipe out months of important business data -- this is why teams often ETL their DB into Snowflake/ClickHouse and only then drop partitions. That makes it hard for the app to use that data again.

The better approach is either to change your storage engine (e.g. OrioleDB is working on adding the undo log to Pg), or to shard which distributes the vacuum load across multiple servers.

sgarland•31m ago
They should be performing bulk deletions, due to GDPR: “Data must be stored for the shortest time possible.” Unless you have some kind of rolling cron checking every few minutes (and even then, depending on your scale, that may well be considered bulk), that generally resolves to something like daily or weekly deletions.
aftbit•51m ago
TIL about pg_partman

https://github.com/pgpartman/pg_partman/blob/development/doc...

saisrirampur•46m ago
Partially true but too much of a blanket statement and clickbaity.

DELETE with well-tuned autovacuum works pretty well. Have seen it work at TBs scale with no hicuups. If DELETEs are large, we used to recommend customers to follow that with a manual VACUUM for table to reclaim space right away for future rows.

DROP TABLE can be risky, it requires an ACCESS EXCLUSIVE LOCK and if its waiting, it blocks all other statements following it, because of how lock queues work in Postgres. And you cannot keep doing high concurrent DROP TABLEs to run your large scale CRUD app.

smithcheck4•30m ago
I have been using TRUNCATE allm the way, and is fast as very good.
xenator•25m ago
Deleting whole file is faster then deleting rows in file.
foreigner•23m ago
Surprisingly to remove small numbers of rows in multiple tables (e.g. cleanup between automated tests), DELETE is often faster than TRUNCATE! It's counterintuitive but just measure it for yourself and see. Note you can DELETE from multiple tables in one statement using CTEs, and that way you don't need to think about foreign key dependency order.
twotwotwo•23m ago
Years ago work was bit by the analogous thing in MySQL. Like it usually does, it took a chain of events:

- We wrote a cronjob to periodically DELETE for a retention policy on a table we'd just created. Most senior person on the team reviewed it, looked fine.

- Unusually for us, we prioritize QA'ing a different feature for release, delaying the release of this cronjob and a bunch of other code.

- During that delay, the new table accumulated many times more rows to be deleted than we'd expected during review.

- Release happens. All looks well since the initial delete wasn't a migration and cronjob hasn't run yet; engineer doing the release signs off.

- Cronjob runs, deleting hundreds of millions of rows quickly.

- Next day, replica lag's high and MySQL's transaction history is very high. MySQL keeps transaction history around until purge threads have visited all the affected pages on disk.

- The bad cluster conditions last for days and lead to other problems.

This omits detail and the 'noise' of everything else we were watching. But it gets across how the code and MySQL behaved.

Like most exciting events, it led to multiple changes to avoid a repeat. For retention policies, our new approach was one at the end of PlanetScale's post, to partition and drop old partitions. Transitioning to this from a huge unpartitioned table can be fun!

If a table is append-only and already huge, with lots of rows already past the retention threshold, you might only copy the rows to be kept to the new partitioned table: copy what you can, lock tables, do a last catch-up copy and swap tables. (Roughly the blog's 'performant one-off delete'.)

If the table's merely kind of big, gh-ost or such could allow you to ALTER without causing lag, locking, etc.

At a scale below that, you could run a slow incremental 'nibble' delete while watching server stats, and a step below that, plain ALTERs or DELETEs are fine.

Using partitioning has fun bits, too. In MySQL, the partition key has to be part of any unique index, understandably. But you have to keep that in mind when you're using INSERT..ON DUPLICATE KEY UPDATE and relying on uniqueness to trigger the update. Things stay interesting!

I hear Vitess shops like PlanetScale usually don't run multi-terabyte myqsld instances in the first place: even when physical nodes are big, they run many smaller mysqlds on them. That wouldn't make all this fully irrelevant--huge deletes would still sometimes be worse than copy-swap-drop--but it does seem real handy for taming issues that tend to worsen with mysqld size, like replication lag. All to say, little bit jelly of their setup over there!

inigyou•7m ago
This website appears to be hard blocking my IP address (drops all packets( so I can't read the article.
It drops dependents.
sgarland•37m ago
I assume partly because that would be extremely surprising behavior, and depending on the RDBMS and version, could introduce unexpected stalls. For example, MySQL < 8.0.23 scans the entire buffer pool to clear pages that were dropped, which can take a long time on large instances. There is / was a similar issue with its adaptive hash index, which AFAIK wasn’t ever fixed, though AHI’s default being shifted to OFF in 8.4 is a workaround, in a very hacky way.
Retr0id•26m ago
Maintaining the expected observable behaviours would get complicated if queries (especially other updates) against the same table are happening concurrently.
crazygringo•14m ago
Because what do you do if rows are being inserted in the original table, while the new table is having rows copied over? You'll get missing rows.

You can only do the DROP TABLE trick if you know nothing else is writing to the table at the same time. You know if that's the case, according to your business logic. The database has no idea.

The DROP TABLE trick effectively bypasses all the normal guarantees of data consistency. This is why it's so fast. But you have to know that that's a safe thing to do for your data.

setr•44m ago
> It takes just as much work to delete a row as it takes to insert a row. Why wouldn't it? Obviously you have to do almost all the same operations: write a log, write the deletion, update indices, replicate it, etc.

It takes far more work to delete/update than insert. My recent example is updating ~2TB of text data was about 40x slower than inserting 12TB (was trying to correct some large text truncation that occurred during migration into PG, ended up being faster to redo).

crazygringo•19m ago
> It takes far more work to delete/update than insert.

Updating rows of text data is going to be more work, because variable-length text can't be updated in-place. So in terms of allocating space, it's more like a delete plus an insert. That's not surprising. (An in-place update that doesn't touch indices is generally going to be faster than an insert, though.)

I'm not aware of instances where a delete is "far more work" than an equivalent insert though. That's not the general case, and I'm having a hard time thinking of any situations where that would be true.

winterbloom•16m ago
how does that solution work if the table that is dropped has foreign key constraints?
crazygringo•12m ago
That's why I said:

> disabling foreign key checks on large operations

And you have to know that, according to your business logic, what you're doing is safe.

Retr0id•10m ago
> if you are constantly inserting and deleting individual rows, DELETE scales the same as INSERT

Technically correct, but for a small table with a high churn rate, the performance characteristics may be surprising in that the "n" in most big-O calculations includes all inserts since the last VACUUM, not the actual number of resident rows.

No, everyone is not using AI for everything

https://gabrielweinberg.com/p/people-are-consuming-ai-like-they
174•yegg•1h ago•159 comments

Rio de Janeiro's "homegrown" LLM appears to be a merge of an existing model

https://github.com/nex-agi/Nex-N2/issues/4
22•unrvl22•1h ago•8 comments

The Birth and Death of JavaScript (2014)

https://www.destroyallsoftware.com/talks/the-birth-and-death-of-javascript
142•subset•4h ago•72 comments

Firewood Splitting Simulator

https://screen.toys/firewood/
283•memalign•4d ago•97 comments

Measles surge in Utah sparks fears US could undo decades of progress

https://www.dailymail.com/news/article-15897903/measles-surge-utah-US-elimination-status.html
85•Bender•1h ago•38 comments

Lisp's Influence on Ruby

https://blog.tacoda.dev/lisps-influence-on-ruby-6a54f1a7740e
134•tacoda•3d ago•15 comments

FarOutCompany

https://faroutcompany.com/
53•bookofjoe•2h ago•4 comments

Caddy compatibility for zeroserve: 3x throughput and 70% lower latency

https://su3.io/posts/zeroserve-caddy-compat
64•losfair•2h ago•18 comments

The only scalable delete in Postgres is DROP TABLE

https://planetscale.com/blog/the-only-scalable-delete
47•hollylawly•2d ago•24 comments

Perlisisms

https://www.cs.yale.edu/homes/perlis-alan/quotes.html
18•tosh•1h ago•6 comments

Rio de Janeiro's city government model Rio3.5 beats Qwen3.7 in recent benchmarks

https://twitter.com/zenmagnets/status/2065796012820848699
85•lucasfcosta•2h ago•25 comments

I indexed 669 GB of my GoPro videos using my M1 Max computer and local ML models

50•iliashad•1h ago•7 comments

Global density and biomass of arbuscular mycorrhizal fungal networks

https://www.science.org/doi/10.1126/science.adu4373
19•zdw•23h ago•0 comments

Formal Methods and the Future of Programming

https://blog.janestreet.com/formal-methods-at-jane-street-index/?from_theconsensus=1
53•eatonphil•4h ago•10 comments

Show HN: Dual YOLOv8n UAV Detection on RK3588S at 42 FPS Using NPU

https://github.com/alebal123bal/khadas_yolov8n_multithread
19•alebal123bal•2h ago•2 comments

How did Atari apply side art to Arcade Cabinets?

https://arcadeblogger.com/2026/06/14/how-did-atari-apply-side-art-to-arcade-cabinets/
43•msephton•3h ago•6 comments

Show HN: 3D print Z reinforcement via injected loops

https://mgunlogson.github.io/magma/
8•mgunlogson•5d ago•5 comments

How to Earn a Billion Dollars

https://paulgraham.com/earn.html
214•kingstoned•4h ago•558 comments

Free SQL→ER diagram tool, runs in the browser, nothing uploaded

https://sqltoerdiagram.com/
305•robhati•12h ago•58 comments

Honda Civics and the Evil Valet

https://juniperspring.org/posts/honda-evil-valet/
359•librick•15h ago•84 comments

KPMG pulls report on AI usage due to apparent hallucinations

https://techcrunch.com/2026/06/13/kpmg-pulls-report-on-ai-usage-due-to-apparent-hallucinations/
58•Brajeshwar•2h ago•5 comments

Dangerous hormone-disrupting chemicals found in US breast milk samples

https://www.theguardian.com/us-news/2026/jun/14/breast-milk-research-chemicals
39•andsoitis•1h ago•8 comments

Cloud-based LLM gold rush is ending

https://automato.substack.com/p/apple-wwdc-and-the-fable-5-embargo
36•andrewstetsenko•1h ago•6 comments

Extinction-Level Capitalism

https://matthewbutterick.com/extinction-level-capitalism.html
63•laurex•2h ago•20 comments

Don't trust large context windows

https://garrit.xyz/posts/2026-05-06-dont-trust-large-context-windows
210•computersuck•10h ago•148 comments

Historic co-determination helps monasteries navigate digital change

https://phys.org/news/2026-05-historic-monasteries-digital-countries.html
62•indynz•2d ago•41 comments

Conversations with a six-year-old on functional programming (2018)

https://byorgey.wordpress.com/2018/05/06/conversations-with-a-six-year-old-on-functional-programm...
31•downbad_•2h ago•3 comments

FreeOberon – Open-Source, Cross-Platform, Free Pascal/Turbo Pascal-Like Language

https://github.com/kekcleader/FreeOberon
130•peter_d_sherman•3d ago•56 comments

A 'cold blob' in the Atlantic could be a sign of AMOC shutdown – CNN

https://www.cnn.com/2026/06/12/climate/cold-blob-atlantic-amoc-ocean-circulation
87•tambourine_man•2h ago•91 comments

Tribblix: The retro Illumos distribution

http://tribblix.org/
70•naturalmovement•11h ago•25 comments