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

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

May 07, 2026

PostgreSQL

Table of contents

01 Sign 1: Your optimization work is cyclical, not cumulative02 Sign 2: Autovacuum is running constantly on tables with low update rates03 Sign 3: Query performance degrades linearly with data volume04 Sign 4: Index strategy has become a tradeoff negotiation05 Sign 5: Storage costs are growing faster than data value06 The diagnostic07 What this actually means

Five Warning Signs Your Database Needs Different Architecture

Five Warning Signs Your Database Needs Different Architecture

Back to blog

PostgreSQL
Matty Stratton

By Matty Stratton

5 min read

May 07, 2026

Table of contents

01 Sign 1: Your optimization work is cyclical, not cumulative02 Sign 2: Autovacuum is running constantly on tables with low update rates03 Sign 3: Query performance degrades linearly with data volume04 Sign 4: Index strategy has become a tradeoff negotiation05 Sign 5: Storage costs are growing faster than data value06 The diagnostic07 What this actually means

Copy as HTML

Open in ChatGPT

Open in Claude

Open in v0

Every database has bad days. A slow query after a schema change. A spike in replication lag during a traffic surge. An autovacuum job that runs long enough to make you nervous.

Those are tuning problems. They have tuning solutions. A senior engineer can fix them in a day, sometimes in an hour, and the fix holds. You move on.

But there's a different category of symptom. The kind that comes back. The kind where each solution introduces the need for the next solution, where performance degrades not because of a specific bad query but because the workload has grown into territory the database wasn't designed for.

Here's the thing that makes this category different: growth changes the nature of the problem, not just the severity. When your workload is fundamentally append-heavy and analytically queried, you're running it on a storage engine built for point lookups and updates. That mismatch doesn't show up at small scale. It compounds. And eventually no amount of configuration adjusts for it.

I've watched teams burn two quarters optimizing their way around this before realizing the optimization itself was the trap. The five signs below are how you recognize it before that happens.

If you're seeing three or more, the problem isn't your configuration.

Sign 1: Your optimization work is cyclical, not cumulative

Tuning that sticks is cumulative. You add an index, queries get faster, they stay faster. You adjust work_mem, hash joins improve, the improvement is permanent. Done.

The warning sign: you're doing the same work again six months later. You partition the table, and after a while you need to re-partition because the data distribution shifted. You optimize a query, and three months later the same query is slow again because the table grew 40%. You upgrade the instance, and the headroom is gone in a quarter.

When optimization is cyclical, the underlying cause is growth, not misconfiguration. Growth doesn't respond to configuration. It responds to architecture.

How to measure: look at your sprint backlog. How many database-related tasks do you have this quarter versus last quarter versus the one before that? If the number is flat or growing, your optimization is running in place.

Sign 2: Autovacuum is running constantly on tables with low update rates

Autovacuum on a heavily updated table makes sense. Dead tuples need cleaning, that's the job.

The warning sign: autovacuum running persistently on tables where fewer than 5% of rows are ever updated. This usually means one of two things: transaction ID wraparound prevention driving vacuum on high-insert tables, or hint bit maintenance generating constant background I/O.

Both are MVCC overhead on append-only data. The visibility tracking machinery runs at full cost on rows that will never be modified. Tuning autovacuum parameters (scale_factor, cost_delay, max_workers) adjusts how the work is distributed, not whether it has to happen. The work still has to happen.

How to measure: check pg_stat_user_tables for your largest tables. Compare n_dead_tup to autovacuum_count. If autovacuum runs frequently but dead tuple counts are consistently low, the vacuum is maintenance overhead, not cleanup. You're paying the full MVCC tax on data that never gets modified.

Sign 3: Query performance degrades linearly with data volume

Postgres query performance should be sublinear with data volume when indexes are working correctly. A B-tree index lookup is O(log n). Adding 10x more data should add a constant factor to indexed lookups, not 10x more time.

The warning sign: queries that get proportionally slower as the table grows. This happens when the dominant pattern is sequential scans or index scans over wide ranges rather than point lookups. Aggregations over time ranges, GROUP BY on large result sets, analytical scans touching millions of rows. These scale with data volume, not with the index.

Partitioning helps by limiting scans to relevant partitions. It's also a manual process that requires ongoing management, and partition-level sequential scans still scale linearly within each partition.

How to measure: run your five slowest queries against progressively larger time ranges. If execution time scales proportionally with the range, the queries are scan-bound. More data will always mean slower queries, and no index is going to change that trajectory.

Sign 4: Index strategy has become a tradeoff negotiation

In a well-matched architecture, indexes are straightforward. Add them where you query, the read performance benefit outweighs the write cost, everyone's happy.

The warning sign: adding indexes to improve read performance measurably degrades write throughput, and the team is now making deliberate tradeoffs. "We can't add that index, it would slow inserts below our SLA." That conversation is the sign.

This is specific to high-throughput insert workloads. A SaaS app doing 1K inserts/sec can add ten indexes without noticing. A telemetry pipeline doing 100K inserts/sec feels every additional index in write amplification. The math is just different.

When index strategy becomes a negotiation between read and write performance, the workload has outgrown what B-tree indexing on row-oriented storage can serve without tradeoffs. You can't index your way out.

How to measure: benchmark write throughput with your current index set, then with one additional index. If the drop is more than 10-15%, you're in the tradeoff zone. Whoops.

Sign 5: Storage costs are growing faster than data value

Postgres stores data at the row level, uncompressed (unless you're using TOAST for large values). For time-series and event data, raw storage cost is proportional to row count with no automatic lifecycle management built in.

The warning sign: storage costs growing linearly with data volume, and the team having conversations about retention not because old data is worthless, but because storing it is expensive. You archive to cold storage or drop old partitions to manage costs, even though you'd rather keep the data online.

When storage cost forces data lifecycle decisions, the storage model isn't efficient for the data type. Columnar compression (10-20x for typical time-series data) and automatic data tiering change the economics so that keeping years of history queryable is practical rather than a monthly budget conversation.

How to measure: calculate your cost per million rows stored. Compare that number to the analytical value of the data. If you're deleting or archiving data you'd rather be querying, storage cost is constraining your product. That's the sign.

The diagnostic

Count how many of these apply to your system.

0-1: Tuning is the right move. Your workload fits the architecture, specific optimizations will address the symptoms. The usual playbook works.

2-3: You're approaching the boundary. Current optimizations will buy months, not years. Start evaluating architectural options now, while you still have time to choose rather than react.

4-5: The workload has outgrown the architecture. Further optimization has diminishing returns. Migration to a system designed for this workload will be less expensive than continued optimization within 6-12 months. Not a fun conclusion, but better to reach it now than after another year of sprint-backlog churn.

What this actually means

Tuning problems have solutions that stick. Architectural mismatches have solutions that buy time. The goal of this whole exercise is to figure out which one you're dealing with before you've spent another quarter on the latter.

If the diagnostic puts you in the 4-5 range, the answer isn't to abandon Postgres. The answer is to stop running a workload it wasn't designed for on vanilla storage primitives. What you actually need is automatic time-based partitioning, hybrid row/columnar storage for analytical scans, and lifecycle management that doesn't require you to babysit it. You need Postgres extended for this workload, not replaced.

That's what TimescaleDB does. Same database, same SQL, same toolchain your team already knows. Different storage engine underneath.

If you want to go deeper on why vanilla Postgres hits this wall and what the underlying mechanics look like, Understanding Postgres Performance Limits for Analytics on Live Data covers the architecture side in detail. And if some of these signs felt familiar but you're not sure you're fully in the architectural category yet, Six Signs That Postgres Tuning Won't Fix Your Performance Problems has a different angle on the same diagnostic.

Related posts

Why Giant IN Clauses Slow Down Your App

Why Giant IN Clauses Slow Down Your App

PostgreSQL PerformancePostgreSQL

May 15, 2026

Giant `IN` clauses inflate PostgreSQL planning time and spike p99 latency. Learn how `ANY(ARRAY[])` cuts the hidden planning tax and keeps your app fast at scale.

Read more

The True Cost of Database Optimization: Engineering Time

The True Cost of Database Optimization: Engineering Time

DatabasePostgreSQL

May 14, 2026

The true cost of Postgres optimization isn't the cloud bill. It's 12-16 engineer-weeks per year that never show up on a budget report.

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