Database Denormalization Strategies
Denormalization techniques that deliberately break normalization to raise read performance, and the strategies for managing data integrity
Contents
A design trade-off: deliberately break normalization to raise read performance, and take on the burden of managing data integrity in return.
What denormalization is
Denormalization is a design technique that intentionally breaks normalization and permits duplicate data in order to improve read performance.
Normalization minimizes data duplication and update anomalies, but when business requirements call for trading integrity guarantees for performance, denormalization is applied.
Why denormalize
Performance
-- normalized structure: requires a 3-table JOIN
SELECT o.order_id, c.name, p.product_name
FROM orders o
JOIN customers c ON o.customer_id = c.id
JOIN products p ON o.product_id = p.id;
-- after denormalization: a single-table query
SELECT order_id, customer_name, product_name
FROM orders_denormalized;Simpler development
Collapsing complex multi-way joins keeps both SQL and application logic concise.
Optimizing OLAP and BI environments
Online analytical processing (OLAP) and business intelligence (BI) tools scan large volumes of data repeatedly.
Removing the cost of repeatedly joining several tables in an analytics tool shortens analysis time.
The downsides
| Downside | Description |
|---|---|
| Data inconsistency | Risk of divergence when duplicated data is updated |
| Higher write cost | INSERT/UPDATE/DELETE touch more tables |
| Structural complexity | Hard to track where duplication is maintained |
| Maintenance burden | The design intent becomes hard to recover over time |
Denormalization techniques
Adding duplicate attributes
-- store the customer name redundantly on the orders table
ALTER TABLE orders ADD COLUMN customer_name VARCHAR(100);
-- upside: query directly with no JOIN
-- downside: changing a customer name also requires updating ordersStoring aggregate results
-- daily sales aggregate table
CREATE TABLE daily_sales_summary (
date DATE PRIMARY KEY,
total_sales DECIMAL(15, 2),
order_count INT,
avg_order_value DECIMAL(10, 2)
);
-- query precomputed results instead of aggregating in real timeMaterialized views
-- PostgreSQL
CREATE MATERIALIZED VIEW order_summary AS
SELECT
date_trunc('day', order_date) as day,
SUM(amount) as total,
COUNT(*) as count
FROM orders
GROUP BY 1;
-- periodic refresh
REFRESH MATERIALIZED VIEW order_summary;A MATERIALIZED VIEW stores aggregate results on disk like a real table. Changes to the underlying orders table are not reflected automatically, so REFRESH must run periodically to recompute the current state.
Replicating tables
Replicating frequently used reference data across several databases or shards cuts remote lookups, which reduces network traffic and latency together. Each replica needs its own synchronization path, so this suits data that changes infrequently.
Stepping down a normal form
Deliberately dropping from Boyce-Codd normal form (BCNF) to third normal form (3NF) puts more attributes in one table and reduces the number of joins. Update anomalies become possible in exchange, so apply this only to attributes that rarely change.
What to consider before applying it
Analyze the access pattern
- Read-heavy (99% reads, 1% writes): denormalization is a good fit
- Write-heavy (50% reads, 50% writes): evaluate denormalization carefully
Set explicit performance targets
- Quantify the target query response time
- Run adequate benchmark tests
- Avoid denormalizing unconditionally
Choose a data synchronization strategy
| Approach | Upside | Downside |
|---|---|---|
| Triggers | Real-time synchronization | Higher database load |
| Batch jobs | Spreads database load | Temporary inconsistency |
| Application logic | Flexible | More complex code |
Check these first
Things to try before deciding to denormalize:
- Analyze the execution plan (EXPLAIN)
- Optimize indexes
- Introduce caching (Redis, Memcached)
- Tune the queries
Consider denormalization only when these are not enough.
Summary
| Environment | Recommendation |
|---|---|
| OLAP, BI, data marts | High |
| Read-heavy web services | Medium |
| OLTP (online transaction processing) | Low |
Denormalization pays off where read performance is the bottleneck, but you must first decide who absorbs the cost of synchronizing duplicated data, and when. Choose the synchronization path among triggers, batch jobs, and application logic, and measure how long inconsistency is tolerable before adopting it.
Applying it without measuring the read ratio and the synchronization cost only makes the write path heavier. Set the criteria for reverting to indexes and caching in advance, for the case where the synchronization burden outgrows the benefit.