Use the Index, Luke (Postgres)
Purpose
Fix slow Postgres queries with an indexing-first workflow that balances read performance, write overhead, and operational risk.
When to use
Use this skill when:
- Query latency or throughput regresses in Postgres.
- You need concrete index recommendations tied to an
EXPLAIN (ANALYZE, BUFFERS) plan.
- You are tuning filters, joins, ORDER BY, GROUP BY, or pagination.
Do not use this skill for:
- Generic ORM cleanup without query-level evidence.
- Cache-first fixes when SQL execution is the bottleneck.
Required inputs
Ask for:
- Exact SQL with representative bind values.
- Table DDL, row counts, and current indexes/constraints.
EXPLAIN (ANALYZE, BUFFERS) from a production-like environment.
- Read/write mix (SELECT vs INSERT/UPDATE/DELETE rates).
If key inputs are missing, proceed with assumptions but label uncertainty clearly.
Optimization workflow
1) Baseline and bottleneck
- Identify dominant plan cost: scans, sort/hash spill, nested loop amplification, or heap fetches.
- Record baseline metrics: total time, rows, shared/local reads, loops.
2) Predicate sargability review
- Prefer direct column predicates over wrapped expressions.
- Rewrite non-sargable filters (
func(col), arithmetic on column, optional-param OR chains).
- Keep type consistency (no implicit casts on indexed columns).
- Use half-open date/time ranges (
>= start AND < end) for index-friendly filtering.
3) Index design
- Composite index order: equality columns first, then range, then ordering columns.
- Align index order with
ORDER BY direction when avoiding sort is important.
- Use expression indexes when expression predicates are required (for example
lower(email)).
- Use partial indexes for stable hot subsets.
- Prefer one good composite index over multiple single-column indexes for the same query path.
- Consider
INCLUDE columns when index-only access can materially reduce heap visits.
4) Join strategy checks
- For nested loops, ensure an index exists on the inner-side join key.
- Verify join order/selectivity does not explode loop counts.
- For hash joins, reduce input cardinality early with selective filters/indexes.
5) Sorting, grouping, and pagination
- Match index keys to frequent
ORDER BY patterns to avoid explicit sort.
- For high offsets, switch from OFFSET pagination to keyset/seek pagination.
- For GROUP BY-heavy queries, test whether index order can reduce sort work.
6) DML trade-off analysis
- Every added index increases write amplification and storage.
- Keep only indexes that pay for themselves under observed workload.
- Identify redundant/overlapping indexes before adding new ones.
7) Validate and rollout
- Re-run
EXPLAIN (ANALYZE, BUFFERS) and compare against baseline.
- Confirm improvements under realistic concurrency, not only isolated runs.
- For large tables, prefer
CREATE INDEX CONCURRENTLY and plan cleanup (DROP INDEX CONCURRENTLY) after verification.
Output format
Return:
- Root-cause diagnosis from the plan.
- Proposed SQL rewrite(s), if needed.
- Proposed index change(s) with exact DDL.
- Expected impact, risks, and write-cost trade-offs.
- Validation plan and rollback notes.
Postgres command quick reference
EXPLAIN (ANALYZE, BUFFERS) <query>;
CREATE INDEX CONCURRENTLY idx_name ON table_name (...);
DROP INDEX CONCURRENTLY idx_name;
ANALYZE table_name;
Reference
1---2name: use-the-index-luke3description: Postgres query and index optimization workflow based on use-the-index-luke principles. Use for EXPLAIN analysis, index design, predicate rewrites, joins, sorting, and pagination tuning.4---56# Use the Index, Luke (Postgres)78## Purpose910Fix slow Postgres queries with an indexing-first workflow that balances read performance, write overhead, and operational risk.1112## When to use1314Use this skill when:1516- Query latency or throughput regresses in Postgres.17- You need concrete index recommendations tied to an `EXPLAIN (ANALYZE, BUFFERS)` plan.18- You are tuning filters, joins, ORDER BY, GROUP BY, or pagination.1920Do not use this skill for:2122- Generic ORM cleanup without query-level evidence.23- Cache-first fixes when SQL execution is the bottleneck.2425## Required inputs2627Ask for:2829- Exact SQL with representative bind values.30- Table DDL, row counts, and current indexes/constraints.31- `EXPLAIN (ANALYZE, BUFFERS)` from a production-like environment.32- Read/write mix (SELECT vs INSERT/UPDATE/DELETE rates).3334If key inputs are missing, proceed with assumptions but label uncertainty clearly.3536## Optimization workflow3738### 1) Baseline and bottleneck3940- Identify dominant plan cost: scans, sort/hash spill, nested loop amplification, or heap fetches.41- Record baseline metrics: total time, rows, shared/local reads, loops.4243### 2) Predicate sargability review4445- Prefer direct column predicates over wrapped expressions.46- Rewrite non-sargable filters (`func(col)`, arithmetic on column, optional-param OR chains).47- Keep type consistency (no implicit casts on indexed columns).48- Use half-open date/time ranges (`>= start AND < end`) for index-friendly filtering.4950### 3) Index design5152- Composite index order: equality columns first, then range, then ordering columns.53- Align index order with `ORDER BY` direction when avoiding sort is important.54- Use expression indexes when expression predicates are required (for example `lower(email)`).55- Use partial indexes for stable hot subsets.56- Prefer one good composite index over multiple single-column indexes for the same query path.57- Consider `INCLUDE` columns when index-only access can materially reduce heap visits.5859### 4) Join strategy checks6061- For nested loops, ensure an index exists on the inner-side join key.62- Verify join order/selectivity does not explode loop counts.63- For hash joins, reduce input cardinality early with selective filters/indexes.6465### 5) Sorting, grouping, and pagination6667- Match index keys to frequent `ORDER BY` patterns to avoid explicit sort.68- For high offsets, switch from OFFSET pagination to keyset/seek pagination.69- For GROUP BY-heavy queries, test whether index order can reduce sort work.7071### 6) DML trade-off analysis7273- Every added index increases write amplification and storage.74- Keep only indexes that pay for themselves under observed workload.75- Identify redundant/overlapping indexes before adding new ones.7677### 7) Validate and rollout7879- Re-run `EXPLAIN (ANALYZE, BUFFERS)` and compare against baseline.80- Confirm improvements under realistic concurrency, not only isolated runs.81- For large tables, prefer `CREATE INDEX CONCURRENTLY` and plan cleanup (`DROP INDEX CONCURRENTLY`) after verification.8283## Output format8485Return:8687- Root-cause diagnosis from the plan.88- Proposed SQL rewrite(s), if needed.89- Proposed index change(s) with exact DDL.90- Expected impact, risks, and write-cost trade-offs.91- Validation plan and rollback notes.9293## Postgres command quick reference9495- `EXPLAIN (ANALYZE, BUFFERS) <query>;`96- `CREATE INDEX CONCURRENTLY idx_name ON table_name (...);`97- `DROP INDEX CONCURRENTLY idx_name;`98- `ANALYZE table_name;`99100## Reference101102- [Use The Index, Luke](https://use-the-index-luke.com/)