惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Vercel News
Vercel News
博客园 - 司徒正美
C
Check Point Blog
G
Google Developers Blog
The GitHub Blog
The GitHub Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
有赞技术团队
有赞技术团队
P
Proofpoint News Feed
IT之家
IT之家
B
Blog
博客园_首页
量子位
MongoDB | Blog
MongoDB | Blog
博客园 - Franky
J
Java Code Geeks
H
Help Net Security
A
About on SuperTechFans
Apple Machine Learning Research
Apple Machine Learning Research
Jina AI
Jina AI
D
DataBreaches.Net
Y
Y Combinator Blog
大猫的无限游戏
大猫的无限游戏
云风的 BLOG
云风的 BLOG
Google DeepMind News
Google DeepMind News

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Lie to the Postgres planner: EXPLAIN your query at 10,000...
ひとし 田畑 · 2026-06-17 · via DEV Community

ひとし 田畑

Your query is snappy in dev. Then it hits production data and the plan quietly flips from an Index Scan to a Seq Scan, or a Nested Loop to a Hash Join, and everything falls over. The painful part: you can't see that coming on a dev database with 5,000 rows, because the planner picks plans based on how big it thinks the tables are.

So let's change what it thinks.

The planner runs on pg_class statistics

When Postgres plans a query, it doesn't count your rows — it reads cached estimates from the catalog, mainly pg_class.reltuples (estimated row count) and relpages (size in pages). ANALYZE refreshes them. Crucially, those are just numbers in a table — and in a transaction you can change them, watch the planner react, and roll the change back so nothing is ever committed.

That gives you a "what does this query's plan look like at scale?" button without loading a single row.

The trick

BEGIN;

-- Tell the planner these tables are 10,000× bigger than they are.
UPDATE pg_class
   SET reltuples = reltuples * 10000
 WHERE relname IN ('orders', 'customers')
   AND relkind IN ('r', 'p');

-- No ANALYZE: this never runs the query, just plans it.
EXPLAIN (FORMAT JSON) SELECT * FROM orders JOIN customers USING (customer_id) WHERE ...;

ROLLBACK;   -- the catalog edit is never committed, invisible to everyone else

Run that at factor 1, 100, 10000 and diff the plans. The factor where a Seq Scan or a join algorithm flips is exactly the data size where your query's behaviour changes — the thing you wanted to know.

A few details that make it safe and correct:

  • EXPLAIN without ANALYZE never executes the query. It only plans it. So you can do this with a scary DELETE or a 10-minute report and nothing happens.
  • ROLLBACK unconditionally. The pg_class edit lives only inside the transaction; other sessions never see it, and it's gone the moment you roll back. (Wrap it in try/finally so an error still rolls back.)
  • Scale the indexes too. An index's row estimate also lives in pg_class (the index has its own row there). If you only bump the table, the planner sees a giant table with a tiny index and makes weird choices. Bump both.

Here's the real version from cli2ui's "scale simulation," which first does a plain EXPLAIN to discover which tables the query actually touches, then scales exactly those plus their indexes:

UPDATE pg_class
   SET reltuples = reltuples * $1            -- the factor
 WHERE oid IN (
   SELECT oid FROM pg_class
     WHERE relname = ANY($2) AND relkind IN ('r','p')
   UNION
   SELECT indexrelid FROM pg_index           -- the tables' indexes too
    WHERE indrelid IN (SELECT oid FROM pg_class
                       WHERE relname = ANY($2) AND relkind IN ('r','p'))
 );

…wrapped in:

conn.autocommit = False
try:
    cur.execute("SET LOCAL statement_timeout = %s", [timeout_ms])
    cur.execute("SET LOCAL lock_timeout = '2s'")   # don't hang on catalog locks
    cur.execute(SCALE_PGCLASS_SQL, [factor, relnames, relnames])
    cur.execute("EXPLAIN (FORMAT JSON) " + sql_text)
    plan = cur.fetchone()[0]
finally:
    conn.rollback()   # never persist the what-if catalog edit

The honest caveats

  • You need privileges to UPDATE pg_class — superuser, or ownership of those catalog rows. This is a developer/staging trick, not something you hand to an untrusted user on prod.
  • It's an approximation. You're scaling row counts, not regenerating column statistics (histograms, n_distinct, correlation). Selectivity estimates for specific WHERE values won't shift the way they would with real data. It's excellent for "does the plan shape change as the table grows," less so for precise cost numbers.
  • lock_timeout matters. Touching pg_class takes catalog locks; cap the wait so a busy server doesn't make your what-if hang.

But for the question that actually bites you — "will the planner abandon my index when this table gets big?" — it's a 2-second answer on your laptop instead of a 2 a.m. incident.


This is one piece of cli2ui — a local-only web UI over the psql commands you keep half-remembering. No AI, no SaaS. It's MIT-licensed on GitHub. What command do you reach for that should be a button?