Skip to content

Postgresql disk/cache hit rate per table

with all_tables as (
    select  *
    from (
        select  'all'::text as table_name,
            sum((coalesce(heap_blks_read,0) + coalesce(idx_blks_read,0) + coalesce(toast_blks_read,0) + coalesce(tidx_blks_read,0))) as from_disk,
            sum((coalesce(heap_blks_hit,0) + coalesce(idx_blks_hit,0) + coalesce(toast_blks_hit,0) + coalesce(tidx_blks_hit,0))) as from_cache
        from pg_statio_all_tables  --> change to pg_statio_USER_tables if you want to check only user tables (excluding postgres's own tables)
    ) all_tables_subquery
    where (from_disk + from_cache) > 0 -- discard tables without hits
),
tables as (
    select  *
    from (
        select  relname as table_name,
            ((coalesce(heap_blks_read,0) + coalesce(idx_blks_read,0) + coalesce(toast_blks_read,0) + coalesce(tidx_blks_read,0)) ) as from_disk,
            ((coalesce(heap_blks_hit,0)  + coalesce(idx_blks_hit,0)  + coalesce(toast_blks_hit,0)  + coalesce(tidx_blks_hit,0))  ) as from_cache
        from pg_statio_all_tables --> change to pg_statio_user_tables if you want to check only user tables (excluding postgres's own tables)
        ) tables_subquery
    where (from_disk + from_cache) > 0 -- discard tables without hits
)
select table_.table_name as "table name",
    table_.from_disk as "disk hits",
    round((table_.from_disk::numeric / (table_.from_disk + table_.from_cache)::numeric)*100.0, 2) as "% disk hits",
    round((table_.from_cache::numeric / (table_.from_disk + table_.from_cache)::numeric)*100.0, 2) as "% cache hits",
    (table_.from_disk + table_.from_cache) as "total hits",
    round(((table_.from_disk::numeric / (all_tables_.from_disk)::numeric)) * 100.0, 2) as "% of total disk hits"
from tables as table_
cross join all_tables as all_tables_
order by table_.from_disk desc;