PostgreSQL Scaling Strategies — Quick Reference
Any robust backend depends deeply on the design of it's data layer. In this area of the architecture development, there are two equally important concerns the system architect should care about. First is how is the data modeled, the second is how the data is accessed. In this post we explore the second field, the way the data is accessed in a Postgres database server.
The database design and configuration is one of the first activities of the software production life cycle. However, as in the early stages of software development developers are more focused into delivering a viable product than into solving scalability issues, not much work is invested into designing a scalable data layer.
As the development advance, it's very common that these concerns are left behind until one day, some APIs start running slow for no apparent reason. Then is when the technical debt on scalability takes over. The problem is that at this point, the data model is quite more complex, more tables, more relations, and in addition, probably much more records containing real production data.
A good system architect should envision and keep in mind scalability requirements from the very beginning. Questions like: what's more important for this table, readings or writings? How many request per second we are expected to receive if this new feature is released to production? How much this query return grows with the growth of some table?
Postgres server offers four strategies the system architect can make use of to address scalability. They are not exclusive but they compete. In other words, some of them enhance reading, others enhances writings to the database so, their use will depend on the specific requirements at any point of the architecture.
Strategy | Primary purpose | Scaling type |
Indexing | Accelerate data retrieval | Query optimization |
Partitioning | Organize large tables into smaller physical tables | Data organization |
Replication | Distribute read workloads across servers | Horizontal read scaling |
Sharding | Distribute data and workloads across servers | Horizontal read/write scaling |
1. Indexing
Creates auxiliary data structures (B-tree, GIN, BRIN, etc.) to locate records efficiently.
Pros
Dramatically accelerates selective queries, joins, and sorting.
Can reduce disk I/O.
Relatively simple to implement.
Cons
Slows writes due to index maintenance.
Consumes storage and memory.
Excessive indexing can cause contention.
Use when: Queries frequently filter, join, or sort large datasets.
Avoid when: Tables are tiny, columns are rarely queried, or indexes have little selectivity and provide no other benefit.
2. Partitioning
Divides a logical table into smaller physical tables within PostgreSQL, typically by date, range, or hash.
Pros
Partition pruning improves queries targeting specific partitions.
Smaller indexes and more manageable maintenance.
Efficient historical data deletion by dropping partitions.
Can reduce contention in certain workloads.
Cons
Does not inherently increase total write capacity.
Adds planning, routing, and maintenance complexity.
Queries spanning many partitions may become slower.
Constraints and unique indexes have limitations across partitions.
Use when: Tables are very large, queries naturally filter by a partition key, or retention management is important.
Avoid when: Tables are relatively small, queries regularly access every partition, or there is no clear partition key.
3. Replication
Maintains copies of PostgreSQL data on additional servers, commonly using streaming replication.
Pros
Increases aggregate read throughput.
Offloads analytical or expensive read queries.
Supports high availability and disaster recovery.
Can indirectly free primary resources for writes.
Cons
Adds WAL transmission and replication overhead.
Does not directly distribute write operations.
Asynchronous replicas can return stale data.
Synchronous replication can increase commit latency.
Requires monitoring, failover management, and additional infrastructure.
Use when: Read workloads saturate the primary, or high availability is required.
Avoid when: The system is primarily write-bound, infrastructure cost is unjustified, or strict read-after-write consistency cannot be accommodated.
4. Sharding
Distributes subsets of data across independent PostgreSQL instances, usually using a distribution key.
Pros
Horizontally scales write and read capacity.
Distributes CPU, memory, storage, and I/O demands.
Can support datasets exceeding one server's capacity.
Enables workload isolation.
Cons
Distributed joins and transactions become more complicated.
Cross-shard queries introduce network overhead.
Shard-key selection is critical.
Rebalancing data can be expensive.
Higher operational complexity.
Use when: A single PostgreSQL instance cannot handle the workload despite reasonable optimization and vertical scaling.
Avoid when: The workload fits comfortably on one server, relationships require frequent cross-shard joins, or the team cannot justify distributed-system complexity.
5. Side-by-side comparison
Capability | Indexing | Partitioning | Replicas | Sharding |
Faster selective reads | Excellent | Conditional | More capacity | More capacity |
Higher write throughput | Usually negative | Conditional | Indirect at best | Yes |
More total storage capacity | No | No* | No* | Yes |
Read workload distribution | No | Limited | Yes | Yes |
Query transparency | Yes | Yes | Routing needed | Middleware needed |
Operational complexity | Low | Medium | Medium | High |
Distributed joins | N/A | Not needed | N/A | Potentially costly |
*Partitioning and replication may use additional storage, but they do not inherently increase the logical capacity of a single PostgreSQL primary.
6. Recommended optimization order
1 - Indexing & query optimization
Fix inefficient queries and access paths
2 - Partitioning
Manage large tables and retention
3 - Read replicas
Scale read concurrency and improve availability
4 - Sharding
Scale beyond one primary's capacity
This is a useful general progression, not a mandatory sequence. For example, replication may be needed early for high availability, and partitioning may never be necessary.
Bottom line: Indexing optimizes how rows are found. Partitioning optimizes how data is organized. Replication increases how many reads can be served. Sharding increases how much workload can be distributed across independent servers.

Comments