Indexes are useful, but they’re not free. Unused indexes are a waste. Use the following SQL to identify unused indexes:
First, exclude indexes used to implement constraints (can’t be dropped)
Expression indexes (containing field 0 in pg_index.indkey)
Then find indexes with zero index scans (you can also use a more lenient condition, such as fewer than 1000 scans)
Finding Unused Indexes
View name: monitor.v_bloat_indexes
Calculation time: 1 second, suitable for daily/manual checks, not suitable for frequent polling
Verified versions: 9.3 ~ 10
Function: Shows current database index bloat situation
Works well on versions 9.3 and 10.4. View definition:
-- CREATE SCHEMA IF NOT EXISTS monitor;
-- DROP VIEW IF EXISTS monitor.pg_stat_dummy_indexes;
CREATEORREPLACEVIEWmonitor.pg_stat_dummy_indexesASSELECTs.schemaname,s.relnameAStablename,s.indexrelnameASindexname,pg_relation_size(s.indexrelid)ASindex_sizeFROMpg_catalog.pg_stat_user_indexessJOINpg_catalog.pg_indexiONs.indexrelid=i.indexrelidWHEREs.idx_scan=0-- has never been scanned
AND0<>ALL(i.indkey)-- no index column is an expression
ANDNOTEXISTS-- does not enforce a constraint
(SELECT1FROMpg_catalog.pg_constraintcWHEREc.conindid=s.indexrelid)ORDERBYpg_relation_size(s.indexrelid)DESC;COMMENTONVIEWmonitor.pg_stat_dummy_indexesIS'monitor unused indexes'
-- Human-readable manual query
SELECTs.schemaname,s.relnameAStablename,s.indexrelnameASindexname,pg_size_pretty(pg_relation_size(s.indexrelid))ASindex_sizeFROMpg_catalog.pg_stat_user_indexessJOINpg_catalog.pg_indexiONs.indexrelid=i.indexrelidWHEREs.idx_scan=0-- has never been scanned
AND0<>ALL(i.indkey)-- no index column is an expression
ANDNOTEXISTS-- does not enforce a constraint
(SELECT1FROMpg_catalog.pg_constraintcWHEREc.conindid=s.indexrelid)ORDERBYpg_relation_size(s.indexrelid)DESC;
Batch Generate Index Drop Commands
SELECT'DROP INDEX CONCURRENTLY IF EXISTS "'||s.schemaname||'"."'||s.indexrelname||'";'FROMpg_catalog.pg_stat_user_indexessJOINpg_catalog.pg_indexiONs.indexrelid=i.indexrelidWHEREs.idx_scan=0-- has never been scanned
AND0<>ALL(i.indkey)-- no index column is an expression
ANDNOTEXISTS-- does not enforce a constraint
(SELECT1FROMpg_catalog.pg_constraintcWHEREc.conindid=s.indexrelid)ORDERBYpg_relation_size(s.indexrelid)DESC;
Finding Duplicate Indexes
Check if there are indexes working on the same columns of the same table, but be careful with partial indexes.