2014-02-23 2 views
1

3 백만 행과 1.3GB 크기의 테이블이 있습니다. 4GB RAM이 장착 된 내 노트북에서 Postgres 9.3을 실행합니다.Postgres의 느린 쿼리 (느린 인덱스 스캔)

explain analyze 
select act_owner_id from cnt_contacts where act_owner_id = 2 

내가 cnt_contacts.act_owner_id에 BTREE 키로 정의 : 쿼리는 왜 이렇게 오래 걸리는 약 5 초

 
Bitmap Heap Scan on cnt_contacts (cost=2598.79..86290.73 rows=6208 width=4) (actual time=5865.617..5875.302 rows=5444 loops=1) 
    Recheck Cond: (act_owner_id = 2) 
    -> Bitmap Index Scan on cnt_contacts_idx_act_owner_id (cost=0.00..2597.24 rows=6208 width=0) (actual time=5865.407..5865.407 rows=5444 loops=1) 
     Index Cond: (act_owner_id = 2) 
Total runtime: 5875.684 ms" 
에서 실행

CREATE INDEX cnt_contacts_idx_act_owner_id 
    ON public.cnt_contacts USING btree (act_owner_id, status_id); 

?

work_mem = 1024MB; 
shared_buffers = 128MB; 
effective_cache_size = 1024MB 
seq_page_cost = 1.0   # measured on an arbitrary scale 
random_page_cost = 15.0   # same scale as above 
cpu_tuple_cost = 3.0 
+0

'cnt_contacts_idx_act_owner_id' 색인의 정의는 무엇입니까? –

+0

CREATE INDEX cnt_contacts_idx_act_owner_id ON public.cnt_contacts USING btree (act_owner_id, status_id); –

+0

'act_owner_id'를 가지고있는 다른 색인을 만들어야합니다. – frlan

답변

2

랩탑의 1.3GB 테이블에 5444 개의 레코드가 분산되어 있습니다. 얼마나 오래 걸릴까요? 을 기대합니까?

인덱스가 캐시에 유지 될 수 없거나 인덱스를 처음 사용했기 때문에 색인이 캐시되지 않은 것처럼 보입니다. 동일한 쿼리를 반복적으로 실행하면 어떻게됩니까? 동일한 쿼리이지만 다른 상수를 사용합니까?

"explain (analyze, buffers)"아래에서 쿼리를 실행하면 특히 track_io_timing을 먼저 켠 경우 추가 정보를 얻는 데 도움이됩니다.

0

좋아, 당신은 큰 테이블, 색인 및 PG에 대한 평생 동안 긴 실행을했습니다. 계획을 개선하고 시간을 단축하는 방법에 대해 생각하게합니다. 행을 쓰고 제거합니다. PG 쓰기 및 제거 튜플 및 테이블과 인덱스가 비대화 수 있습니다. 좋은 검색을 위해 PG는 공유 버퍼에 인덱스를로드합니다. 그리고 색인을 가능한 한 깨끗하게 유지해야합니다. 선택을 위해 PG가 공유 버퍼로 검색하고 검색보다. 버퍼 메모리를 설정하고 인덱스 및 테이블 블로 팅을 줄이고 db를 정리하십시오.

당신이 할과 생각 :

1) 그냥 인덱스 중복을 확인하고 인덱스가 좋은 선택이 있음 :

WITH table_scans as (
    SELECT relid, 
     tables.idx_scan + tables.seq_scan as all_scans, 
     (tables.n_tup_ins + tables.n_tup_upd + tables.n_tup_del) as writes, 
       pg_relation_size(relid) as table_size 
     FROM pg_stat_user_tables as tables 
), 
all_writes as (
    SELECT sum(writes) as total_writes 
    FROM table_scans 
), 
indexes as (
    SELECT idx_stat.relid, idx_stat.indexrelid, 
     idx_stat.schemaname, idx_stat.relname as tablename, 
     idx_stat.indexrelname as indexname, 
     idx_stat.idx_scan, 
     pg_relation_size(idx_stat.indexrelid) as index_bytes, 
     indexdef ~* 'USING btree' AS idx_is_btree 
    FROM pg_stat_user_indexes as idx_stat 
     JOIN pg_index 
      USING (indexrelid) 
     JOIN pg_indexes as indexes 
      ON idx_stat.schemaname = indexes.schemaname 
       AND idx_stat.relname = indexes.tablename 
       AND idx_stat.indexrelname = indexes.indexname 
    WHERE pg_index.indisunique = FALSE 
), 
index_ratios AS (
SELECT schemaname, tablename, indexname, 
    idx_scan, all_scans, 
    round((CASE WHEN all_scans = 0 THEN 0.0::NUMERIC 
     ELSE idx_scan::NUMERIC/all_scans * 100 END),2) as index_scan_pct, 
    writes, 
    round((CASE WHEN writes = 0 THEN idx_scan::NUMERIC ELSE idx_scan::NUMERIC/writes END),2) 
     as scans_per_write, 
    pg_size_pretty(index_bytes) as index_size, 
    pg_size_pretty(table_size) as table_size, 
    idx_is_btree, index_bytes 
    FROM indexes 
    JOIN table_scans 
    USING (relid) 
), 
index_groups AS (
SELECT 'Never Used Indexes' as reason, *, 1 as grp 
FROM index_ratios 
WHERE 
    idx_scan = 0 
    and idx_is_btree 
UNION ALL 
SELECT 'Low Scans, High Writes' as reason, *, 2 as grp 
FROM index_ratios 
WHERE 
    scans_per_write <= 1 
    and index_scan_pct < 10 
    and idx_scan > 0 
    and writes > 100 
    and idx_is_btree 
UNION ALL 
SELECT 'Seldom Used Large Indexes' as reason, *, 3 as grp 
FROM index_ratios 
WHERE 
    index_scan_pct < 5 
    and scans_per_write > 1 
    and idx_scan > 0 
    and idx_is_btree 
    and index_bytes > 100000000 
UNION ALL 
SELECT 'High-Write Large Non-Btree' as reason, index_ratios.*, 4 as grp 
FROM index_ratios, all_writes 
WHERE 
    (writes::NUMERIC/(total_writes + 1)) > 0.02 
    AND NOT idx_is_btree 
    AND index_bytes > 100000000 
ORDER BY grp, index_bytes DESC) 
SELECT reason, schemaname, tablename, indexname, 
    index_scan_pct, scans_per_write, index_size, table_size 
FROM index_groups; 

2) 테이블과 인덱스 팽만감이있을 경우 확인을?

 SELECT 
     current_database(), schemaname, tablename, /*reltuples::bigint, relpages::bigint, otta,*/ 
     ROUND((CASE WHEN otta=0 THEN 0.0 ELSE sml.relpages::FLOAT/otta END)::NUMERIC,1) AS tbloat, 
     CASE WHEN relpages < otta THEN 0 ELSE bs*(sml.relpages-otta)::BIGINT END AS wastedbytes, 
     iname, /*ituples::bigint, ipages::bigint, iotta,*/ 
     ROUND((CASE WHEN iotta=0 OR ipages=0 THEN 0.0 ELSE ipages::FLOAT/iotta END)::NUMERIC,1) AS ibloat, 
     CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta) END AS wastedibytes 
    FROM (
     SELECT 
     schemaname, tablename, cc.reltuples, cc.relpages, bs, 
     CEIL((cc.reltuples*((datahdr+ma- 
      (CASE WHEN datahdr%ma=0 THEN ma ELSE datahdr%ma END))+nullhdr2+4))/(bs-20::FLOAT)) AS otta, 
     COALESCE(c2.relname,'?') AS iname, COALESCE(c2.reltuples,0) AS ituples, COALESCE(c2.relpages,0) AS ipages, 
     COALESCE(CEIL((c2.reltuples*(datahdr-12))/(bs-20::FLOAT)),0) AS iotta -- very rough approximation, assumes all cols 
     FROM (
     SELECT 
      ma,bs,schemaname,tablename, 
      (datawidth+(hdr+ma-(CASE WHEN hdr%ma=0 THEN ma ELSE hdr%ma END)))::NUMERIC AS datahdr, 
      (maxfracsum*(nullhdr+ma-(CASE WHEN nullhdr%ma=0 THEN ma ELSE nullhdr%ma END))) AS nullhdr2 
     FROM (
      SELECT 
      schemaname, tablename, hdr, ma, bs, 
      SUM((1-null_frac)*avg_width) AS datawidth, 
      MAX(null_frac) AS maxfracsum, 
      hdr+(
       SELECT 1+COUNT(*)/8 
       FROM pg_stats s2 
       WHERE null_frac<>0 AND s2.schemaname = s.schemaname AND s2.tablename = s.tablename 
      ) AS nullhdr 
      FROM pg_stats s, (
      SELECT 
       (SELECT current_setting('block_size')::NUMERIC) AS bs, 
       CASE WHEN SUBSTRING(v,12,3) IN ('8.0','8.1','8.2') THEN 27 ELSE 23 END AS hdr, 
       CASE WHEN v ~ 'mingw32' THEN 8 ELSE 4 END AS ma 
      FROM (SELECT version() AS v) AS foo 
     ) AS constants 
      GROUP BY 1,2,3,4,5 
     ) AS foo 
    ) AS rs 
     JOIN pg_class cc ON cc.relname = rs.tablename 
     JOIN pg_namespace nn ON cc.relnamespace = nn.oid AND nn.nspname = rs.schemaname AND nn.nspname <> 'information_schema' 
     LEFT JOIN pg_index i ON indrelid = cc.oid 
     LEFT JOIN pg_class c2 ON c2.oid = i.indexrelid 
    ) AS sml 
    ORDER BY wastedbytes DESC 

3) 사용하지 않은 튜플을 하드 디스크에서 닦으시겠습니까? 진공을위한 시간인가?

SELECT 
    relname AS TableName 
    ,n_live_tup AS LiveTuples 
    ,n_dead_tup AS DeadTuples 
FROM pg_stat_user_tables; 

4) 생각해보십시오. db에 10 개의 레코드가 있고 10의 8 개가 id = 2 인 경우 색인의 선택도가 좋지 않으므로 PG는 8 개의 레코드를 모두 검색합니다. 그러나 당신은 id를 사용하려고 시도합니다! = 2 색인은 잘 작동합니다. 좋은 선택으로 색인을 설정하십시오.

5) 올바른 열 유형을 사용하여 데이터를 가져옵니다. 당신이 당신을 위해 더 적은 kb 타입을 사용할 수 있다면, 그것을 변환하십시오.

6) DB와 상태를 확인하십시오. 시작을 확인하십시오 page 테이블에 사용되지 않은 데이터가 데이터베이스에 있음을 확인하고 색인을 정리해야하며 색인에 대한 선택성을 확인하십시오. 데이터에 다른 브린 색인을 사용해보십시오. 색인을 다시 작성하십시오.