Topic 50 of 52
Clustered Index Physical Sequence
Overview
A clustered index determines the physical order of data storage in the table — rows are physically stored in the order of the index key. Each table can have only ONE clustered index, which is typically the primary key.
Syntax
sql
-- In PostgreSQL: tables are stored as HEAP (no inherent order)
-- Clustered index concept via CLUSTER command
-- Make table data physically ordered by an index
CLUSTER users USING idx_users_created_at;
-- Rows are now physically ordered by created_at
-- SQL Server / MySQL: Primary key IS the clustered index
CREATE TABLE orders (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, -- clustered in MySQL
...
);
-- InnoDB (MySQL): table organized by primary key (clustered)
-- Every secondary index stores PK values, not row pointers
-- PostgreSQL equivalent: using PRIMARY KEY creates a B-tree index
-- Use CLUSTER to physically reorder data
-- Checking cluster info (PostgreSQL)
SELECT tablename, indexname FROM pg_stat_user_tables
JOIN pg_indexes USING (tablename) WHERE indexname IS NOT NULL;Common Pitfalls
- PostgreSQL's CLUSTER is a one-time operation — new inserts don't maintain physical ordering. Re-run CLUSTER periodically.
- In MySQL/InnoDB, the primary key IS the clustered index — choose it wisely (auto-increment is better than UUID for clustered index performance).
- Interview tip: Clustered indexes are ideal for range queries (WHERE date BETWEEN ...) because physically adjacent rows are read with sequential I/O (fast).
Real-World Example
Clustered index strategy for time-series data:
example
sql
-- Time-series table: cluster by timestamp for range scan efficiency
CREATE TABLE sensor_readings (
id BIGSERIAL PRIMARY KEY,
sensor_id INT NOT NULL,
value DECIMAL(10,4),
recorded_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Create index on the most common range query column
CREATE INDEX idx_readings_time ON sensor_readings(recorded_at DESC);
-- Cluster the table by time (physical ordering)
CLUSTER sensor_readings USING idx_readings_time;
-- After CLUSTER: range queries read sequential pages = fast!
EXPLAIN ANALYZE
SELECT * FROM sensor_readings
WHERE recorded_at BETWEEN NOW() - INTERVAL '1 hour' AND NOW()
ORDER BY recorded_at DESC;
-- Heap Blocks: hit=100, read=2 → mostly in memory due to sequential layout
-- Regular re-clustering (CLUSTER loses effect as new rows are inserted)
-- Schedule: CLUSTER sensor_readings during low-traffic hours