Database Performance Optimization: A Practical Guide

database performance optimizationquery tuningindexing strategiesdatabase monitoringschema design
Database Performance Optimization: A Practical Guide

Most database performance advice starts with the wrong question. Teams ask which index to add, which query to rewrite, or which configuration parameter to increase before asking what the system is waiting for. That habit produces busywork, unused access paths, avoidable write overhead, and changes that move pressure from the database to storage, memory, connections, or even the surrounding data centre.

Database performance optimization is a diagnostic discipline before it's a tuning exercise. The reliable order is to measure workload shape, identify dominant waits, check infrastructure limits, isolate the highest-cost operations, and then test a narrowly defined change. That order matters whether you run PostgreSQL, MySQL, SQL Server, a managed cloud database, or a public-sector data platform with years of accumulated catalog and governance debt.

Table of Contents

Why Most Database Tuning Efforts Fail

The popular assumption is that a slow database needs more indexes. Sometimes it does. Often, the database is waiting on disk, locks, memory, network delivery, or an exhausted connection budget. An index can't fix a storage device at its limit, and a query rewrite won't release a lock held by another transaction.

That makes the central failure a diagnostic ordering problem. Engineers modify the most visible layer, usually SQL, before proving that SQL is the dominant source of delay. The result is a familiar pattern: an index gets added, read latency improves for one statement, write latency rises across the workload, and the original incident returns during a different traffic shape.

Practical rule: Don't change an access path until you can name the wait, workload, and resource constraint you're trying to change.

Israel's public-data modernisation effort offers a useful historical baseline for this problem. The national open-data programme reported that the number of government databases available through data.gov.il had increased four times compared with the start of its action plan, while another government report recorded 510 datasets, more than double the midterm count. By August 2018, 400 databases from 44 organisations were publicly accessible, according to an English-language State Comptroller report.

Scale alone wasn't the only issue. The same report says that by May 2019, about one third of ministries had only partially completed database mapping or had done no mapping, and about 28% had not prepared the required work plan. Those milestones show why cataloguing, ownership, metadata, and governance belong in a performance conversation. You can't optimise a platform reliably when you don't know which datasets exist, who owns them, how they relate, or whether the operational map is current.

The cost of premature tuning

Premature changes create three kinds of damage:

  • Technical debt: Unused or overlapping indexes consume storage and increase maintenance work. Oracle's Database Performance Tuning Guide recommends monitoring index usage over a representative workload and dropping indexes the application doesn't use.
  • Wasted engineering effort: A team can spend days polishing a query that contributes little total load while a lock queue or storage bottleneck affects every request.
  • Operational risk: An untested index build, memory increase, or plan change can compete with production work and trigger a second incident.

The World Bank's Statistical Performance Indicators give Israel's Pillar 5 data infrastructure score a 0 to 100 scale, with an overall SPI score of 90.3 in 2024. That score is available through the World Bank Israel indicator, but a mature infrastructure score doesn't remove the need for operational discipline. The historical reporting also noted that database mapping hadn't been validated since May 2018 and that some ministries still hadn't published databases on the central portal by May 2019.

The lesson is direct. Runtime tuning sits on top of infrastructure maturity. Teams that need to communicate this kind of systems thinking to non-technical stakeholders can also use a practical growth marketing strategy for startups as a model for connecting technical work to measurable business decisions. The wording and audience differ, but the underlying discipline is similar: define the outcome, collect evidence, and avoid confusing activity with progress.

A diagram comparing incorrect trial-and-error database tuning versus a correct data-driven performance optimization process.

Start With Evidence and Wait Statistics

Before changing SQL, capture a baseline that represents normal and degraded behaviour. Record query latency, execution counts, CPU pressure, storage latency, I/O throughput, lock activity, active connections, memory pressure, and errors. A single average hides queueing and outliers, so retain latency distributions where your monitoring system supports them.

Collect engine-level evidence

For PostgreSQL, pg_stat_statements helps rank statements by total execution time, calls, and mean time. pg_stat_activity exposes active sessions and wait events:

SELECT queryid,
       calls,
       total_exec_time,
       mean_exec_time,
       rows,
       query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;

SELECT pid,
       usename,
       state,
       wait_event_type,
       wait_event,
       query_start,
       query
FROM pg_stat_activity
WHERE state <> 'idle'
ORDER BY query_start;

Use the first query to find workload contribution, not merely the statement with the worst single response. Use the second to distinguish a query actively consuming CPU from one waiting on a lock or another resource.

For MySQL, inspect active sessions and Performance Schema:

SHOW FULL PROCESSLIST;

SELECT EVENT_NAME,
       COUNT_STAR,
       SUM_TIMER_WAIT
FROM performance_schema.events_waits_summary_global_by_event_name
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 20;

The output needs context. A long-running session may be blocked, idle in a transaction, or performing legitimate analytical work. Pair process data with statement history and storage metrics before deciding that the SQL text is at fault.

SQL Server exposes accumulated waits through sys.dm_os_wait_stats:

SELECT TOP (20)
       wait_type,
       wait_time_ms,
       signal_wait_time_ms,
       waiting_tasks_count
FROM sys.dm_os_wait_stats
WHERE wait_type NOT IN (
  'SLEEP_TASK',
  'BROKER_TASK_STOP',
  'BROKER_TO_FLUSH',
  'SQLTRACE_BUFFER_FLUSH'
)
ORDER BY wait_time_ms DESC;

Reset or snapshot cumulative counters in a controlled way, then compare intervals. A raw total from an old uptime period can mislead you.

Interpret the dominant wait

Wait Type Database Root Cause Optimization Category
CPU pressure PostgreSQL, MySQL, SQL Server Expensive operators, excessive concurrency, poor plan choice Query shape, workload management
Data I/O PostgreSQL, MySQL, SQL Server Cache misses, slow storage, large scans Physical design, storage capacity
Lock waits PostgreSQL, MySQL, SQL Server Long transactions, conflicting writes, missing access paths Transaction design, indexing
Log or flush waits PostgreSQL, MySQL, SQL Server Commit pressure, checkpoint or log I/O contention Logging, checkpoint, storage
Network waits Any Large result sets, slow clients, service-to-service latency Result shaping, transport, architecture
Memory pressure Any Oversized per-query allocations, too many sessions, insufficient memory Pooling, query memory, instance sizing

Infrastructure checks belong beside SQL telemetry. Examine disk IOPS saturation, storage latency, available memory, swap activity, network throughput, and connection limits. If a disk is saturated, adding a covering index may shift reads into extra index maintenance. If memory is tight, raising per-query sort memory can turn a slow system into an unstable one.

For cross-layer diagnosis, the CloudCops GmbH latency reduction guide is a useful reminder to treat latency as a path through the whole system, not as a database-only property. Keep the resulting baseline with the change record and link it to your performance testing strategy, so every later comparison uses the same workload definition.

Query Tuning and Indexing That Actually Works

Once evidence points to a query-level bottleneck, read the execution plan before editing SQL. Microsoft's SQL Server query-performance guidance?redirectedfrom=MSDN) describes plan examination as the usual starting point because the plan reveals the operators and access methods chosen by the optimiser. IBM's optimisation guidance supports the same prioritisation principle, focus analysis where the largest performance increases are available.

Find the expensive work

In PostgreSQL, compare estimated and actual plans with EXPLAIN (ANALYZE, BUFFERS). In MySQL, use EXPLAIN ANALYZE where available. In SQL Server, capture the actual execution plan and inspect operator costs, row estimates, warnings, memory grants, and spills.

Look for patterns rather than decorative plan complexity:

  • A sequential scan may be appropriate for a small relation, but suspicious when it reads a large table for a selective predicate.
  • A nested loop can work well when the outer input is small. It becomes dangerous when the optimiser underestimates rows and repeatedly probes a large inner relation.
  • A sort or hash operation that spills to disk indicates a memory or cardinality problem, not automatically a missing index.
  • A large difference between estimated and actual rows often points to stale statistics, skewed distributions, or expressions that prevent useful selectivity estimates.

Rank candidates by total workload cost. In PostgreSQL, pg_stat_statements gives you total_exec_time and calls, which lets you compare a frequently executed moderate query with a rarely executed slow report. Optimising the latter may improve a demo while leaving production load unchanged.

A practical before-and-after comparison should include the plan, total execution time, reads, writes, rows returned, and concurrency conditions. Don't call a change successful because one isolated execution became faster.

A comparison infographic between reactive trial-and-error query tuning and a data-driven, systematic approach to database performance optimization.

Build fewer, better indexes

Index design follows access patterns. Index keys that frequently appear in WHERE predicates or join conditions, as Oracle recommends, but verify that the resulting access path serves a meaningful workload.

Composite index ordering should reflect the predicates and ordering requirements. A query filtering by tenant and status, then ordering by creation time, may benefit from an index beginning with the tenant key, followed by the selective or commonly constrained columns, then the ordering column. The right order depends on actual predicates, cardinality, and whether the query returns enough rows to justify the path.

Covering indexes can avoid heap or table lookups when they contain the filter and projected columns. Use them selectively, because adding every selected column creates wide structures that increase write and storage costs. Partial or filtered indexes are valuable when a small, stable subset of rows receives most reads, such as active records, but their predicate must match the query shape.

The Illinois workload research illustrates why benchmark validation matters. On shared-memory systems, out-of-order execution plus multiple issue produced a 1.5× speedup for OLTP and a 2.6× speedup for DSS over an in-order single-issue baseline, as reported in the University of Illinois research summary. A separate benchmark result reported up to 29% improvement on one TPC-D query and 13% average improvement across five queries, while prefetching alone contributed an average 28% gain across all queries, from the same source. These results don't justify copying a hardware or execution technique blindly. They justify testing representative query classes.

For SQL Server-specific execution-plan review, rewrites, and index decisions, the Ryware SQL Server optimisation service guide provides a practical reference point. The principle remains the same: remove an index only after observing its usage across the workload, including seasonal operations, and measure write-side effects as carefully as read latency.

Schema Design and Caching Strategies

Schema design determines how much work the optimiser must do and how much data the storage layer must touch. Normalisation protects consistency and keeps writes focused, but read-heavy paths may need carefully chosen denormalisation, summary tables, or materialised views. The correct design follows the workload, not a universal preference for either fewer joins or stricter decomposition.

A vertical split separates frequently accessed columns from rarely needed, wide attributes. That can reduce page reads for hot paths, but it adds a join when the complete record is required. Horizontal partitioning separates rows by range, list, or hash. Range partitioning often fits time-oriented data, list partitioning fits known categories, and hash partitioning can distribute keys when no natural range provides balanced access.

Make partitioning earn its complexity

Partitioning helps when the query predicate enables partition pruning. If a request filters by a partition key, the engine can avoid unrelated partitions before it performs deeper access-path work. If application queries omit that key, partitioning may add maintenance and planning complexity without reducing I/O.

Maintenance is part of the design. Teams need procedures for creating future partitions, retiring old data, validating constraints, updating statistics, and monitoring whether pruning still occurs. A partitioned table that loses pruning can become a collection of expensive scans with a more complicated failure mode.

A useful design review asks:

  • Which access pattern is the partitioning scheme serving?
  • Does the application always provide the partition predicate?
  • How will new partitions and archival work run without blocking users?
  • What happens when one partition becomes much hotter than the others?

The University of Illinois has also hosted research on an enhanced template-based B+ tree designed to improve query efficiency and insertion behaviour for both key-range and time-range workloads. That ICDE paper hosted by Illinois reinforces a practical point: access structures should reflect the actual range and time patterns of the workload.

Treat caches as consistency systems

“Add Redis” isn't a caching strategy. Decide which layer owns truth, how stale a value may be, and what happens when an entry disappears.

  • Write-through caching updates the cache as part of the write path. It simplifies reads but can add latency and coordination work.
  • Write-behind caching acknowledges changes before the database receives them. It can absorb bursts, but durability, ordering, and recovery become application responsibilities.
  • TTL-based caching suits data whose freshness window is understood. Tie expiry to volatility rather than choosing one blanket duration.
  • Request coalescing prevents a thundering herd when many requests miss the same key at once.

Read replicas and materialised views may beat an application cache when the result is relational, broadly reused, or expensive to reconstruct. A cache can also amplify load if every miss triggers the same heavy query, if invalidation is incomplete, or if an expiry event causes concurrent rebuilds.

A sound guide to building a strong data foundation is useful during schema reviews because the performance decision is inseparable from ownership, relationships, lifecycle, and data quality. Optimisation that ignores those boundaries usually returns as operational work later.

A diagram comparing normalized database schema design with a denormalized schema using caching to improve performance.

Configuration Tuning and Connection Pooling

Configuration tuning should begin with the smallest set of parameters that explains the measured bottleneck. Large checklists encourage random edits. A better approach is to connect each setting to a resource budget, change one related group at a time, and observe memory, latency, throughput, and waits during a representative workload.

Memory settings need concurrency discipline. PostgreSQL's shared_buffers controls shared page caching, while work_mem applies to individual operations, not to the server as a single pool. A query with several sorts or hash operations can consume that allowance multiple times, and concurrent sessions multiply the exposure. MySQL's innodb_buffer_pool_size serves a similar cache-oriented purpose, but its safe value depends on the operating system, connection count, temporary work, and other processes.

Parameter OLTP Guidance Analytical Guidance Common Mistake
shared_buffers Reserve enough cache for hot pages while protecting process and connection memory Increase only with evidence that cache misses and storage reads dominate Treating a percentage of RAM as a universal answer
work_mem Keep per-operation allocations conservative for many short transactions Increase selectively for proven sort or hash pressure Multiplying the setting by every possible concurrent operator
innodb_buffer_pool_size Protect headroom for connections, temporary work, and the host Allocate for the active analytical working set, then verify memory pressure Assuming a larger pool fixes slow plans
max_connections Keep it aligned with pool capacity and transaction duration Separate reporting capacity or workload classes where appropriate Setting it high to hide application connection leaks
Checkpoint settings Smooth write activity and avoid sudden flush pressure Balance recovery needs against sustained analytical I/O Changing intervals without watching flush and log waits
Autovacuum thresholds Keep dead-row cleanup close to write behaviour Tune heavily modified tables independently Waiting for bloat to become an incident

Connection pooling is often the more effective change for OLTP systems. PgBouncer transaction pooling can reuse a server connection between transactions, reducing backend pressure, but session-specific state, temporary tables, prepared statements, and session-level features may require session pooling. ProxySQL can route MySQL traffic and apply workload-aware rules, but routing logic introduces another control plane that needs testing and observability.

Pool size should reflect database capacity and observed query-duration distributions, not the number of application threads. A large pool doesn't create throughput. It creates more simultaneous work, more contention, and potentially longer queues inside the database.

Checkpoint pressure, autovacuum delay, and oversized max_connections often masquerade as random latency. Fix the workload shape or resource budget first. Only then should you adjust the parameter that the evidence identifies as restrictive.

Observability and Safe Rollout Practices

An optimisation is a hypothesis until production traffic validates it. A faster benchmark query can still worsen p99 latency, increase write amplification, consume memory, or create a new lock pattern under concurrency. Observability turns that uncertainty into a controlled feedback loop.

Track query latency distributions, execution counts, cache effectiveness, active and waiting connections, lock duration, storage latency, I/O waits, CPU, memory, and result-set size. Alert on changes in wait composition, not only on CPU utilisation. A database can show moderate CPU while requests queue behind storage or locks.

Roll out changes as experiments

Use a repeatable rollout sequence:

  1. Record the baseline: Save the plan, workload sample, latency distribution, resource profile, and known failure conditions.
  2. Limit exposure: Use a canary, shadow execution path, or controlled tenant group where the database engine and application architecture permit it.
  3. Compare behaviour: Check throughput, tail latency, waits, memory, storage, locks, and errors before declaring success.
  4. Keep rollback practical: Document how to disable the query path, remove or ignore an index, restore a configuration value, and handle cache warming.
  5. Review delayed effects: Some changes look healthy until bloat, statistics drift, cache churn, or write amplification appears later.

Feature flags can control application query paths and staged index usage, but they need clear ownership and an expiry plan. For larger feature and roadmap changes, a dedicated flag-management system such as NonaConfig can be relevant when teams need controlled rollout decisions outside hard-coded deployment logic. Don't use a flag as a substitute for a rollback plan. A flag controls exposure, not database mechanics.

Production observability should also cover infrastructure economics. Illinois-specific analysis projects that data centres could raise ComEd system costs by $18 billion from 2025 to 2040, increase residential bills by about 8.3%, and account for roughly 30% more annual electricity requirement by 2040, as reported in this Illinois data-centre energy analysis. A separate Illinois filing attributes up to 72% of electricity-demand growth by 2030 to data centres, a projection documented in comments on data centres and efficiency in Illinois.

Those projections make operational economics part of performance engineering. The fastest query plan may demand more memory, higher rack density, additional cooling, or earlier capacity expansion. Observability should expose that resource amplification so teams can choose predictable latency without treating power and infrastructure cost as someone else's problem.

For teams building that feedback loop across application, database, and infrastructure layers, Ryware's observability services describe the kind of monitoring and reliability work needed to validate performance changes safely.


Ryware helps teams diagnose database bottlenecks, review execution plans, tune indexes and connection pools, and connect database performance optimization to reliable cloud infrastructure and observability. Visit Ryware to discuss a measured performance programme built around your workload, operational constraints, and rollback requirements.

Have a project in mind?

Tell us what you're building and we'll help you find the right approach.

Get in touch

© 2026 - Ryware.