TigerData logo
TigerData logo
  • Product

    Product

    Tiger Cloud

    Robust elastic cloud platform for startups and enterprises

    TimescaleDB Enterprise

    Self-managed TimescaleDB for on-prem, edge and private cloud

    Open source

    TimescaleDB

    Time-series, real-time analytics and events on Postgres

    Search

    Vector and keyword search on Postgres

  • Industry

    Crypto

    Energy Telemetry

    Oil & Gas Operations

  • Docs
  • Pricing

    Pricing

    Enterprise Tier

  • Developer Hub

    Changelog

    Benchmarks

    Blog

    Community

    Customer Stories

    Events

    Support

    Integrations

    Launch Hub

  • Company

    Contact us

    About

    Timescale

    Partners

    Security

    Careers

Log InStart a free trial
TigerData logo

Products

Time-series and Analytics AI and Vector Enterprise Plan Cloud Status Support Security Cloud Terms of Service

Learn

Documentation Blog Tutorials Changelog Success Stories Time-series Database

Company

Contact Us Careers About Newsroom Brand Community Code Of Conduct Events

Subscribe to the Tiger Data Newsletter

By submitting, you acknowledge Tiger Data's Privacy Policy

2026 (c) Timescale, Inc., d/b/a Tiger Data. All rights reserved.

Privacy preferences
LegalPrivacySitemap

Back to blog

Copy as HTML

Open in ChatGPT

Open in Claude

Open in v0

Matty Stratton

By Matty Stratton

5 min read

Mar 05, 2026

PostgreSQL Performance

Table of contents

01 What partitioning actually fixes (and nothing else)02 The operational complexity nobody mentions03 The performance cliff for long-range queries04 When to partition vs. when you're just buying time05 The decision point

The Hidden Costs of Table Partitioning at Scale

The Hidden Costs of Table Partitioning at Scale

Back to blog

PostgreSQL Performance
Matty Stratton

By Matty Stratton

5 min read

Mar 05, 2026

Table of contents

01 What partitioning actually fixes (and nothing else)02 The operational complexity nobody mentions03 The performance cliff for long-range queries04 When to partition vs. when you're just buying time05 The decision point

Copy as HTML

Open in ChatGPT

Open in Claude

Open in v0

You implemented partitioning six months ago. DROP TABLE replaced your multi-hour DELETEs. Retention is clean. Queries got faster. It worked.

Then something changed.

Quarterly reports started slowing down. A new engineer broke the partition gap monitor. The pg_partman job failed silently over a weekend. You added a column and had to touch 400 tables.

Partitioning solved the problem it was designed to solve. These are tradeoffs, not bugs.

What partitioning actually fixes (and nothing else)

Let's be precise about the win, because it's real.

Partition pruning on time-range queries is fast. You can see it in the query plan: Partitions removed: 498 of 500. That's the planner skipping almost everything and going straight to the data you care about. DROP TABLE vs DELETE is a massive win at scale. No dead tuples, no vacuum pressure, no multi-hour cleanup windows. And autovacuum scope gets smaller per partition, which means vacuum runs finish faster and cause less contention.

Those are real improvements. If your workload is narrow time-range queries and periodic data retention, partitioning does exactly what you need.

But let's talk about what didn't change.

The write path, per-row overhead, and aggregate WAL volume: these are all unchanged. You're still writing row-by-row through MVCC, still generating the same WAL, still paying the same per-tuple cost on every insert. The performance gains are almost entirely on the read side, and specifically for queries that hit a narrow time range.

Keep that in your head. Partitioning is a read optimization with operational side effects. Not an architecture.

The operational complexity nobody mentions

This is the part where your pager starts going off at 2am.

The first thing you'll discover is that partition automation must not fail. pg_partman or cron pre-creates your partitions. When it works, you never think about it. When it doesn't run, you don't get a warning. You get a failed insert. Your application throws an error because the target partition doesn't exist, and now you have silent gaps in your production data. Nobody notices until someone queries that time range and gets empty results. By then, the data is gone. That's not a bug in your partitioning setup but the fundamental fragility of pre-creation as a pattern.

The second thing is that your runbooks grow. Not a little. A lot. You now have partition management procedures. Backfill processes that have to understand partition boundaries. Schema migration procedures that touch every partition individually (or use tooling that does, which is its own maintenance surface). Failover procedures that now involve partition state. Every new process that touches the database has to be partition-aware. New engineer onboarding time goes up, not down, because there's more implicit knowledge required to operate the system safely.

The third thing is monitoring surface area. Autovacuum lag per partition. Replication lag that can spike when a partition gets dropped on the primary. pg_dump time scaling linearly with partition count. Partition gap monitoring (which, as we just discussed, you absolutely need). Index bloat per partition. Each of these is a new thing that can break independently, and each one needs its own alert threshold.

Do the math. At 500 daily partitions (roughly 16 months of daily data), a schema migration that used to be one ALTER TABLE statement now touches 500 tables. That's not a complaint about Postgres. It's just arithmetic. And 500 is not a big number for teams doing daily partitions with multi-year retention.

The performance cliff for long-range queries

Remember how partition pruning made your queries faster? Same mechanism. Same planner. It's about to make different queries slower.

Partition pruning is fast per-partition. The planner looks at your WHERE clause, figures out which partitions match, and skips the rest. When you're querying the last hour or the last day, pruning is doing exactly what you want. The planner prunes 498 of 500 partitions and you get your results fast.

But pruning is linear with partition count. At 500+ partitions, the planner still evaluates pruning logic for every single one. For a query that touches 200 partitions (say, a quarter's worth of daily partitions), you pay that evaluation cost 200 times. The PostgreSQL docs say it directly: planning time scales with partition count after pruning.

Guess which queries hit this cliff. Quarterly business reviews. Year-over-year comparisons. ML training data pulls that need six months of history. Compliance audits that span the full retention window.

You optimized for the p95 dashboard query. The CFO's annual report query never made it into the benchmark.

When to partition vs. when you're just buying time

There are two distinct scenarios here, and it's worth being honest about which one you're in.

Partitioning solves your problem when retention management is the primary concern. When writes are moderate, not continuous high-frequency ingestion. When long-range queries are rare. When data volume growth is predictable. When the operational overhead is acceptable given your team's size and maturity. If that's your workload, partitioning is the right call and you should feel good about it.

Partitioning delays your problem when you're running continuous high-frequency ingestion. IoT, financial data, observability. When write amplification and WAL volume are already concerns. When you need fast queries across the full retention window, not just the hot partition. When your team is spending real engineering time managing the partitioning scheme instead of building product.

In the second scenario, partitioning buys you three to six months. The ceiling doesn't move. The underlying MVCC overhead, the row-based storage read amplification, the WAL volume: none of that is affected by partitioning. Partitioning reorganizes the data. It doesn't change how Postgres handles it.

The decision point

Partitioning is the right call for a lot of teams. The operational cost is worth it when the workload matches what partitioning was built for.

The mistake is treating it as an architecture rather than an optimization. When the workload is continuous high-frequency ingestion with long retention and cross-range query requirements, partitioning is the correct next step on the optimization path. It buys time. And then the ceiling reappears.

If you're under 100M rows and already building partitioning automation, that's worth paying attention to. Migration at 50M rows is a week of work. At 1B rows, it's a multi-month project.

Score your workload honestly. How much of your query traffic hits narrow time ranges vs. the full retention window? How much engineering time goes into partition management vs. product development? How predictable is your data growth?

If you don't like the answers, you're probably buying time.

Tiger Data extends vanilla Postgres to handle this workload without replacing it. You keep SQL, your extensions, your tooling. What changes is the storage engine and partitioning layer underneath. In benchmarks at one billion rows, TimescaleDB delivered up to 1,000x faster query performance while reducing storage by 90% through native compression. Those are the exact query patterns this post describes: time-range filters, aggregations, and selective scans on growing datasets.

This post is part of a series on Postgres performance limits for high-frequency data workloads. The full analysis, including a workload scoring framework and migration complexity breakdown at different scales, is in the essay: Understanding Postgres Performance Limits for Analytics on Live Data. Ready to test it on your own data? Start a free Tiger Data trial.

Related posts

Write Amplification in Postgres: The 3-4x Tax on Every Insert

Write Amplification in Postgres: The 3-4x Tax on Every Insert

PostgreSQL Performance

Apr 20, 2026

Every 1 KB insert in Postgres becomes ~2.5 KB of committed I/O before it's done. Here's where the multiplier comes from, and where the tuning knobs run out.

Read more

Moving from Row Deletes to Instant Data Retention

Moving from Row Deletes to Instant Data Retention

PostgreSQL PerformancePostgreSQL Tips

Apr 17, 2026

Row-level DELETE generates massive WAL volume and autovacuum backlogs at scale. Learn how partition-based retention drops 90 days of data in milliseconds—no dead tuples, no cron jobs.

Read more

Stay updated with new posts and releases.

Receive the latest technical articles and release notes in your inbox.

Share

Start a free trial