Fix bad row estimates with CREATE STATISTICS
The planner assumes columns are independent. When city and postcode are correlated, it multiplies their selectivities, estimates far too few rows, and picks a nested loop that takes minutes.
CREATE STATISTICS addr_stats (dependencies) ON city, postcode FROM addresses;
ANALYZE addresses;Look for plans where the row estimate is off by a factor of ten or more. That gap is nearly always a correlation the planner can't see.
postgresqlperformance