Sign in

Radim @ boringSQL

@boringsql.com
65 followers 40 following 66 posts

Straightforward PostgreSQL and SQL tips, tricks, and tools to get stuff done (no fluff).

PostsRepliesMedia
Radim @ boringSQL @boringsql.com · 21/09/2026
Every row lock in #postgres is a write to the page. the locker's xid goes into the same field as a delete marker; flags say "not actually deleted". commit doesn't clean up, vacuum does, at 3am. pg_locks shows nothing. the row is not unlocked. boringsql.com/posts/row-lo...
boringsql.com
Where PostgreSQL stores row locks
What SELECT FOR UPDATE, FOR SHARE, and a foreign key check actually write into the tuple header. t_xmax as a locker, the infomask bits that say so, MultiXactIds when two sessions lock the same row, an...
110
Radim @ boringSQL @boringsql.com · 11/09/2026
The part I didn't expect: four words at the end of a prompt. Adding "make it production-ready" bought more indexes! Were those indexes wrong? No - they would pass regular PR review. Except their impact on maintenance. Writes going up and vacuum taking longer every week. #postgresql #vibecoding
011
Radim @ boringSQL @boringsql.com · 11/09/2026
Are AI agents over-indexing the schemas they create? First it was a hunch. Then I had to verify it. And there was it! Every agent dumps its indexes onto the single table taking all the writes. boringsql.com/posts/unbear...
boringsql.com
The unbearable lightness of one more index
Thirty PostgreSQL schemas written by coding agents, loaded and measured. The indexes are competent; the cumulative cost is not.
110
Radim @ boringSQL @boringsql.com · 01/09/2026
For impatient here's TL;DR: even with replica on the same machine, loopback, naive reads miss 99.2% of the time. not sometimes. not under load. always.
010
Radim @ boringSQL @boringsql.com · 01/09/2026
@apatheticmagpie.bsky.social wrote the mechanics piece last week (seriously read hers first) she saved me from the boring part. what i did was stress-test it and measure what actually happens. clickhouse.com/blog/postgre...
clickhouse.com
Read your writes: WAIT FOR in PostgreSQL 19 | ClickHouse
PostgreSQL 19's new `WAIT FOR` command enables read-your-writes consistency on asynchronous replicas by letting individual reads wait for a specific WAL position.
121
Radim @ boringSQL @boringsql.com · 01/09/2026
postgres 19 has WAIT FOR LSN, which means you can finally drop the "sleep and hope" workarounds for reading your own writes from replicas. I did all the digging around. Sample Go implementation, and the Rails workaround (Active Record thinks WAIT FOR is a write 🙃) boringsql.com/posts/read-y...
boringsql.com
Read your own writes, off the primary
WAIT FOR in PostgreSQL 19 lets a reader block until the replica has replayed a specific LSN. Measured against sticky routing, sleeps, and synchronous commit, on a replica that misses the write 99% of ...
121
Radim @ boringSQL @boringsql.com · 18/08/2026
Spent months benchmarking AlloyDB. The engineering is real, but the compatibility story is more nuanced than the marketing suggests. boringsql.com/posts/google... #postgres #benchmark #htap
boringsql.com
The curious case of Google's AlloyDB
AlloyDB offers Postgres wire compatibility with a divergent storage engine. A deep-dive review into HTAP claims, ScaNN limits, adaptive autovacuum, and costs
000
Radim @ boringSQL @boringsql.com · 07/08/2026
No, it didn't feel like an interrogation. Judge for yourself 😉 Jury's out.
010
Radim @ boringSQL @boringsql.com · 07/08/2026
Striking while the iron was hot, we took it to Postgres FM with Michael and Nik and nobody in that room can be accused of being a fanboy. postgres.fm/episodes/mvcc
postgres.fm
Postgres FM | MVCC
Nik and Michael are joined by Radim Marek to discuss MVCC, including his recent article on how Postgres chose to implement it compared to other systems.   Here are some links to things they mention...
121
Radim @ boringSQL @boringsql.com · 07/08/2026
Postgres MVCC is a 40-year-old design mistake" is one of those claims that just keeps getting repeated outside #postgresql community. So I wrote the article. And got the backlash. boringsql.com/posts/mvcc-b...
boringsql.com
PostgreSQL's MVCC is bad. So is everyone else's.
The critics are right: PostgreSQL's MVCC bloats tables and amplifies writes. Oracle, InnoDB, SQL Server, MongoDB, LSM stores, and etcd answer the same four design questions differently, and each pays ...
140
Reposted by Radim @ boringSQL
Pavlo Golub @pavlogolub.bsky.social · 13/07/2026
Only 10 hours left! 2026.pgconf.pl/call-for-pap...
2026.pgconf.pl
Call for Papers - PGConf.PL 2026
Submit your talk for PGConf.PL 2026!
001
Radim @ boringSQL @boringsql.com · 06/07/2026
Everyone knows #postgres VACUUM reclaims dead tuples. Far fewer people can tell you that a deleted tuple loses its storage at one moment and its line-pointer slot at a completely different one. boringsql.com/posts/vacuum...
boringsql.com
VACUUM at the Page Level
Watch a VACUUM cycle with pageinspect, pg_freespace, and pg_visibility: how a dead tuple loses its storage, then its index entry, then its slot, and how the freed space gets reused.
020
Radim @ boringSQL @boringsql.com · 29/06/2026
Everyone says store money as numeric, not float. Fewer people can say what breaks if you don’t. Your float SUM changes every run. Same rows, nothing touched, three different totals. That’s floats plus parallel aggregation. #postgres boringsql.com/posts/same-r...
boringsql.com
Same rows, different SUM
Same query, same rows, different SUM totals in PostgreSQL. The culprit is parallel aggregation over double precision floats, not a bug. Here is the mechanism and the fix.
000
Radim @ boringSQL @boringsql.com · 15/06/2026
A single NULL turns NOT IN into a query that silently returns zero rows, and the planner couldn't optimize around it for as long as anyone can remember, until a PostgreSQL 19 patch fixed that. boringsql.com/posts/not-in...
boringsql.com
The NULL in your NOT IN
A single NULL turns NOT IN into a query that silently returns zero rows, and the planner couldn't optimize around it for as long as anyone can remember, until a PostgreSQL 19 patch.
000
Reposted by Radim @ boringSQL
lukas.fittl.com @lukas.fittl.com · 05/06/2026
Gave a talk today at #PGData2026 on #Postgres plan management and monitoring. Some exciting changes coming to Postgres 19, and pg_stat_plans (plan statistics) now supports the new release. Full slides from the talk: resources.pganalyze.com/pganalyze_PG...
resources.pganalyze.com
043
Radim @ boringSQL @boringsql.com · 04/06/2026
Postgres chat with a view - amazing warmup for @pg-data.bsky.social - let’s go #postgres Chicago
010
Radim @ boringSQL @boringsql.com · 03/06/2026
You trust #postgresql pg_stat_statements bit too much. That might be the problem. It's quietly bending what it reports, and none of it is a bug. Part 1 - everything it tells you boringsql.com/posts/pg-sta... Part 2 - everything it can't boringsql.com/posts/pg-sta...
boringsql.com
pg_stat_statements: everything it tells you
What pg_stat_statements records and what it quietly drops: the queryid jumble that splits one query into many rows, the frozen first-seen query text, and averages that hide your p99.
010
Radim @ boringSQL @boringsql.com · 11/05/2026
Without fail, VIEWs are source of heated discussions. What should be a cleanest SQL abstraction layer comes with a lot of baggage. boringsql.com/posts/strong...
boringsql.com
Strong views on PostgreSQL VIEWs
Views are PostgreSQL's cleanest abstraction and its most rigid one. How rewrite rules, attribute numbers, and the dependency catalog conspire to make every column change a teardown, and what is missin...
010
Radim @ boringSQL @boringsql.com · 19/04/2026
This is the core knowledge I will cover during my session(s) "Visualizing PostgreSQL Storage Internals" next week in Essen (German Postgres Conf) and in June at @pg-data.bsky.social
000
Radim @ boringSQL @boringsql.com · 19/04/2026
It comes down to eight bytes on every tuple (xmin, xmax) and a three-number snapshot on every transaction. That's the whole mechanism. The consequences: bloat, VACUUM horizons,and isolation-level surprises.
100
Radim @ boringSQL @boringsql.com · 19/04/2026
Shared Buffers showed you the cache. The 8KB Page showed you what's inside. MVCC is what bind them together in concurrent access. The reason two sessions can read the same bytes and legitimately see different rows.
100
Radim @ boringSQL @boringsql.com · 19/04/2026
The third boringSQL visualizer is live, and this one is the piece I've been building toward: MVCC. boringsql.com/visualizers/... #postgresql
boringsql.com
PostgreSQL MVCC Visualized
Interactive visualization of PostgreSQL's MVCC: xmin, xmax, snapshots, two concurrent sessions, and VACUUM reclaim.
100
Reposted by Radim @ boringSQL
Jimmy Angelakos @vyruss.org · 30/03/2026
If you're interested in #PostgreSQL, make sure you attend my April 3rd LinkedIn Live event (10am ET/3pm BST)! I'll break down real #SQL anti-patterns from my book (via @manning.com) and fix them live. 👇 🎟️ Register: linkedin.com/events/74433... 📖 Book: hubs.la/Q03Nc0hv0 #Postgres #webinar
LinkedIn LIVE: Jimmy Angelakos - PostgreSQL Mistakes and How to Avoid Them
021
Radim @ boringSQL @boringsql.com · 30/03/2026
That imperative mindset leads to a pattern I see everywhere: one CTE filters rows, the next LEFT JOINs and aggregates with GROUP BY, the next filters the result. Reads like a pipeline. Performs terribly. The GROUP BY creates a wall the planner can't optimize past, even after in-lining.
000
Radim @ boringSQL @boringsql.com · 30/03/2026
Rewriting badly written CTEs is a constant source of work for me. Not because CTEs are bad. Mostly because developers use them to force execution order on the database. "First do this, then do that". Learn more at boringsql.com/posts/good-c... #postgresql #sql
boringsql.com
Good CTE, bad CTE
The planner treats CTEs very differently depending on how you write them. Here's what happens under the hood, version by version, through PostgreSQL 18.
100
Reposted by Radim @ boringSQL
PostgresEDI @postgresedi.bsky.social · 10/03/2026
This week: #PostgreSQL #Edinburgh #meetup on 📅 Thurs, March 12th! Join @boringsql.com and @vyruss.org for talks on making sure your queries don't break production & on how easy it is to run #Postgres on #Kubernetes with @cloudnativepg.bsky.social 👇 luma.com/5pglgx8h #CloudNativePG #k8s #TechMeetups
Lister Learning and Teaching Centre
032
Radim @ boringSQL @boringsql.com · 09/03/2026
Major progress for RegreSQL 2.0. It can now force pretty much any statistics. Yesterday I highlighted the current limitation of overriding statistics. Today I can show you planner being completely independent and choosing Bitmap Index Scan on table with 1 tuple.
020
Radim @ boringSQL @boringsql.com · 09/03/2026
What happens when you inject production statistics into a 10,000-row PostgreSQL test table? The query plan flips.

 PostgreSQL 18 makes this a one-liner. pg_dump --statistics-only
 Plain SQL, under 1MB, no data needed. Learn the mechanics in my latest blog post. 

boringsql.com/posts/portab...
boringsql.com
Production query plans without production data
PostgreSQL 18 makes optimizer statistics portable. Export them from production and inject them into test databases to get realistic query plans without the data.
020
Radim @ boringSQL @boringsql.com · 27/02/2026
You just might be onto something 🤣 I've been looking for something for the back of the t-shirts and I think we have a strong candidate Equality: 0.5% Ranges: 33% May your indexes be with you
010
Radim @ boringSQL @boringsql.com · 27/02/2026
Or what about cases where there are no statistics at all? Like materialized CTEs, temporary tables, or computed expressions in WHERE clauses. The planner falls back to hardcoded constants. 0.5% selectivity for equality. 33% for ranges. Good luck.
110
Radim @ boringSQL @boringsql.com · 27/02/2026
#postgresql takes a bite of 30,000 rows from your 50 million row table and calls it a day. That's 0.06% of your data deciding if your query will be fast or slow. boringsql.com/posts/postgr...
boringsql.com
PostgreSQL Statistics: Why queries run slow
Slow PostgreSQL queries? Stale statistics are often to blame. Learn how the query planner uses statistics to estimate row counts and how to fix them.
110
Radim @ boringSQL @boringsql.com · 24/02/2026
It's not magic. It's called SQL query regression testing. 🍻 as postmortem 😉
030
Radim @ boringSQL @boringsql.com · 24/02/2026
Step 1: "LGTM, this query is fine" Step 2: Deploy to production Step 3: Pray 🙏 Step 4: SRE - "This query is definitely NOT fine" Step 5: "But... it worked on MY database" 🔥 No ifs. No buts. On 12 March @postgresedi.bsky.social find out how to become an atheist when it comes to your SQL queries.
252
Radim @ boringSQL @boringsql.com · 21/02/2026
Busted :) These are the ones I'm speaking at - next time I'll have to do better proposal so Paris find me worthy 😉
000
Radim @ boringSQL @boringsql.com · 21/02/2026
#postgresql season is officially on. I'll be on the ground at 🇪🇺 Nordic pgDay in Helsinki (March) 🇪🇺 German pgConf in Essen (April) 🇺🇸 PGDATA in Chicago (June) 🇬🇧 pgDay in London (September) I’m looking forward to all the chats and fun that's ahead.
110
Radim @ boringSQL @boringsql.com · 19/02/2026
To make it bit more fun, you can play with the 2nd visualizer. boringsql.com/visualizers/...
boringsql.com
Inside the 8KB Page: PostgreSQL Page Layout Visualized
Interactive visualization of PostgreSQL's 8KB page: header, line pointers, free space, and tuple data.
010
Radim @ boringSQL @boringsql.com · 19/02/2026
Epitome of boring. What else to write on site called boringSQL then dry introduction of what is inside #postgresql 8KB page? boringsql.com/posts/inside...
boringsql.com
Inside PostgreSQL's 8KB Page
A byte-level tour of the 8KB page. PostgreSQL's atomic unit of storage. We deep dive into page headers, line pointers, and free space using pageinspect to see exactly how your data lives on disk.
110
Radim @ boringSQL @boringsql.com · 10/02/2026
"The Alchemy of Shared Buffers" must have been amazing presentation by Josef Machytka during CERN pgDay (had to skip that one unfortunately). It's worth checking the slides. medium.com/@josef.machy...
medium.com
The Alchemy of Shared Buffers: Balancing Concurrency and Performance
Josef Machytka: Speaker portfolio
010
Radim @ boringSQL @boringsql.com · 06/02/2026
Back to Buffers. This time you can learn how to read and understand Buffer statistics in EXPLAIN output in my latest article boringsql.com/posts/explai... #postgresql
boringsql.com
Reading Buffer statistics in EXPLAIN output
Learn how to interpret buffer statistics in PostgreSQL EXPLAIN output. Understand shared hits, disk reads, temp spills, and planning buffers to accurately diagnose and fix query performance bottleneck...
010
Radim @ boringSQL @boringsql.com · 05/02/2026
While this led to 2748 lines dropped from RegreSQL it gets things done. Stay tuned for 2.0 release!
010
Radim @ boringSQL @boringsql.com · 05/02/2026
That's where new project "fixturize" comes in: github.com/boringSQL/fi... its goal is to retrieve data snapshots with referential integrity intact, while masking sensitive data in the process.
github.com
GitHub - boringSQL/fixturize: Extract seed data from any PostgreSQL database
Extract seed data from any PostgreSQL database. Contribute to boringSQL/fixturize development by creating an account on GitHub.
110
Radim @ boringSQL @boringsql.com · 05/02/2026
But it's not bad news. Agonising over fixtures forced me to look at the problem from another direction - how to quickly and securely use production data. Gave up on sampling, only to find a better solution: graph-traversal based data extraction with masking to strip customer data and PII.
110
Radim @ boringSQL @boringsql.com · 05/02/2026
While other changes leading up to v2.0 are strong quality-of-life improvements, I've decided to pull generated fixture support out of RegreSQL entirely for now.
100
Radim @ boringSQL @boringsql.com · 05/02/2026
There are weeks of full wins, and those where it's time to eat the humble cake. RegreSQL has been making great progress, but I've been fighting the fixture system more and more. After many conversations and endless evenings - it's time to admit: fixtures are a genuinely hard problem
110
Radim @ boringSQL @boringsql.com · 31/01/2026
Still recharging after this week. But it's that good kind of tired - the one you get when something actually matters. @praguepgdevday.bsky.social was my first time speaking at a bigger Postgres event. The RegreSQL session went well, but honestly? The conversations afterwards were the real thing
010
Reposted by Radim @ boringSQL
Prague PostgreSQL Developer Day @praguepgdevday.bsky.social · 29/01/2026
That’s a wrap at #P2D2! 🎉 Huge thanks to our amazing speakers, organizers, and attendees for making it unforgettable. PostgreSQL, people, and Prague—what a combo. We can’t wait to do it again next year. See you soon — brzy na viděnou! 🇨🇿💙
032
Radim @ boringSQL @boringsql.com · 24/01/2026
Buffers came up during Postgres FM chat and people asked why they matter so much. So I wrote about it - with interactive demos to make it click. Introduction to Buffers: boringsql.com/posts/introd... Do the visuals help you to grasp the concept easier? Let me know.
boringsql.com
Introduction to Buffers in PostgreSQL
How PostgreSQL actually manages memory, from shared_buffers and dirty pages to the OS page cache sitting underneath it all.
000
Radim @ boringSQL @boringsql.com · 18/01/2026
First testing of RegreSQL integration with Rails / ActiveRecord via `rake regresql:export`.
010
Radim @ boringSQL @boringsql.com · 16/01/2026
I got myself talking about the motivation behind RegreSQL on Postgres FM 🎙️ We nerded out a bit about why timing is a terrible metric, why buffers actually matter, statistics, performance – and why all of this is more relevant in the age of agentic coding. postgres.fm/episodes/reg...
postgres.fm
Postgres FM | RegreSQL
Nik and Michael are joined by Radim Marek from boringSQL to talk about RegreSQL, a regression testing tool for SQL queries they forked and improved recently. Here are some links to things they ment...
110
Radim @ boringSQL @boringsql.com · 14/01/2026
Test if queries hit the right indexes at production scale – without needing to have production data. I'll show you exactly how during @praguepgdevday.bsky.social - Session "It works on my database - Regression testing of SQL queries" is last of the day, but I intend to make it worth staying for.
032