Here are some SQLs you can use to query or check table and index access statistics from postgreSQL buffer, the SQLs all have been tested on pg9.1, pg9.2, pg9.4 and pg9.4

 

#1 table access statistics

#select schemaname,relname,seq_scan,idx_scan,cast(idx_scan as numeric) / (idx_scan + seq_scan)
as idx_scan_pct
from pg_stat_user_tables where (idx_scan +seq_scan) >0 order by idx_scan_pct;

 schemaname |       relname        | seq_scan | idx_scan |      idx_scan_pct
------------+----------------------+----------+----------+------------------------
 public     | t_inodes_data        |     1744 |    13728 | 0.88728024819027921406
 public     | t_acl                |    12010 |   223622 | 0.94903069192639369865
 public     | t_locationinfo_trash |      474 |    17666 | 0.97386990077177508269
 public     | t_inodes_checksum    |        4 |   164706 | 0.99997571489284196467
 public     | t_tags_inodes        |        4 |   258982 | 0.99998455514969921154
 public     | t_inodes             |       18 |  1998645 | 0.99999099397947527922
 public     | t_tags               |        6 |   705392 | 0.99999149416357857550
 public     | t_dirs               |        9 |  1381699 | 0.99999348632272520677


Higher pct means more likely your postgreSQL is using index scan, which is good.

#2 table io statistics

#select relname,cast(heap_blks_hit as numeric) /(heap_blks_hit +heap_blks_read) 
as hit_pct,heap_blks_hit,heap_blks_read from pg_statio_user_tables
where (heap_blks_hit + heap_blks_read) >0 order by hit_pct;

          relname          |        hit_pct         | heap_blks_hit | heap_blks_read
---------------------------+------------------------+---------------+----------------
 t_tags                    | 0.55777244426352788178 |       1688134 |        1338430
 t_locationinfo_trash      | 0.97145105479088331777 |       4536793 |         133327
 t_tags_inodes             | 0.98326549714237091033 |        277332 |           4720
 t_inodes_data             | 0.99315849486887115165 |          1742 |             12

Higher hit_pct means more likely the data required is cached.

#3 index access statistics

this shows all of the disk i/o for every index on each table

#select relname,cast(idx_blks_hit as numeric) /(idx_blks_hit + idx_blks_read )
as hit_pct,idx_blks_hit,idx_blks_read from pg_statio_user_tables
where (idx_blks_hit +idx_blks_read) >0 order by hit_pct;

       relname        |        hit_pct         | idx_blks_hit | idx_blks_read
----------------------+------------------------+--------------+---------------
 t_storageinfo        | 0.37240811981321526215 |        30784 |         51878
 t_level_1            | 0.96236802970056848744 |        91245 |          3568
 t_inodes             | 0.96443663044941563417 |      8037937 |        296397
 t_level_5            | 0.97766621612923987900 |        30380 |           694
 t_level_4            | 0.97766621612923987900 |        30380 |           694
 t_level_3            | 0.97766621612923987900 |        30380 |           694
 t_level_6            | 0.97766621612923987900 |        30380 |           694
 t_level_7            | 0.97766621612923987900 |        30380 |           694
 t_tags_inodes        | 0.99831073742317177577 |       781268 |          1322

#4 index io statistics

#select indexrelname,cast(idx_blks_hit as numeric) /( idx_blks_hit + idx_blks_read) 
as hit_pct,idx_blks_hit,idx_blks_read from pg_statio_user_indexes
where (idx_blks_hit +idx_blks_read)>0 order by hit_pct ;

       indexrelname        |        hit_pct         | idx_blks_hit | idx_blks_read
---------------------------+------------------------+--------------+---------------
 i_dirs_iparent            | 0.16721455999354324011 |        53867 |        268276
 t_tags_pkey               | 0.93301386188713414472 |      2802699 |        201221
 t_dirs_pkey               | 0.93720418268694208529 |      6482900 |        434376
 t_level_1_pkey            | 0.96236326621027204354 |        91233 |          3568
 t_inodes_pkey             | 0.96443156336186174979 |      8036587 |        296391
 t_level_3_pkey            | 0.97766334084325716125 |        30376 |           694
 t_level_4_pkey            | 0.97766334084325716125 |        30376 |           694
 t_level_5_pkey            | 0.97766334084325716125 |        30376 |           694
 t_level_6_pkey            | 0.97766334084325716125 |        30376 |           694
 t_level_7_pkey            | 0.97766334084325716125 |        30376 |           694
 t_tags_inodes_pkey        | 0.99831054960811651840 |       781181 |          1322

#5 Less used indexes(from top to bottom)

#select schemaname,relname,indexrelname,idx_scan,pg_size_pretty(pg_relation_size(i.indexrelid)) 
as index_size from pg_stat_user_indexes i join pg_index using (indexrelid)
where indisunique is false order by idx_scan,relname;
schemaname |    relname     |      indexrelname      | idx_scan | index_size
------------+----------------+------------------------+----------+------------
 public     | t_dirs         | i_dirs_iparent         |     1466 | 1751 MB
 public     | t_locationinfo | i_locationinfo_ipnfsid |    32328 | 1771 MB
 public     | t_acl          | i_t_acl_rs_id          |    38799 | 887 MB
 public     | t_dirs         | i_dirs_ipnfsid         |    99780 | 1751 MB

Note: The main thing that the counts in pg_stat_user_indexes are useful for is to determining which indexes are actually being used by your application. Since indexes add overhead to the system, but drop them with care.

However, all above statistics only for postgreSQL shared_buffer,  which means not all heap block reads actually turn into physical disk i/o, they might instead read the data from the operating system cache, and postgreSQL doesn't have any idea which read happens. You should be aware of it before you take actions according to above io statistics. See my another article for overall cache of postgreSQL
Postgresql database buffer cache and OS cache

If you want to trace PostgreSQL OS io, then there is tool called 'Dtrace' supports it.