PostgreSQL in 语法 的优化器处理以及如何优化

PostgreSQL in 的优化器处理以及如何优化

在使用数据库的过程中,经常会遇到需要匹配多个值的情况。
通常的写法包括:

-- select * from table where id = any(array);
-- select * from table where id in (values);
-- select * from table where id=x or id=x or ....;
-- select * from table where id in (query);
-- select * from table where id in ( values query );
-- select * from table t1 join (query or values query) t2 on t1.id=t2.id;  

每种写法会产生多种执行计划的可能,如下:

-- select * from table where id = any(array);
  优化器可以使用index scan, bitmap scan, seq scan.  

-- select * from table where id in (values);
  优化器可以使用index scan, bitmap scan, seq scan.  

-- select * from table where id=x or id=x or ....;
  优化器可以使用bitmap scan + BitmapOr, seq scan.  

-- select * from table where id in (query);
  优化器可以使用join (merge,hash,nest).  

-- select * from table where id in ( values query );
  优化器可以使用join (merge,hash,nest).  

-- select * from table t1 join (query or values query) t2 on t1.id=t2.id;
  优化器可以使用join (merge,hash,nest).  

SQL优化策略是尽量减少CPU的运算以及page的扫描数量。

下面针对每种SQL,看看对应的可能的执行计划有什么差别,(使用开关来控制执行计划的选择, 如set enable_indexscan=off)
支持的开关如下:

enable_bitmapscan     enable_hashjoin       enable_indexscan      enable_mergejoin      enable_seqscan        enable_tidscan
enable_hashagg        enable_indexonlyscan  enable_material       enable_nestloop       enable_sort   

开始测试,使用auto_explain输出执行计划:

load 'auto_explain';
set auto_explain.log_analyze =true;
set auto_explain.log_buffers =true;
set auto_explain.log_nested_statements=true;
set auto_explain.log_timing=true;
set auto_explain.log_triggers=true;
set auto_explain.log_verbose=true;
set auto_explain.log_min_duration=0;
set client_min_messages ='log';
set work_mem='8GB';    

测试SQL写法1:

-- select * from table where id = any(array);  

do language plpgsql $$
declare
  v_id int[];
begin
  select array_agg(trunc(random()*100000)) into v_id from generate_series(1,200) t(id);
  perform * from t_in_test where id = any (v_id);
end;
$$;  

优化器选择1 (index scan):
离散扫描,适合小的扫描集。

LOG:  duration: 2.312 ms  plan:
Query Text: SELECT * from t_in_test where id = any (v_id)
Index Scan using t_in_test_pkey on public.t_in_test  (cost=0.43..895.50 rows=200 width=37) (actual time=0.039..2.266 rows=200 loops=1)
  Output: id, info
  Index Cond: (t_in_test.id = ANY ('{50836,73414,41071,45604,...省略部分...,76236}'::integer[]))
  Buffers: shared hit=776
CONTEXT:  SQL statement "SELECT * from t_in_test where id = any (v_id)"
PL/pgSQL function inline_code_block line 6 at PERFORM  

优化器选择2 (bitmap scan):
比index scan多了Recheck的开销,以及按照ctid排序的开销。
适合大的扫描集,排序的目的是减少离散扫描,还可以用到块设备的prefetch。

LOG:  duration: 1.602 ms  plan:
Query Text: SELECT * from t_in_test where id = any (v_id)
Bitmap Heap Scan on public.t_in_test  (cost=888.55..1711.16 rows=200 width=37) (actual time=0.880..1.563 rows=200 loops=1)
  Output: id, info
  Recheck Cond: (t_in_test.id = ANY ('{32635,31123,6282,59640,...省略部分...,87705}'::integer[]))
  Heap Blocks: exact=184
  Buffers: shared hit=784
  ->  Bitmap Index Scan on t_in_test_pkey  (cost=0.00..888.50 rows=200 width=0) (actual time=0.846..0.846 rows=200 loops=1)
        Index Cond: (t_in_test.id = ANY ('{32635,31123,6282,59640,...省略部分...,87705}'::integer[]))
        Buffers: shared hit=600
CONTEXT:  SQL statement "SELECT * from t_in_test where id = any (v_id)"
PL/pgSQL function inline_code_block line 6 at PERFORM  

优化器选择3 (seq scan):
适合非常庞大的扫描集。

LOG:  duration: 19940.394 ms  plan:
Query Text: SELECT * from t_in_test where id = any (v_id)
Seq Scan on public.t_in_test  (cost=0.00..2683354.80 rows=200 width=37) (actual time=4.237..19940.330 rows=199 loops=1)
  Output: id, info
  Filter: (t_in_test.id = ANY ('{45867,72450,95153,86233,63073,11016,56010,47158,...省略部分...,90444}'::integer[]))
  Rows Removed by Filter: 9999801
  Buffers: shared hit=83334
CONTEXT:  SQL statement "SELECT * from t_in_test where id = any (v_id)"
PL/pgSQL function inline_code_block line 6 at PERFORM  

测试SQL写法2:

-- select * from table where id in (values);  

do language plpgsql $$
declare
  v_where text;
begin
  select string_agg(id::text,',') into v_where from (select trunc(random()*100000)::int as id from generate_series(1,200) t(id)) t;
  execute 'select * from t_in_test where id in ('||v_where||')';
end;
$$;  

优化器选择1 (index scan):

LOG:  duration: 0.919 ms  plan:
Query Text: select * from t_in_test where id in (8826,2038,72163,29843,76886,37893,5279,64308,...省略部分...,48126,44868)
Index Scan using t_in_test_pkey on public.t_in_test  (cost=0.43..895.50 rows=200 width=37) (actual time=0.017..0.894 rows=200 loops=1)
  Output: id, info
  Index Cond: (t_in_test.id = ANY ('{8826,2038,72163,29843,76886,37893,5279,64308,7370,80216,...省略部分...,48126,44868}'::integer[]))
  Buffers: shared hit=779
CONTEXT:  SQL statement "select * from t_in_test where id in (8826,2038,72163,29843,76886,37893,5279,64308,7370,80216,...省略部分...,73366,48126,44868)"
PL/pgSQL function inline_code_block line 6 at EXECUTE  

优化器选择2 (bitmap scan):

LOG:  duration: 1.012 ms  plan:
Query Text: select * from t_in_test where id in (17424,80517,35148,38245,93037,...省略部分...,14997,34639,10646)
Bitmap Heap Scan on public.t_in_test  (cost=888.55..1711.16 rows=200 width=37) (actual time=0.657..0.978 rows=200 loops=1)
  Output: id, info
  Recheck Cond: (t_in_test.id = ANY ('{17424,80517,35148,38245,93037,4516,...省略部分...,14997,34639,10646}'::integer[]))
  Heap Blocks: exact=177
  Buffers: shared hit=779
  ->  Bitmap Index Scan on t_in_test_pkey  (cost=0.00..888.50 rows=200 width=0) (actual time=0.629..0.629 rows=200 loops=1)
        Index Cond: (t_in_test.id = ANY ('{17424,80517,35148,38245,93037,4516,27690,...省略部分...,34639,10646}'::integer[]))
        Buffers: shared hit=602
CONTEXT:  SQL statement "select * from t_in_test where id in (17424,80517,35148,38245,93037,4516,27690,48978,11902,...省略部分...,34639,10646)"
PL/pgSQL function inline_code_block line 6 at EXECUTE  

优化器选择3 (seq scan):

LOG:  duration: 19678.014 ms  plan:
Query Text: select * from t_in_test where id in (77056,1340,73056,42536,6862,44702,64810,42774,...省略部分...,24083,11322)
Seq Scan on public.t_in_test  (cost=0.00..2683354.80 rows=200 width=37) (actual time=2.045..19677.975 rows=200 loops=1)
  Output: id, info
  Filter: (t_in_test.id = ANY ('{77056,1340,73056,42536,6862,...省略部分...,24083,11322}'::integer[]))
  Rows Removed by Filter: 9999800
  Buffers: shared hit=83334
CONTEXT:  SQL statement "select * from t_in_test where id in (77056,1340,73056,42536,6862,44702,...省略部分...,24083,11322)"
PL/pgSQL function inline_code_block line 6 at EXECUTE  

测试SQL写法3:

-- select * from table where id=x or id=x or ....;  

do language plpgsql $$
declare
  v_where text := 'id=';
  v int;
begin
  for v in select trunc(random()*100000)::int from generate_series(1,200) t(id)
  loop
    v_where := ' '|| v_where ||v||' or id=';
  end loop;
  v_where := rtrim(v_where,'or id=');
  execute 'select * from t_in_test where '||v_where;
end;
$$;  

优化器选择1 (bitmapindex scan + bitmapor):
使用or的写法,只能选择bitmap index scan,所以不如使用IN的写法。

LOG:  duration: 1.085 ms  plan:
Query Text: select * from t_in_test where                                                                                                                                                                                                         id=29207 or id=69918 or id=4044 or ...省略部分... or id=53009 or id=28015 or id=11763
Bitmap Heap Scan on public.t_in_test  (cost=898.50..1771.11 rows=200 width=37) (actual time=0.754..1.043 rows=200 loops=1)
  Output: id, info
  Recheck Cond: ((t_in_test.id = 29207) OR (t_in_test.id = 69918) OR (t_in_test.id = 4044) OR (t_in_test.id = 65838) OR ...省略部分... OR (t_in_test.id = 28015) OR (t_in_test.id = 11763))
  Heap Blocks: exact=180
  Buffers: shared hit=781
  ->  BitmapOr  (cost=898.50..898.50 rows=200 width=0) (actual time=0.725..0.725 rows=0 loops=1)
        Buffers: shared hit=601
        ->  Bitmap Index Scan on t_in_test_pkey  (cost=0.00..4.44 rows=1 width=0) (actual time=0.020..0.020 rows=1 loops=1)
              Index Cond: (t_in_test.id = 29207)
              Buffers: shared hit=3
        ->  Bitmap Index Scan on t_in_test_pkey  (cost=0.00..4.44 rows=1 width=0) (actual time=0.011..0.011 rows=1 loops=1)
              Index Cond: (t_in_test.id = 69918)
              Buffers: shared hit=3
        .....省略部分
        ->  Bitmap Index Scan on t_in_test_pkey  (cost=0.00..4.44 rows=1 width=0) (actual time=0.004..0.004 rows=1 loops=1)
              Index Cond: (t_in_test.id = 11763)
              Buffers: shared hit=3
CONTEXT:  SQL statement "select * from t_in_test where                                                                                                                                                                                                         id=29207 or id=69918 or id=4044 or ...省略部分... or id=28015 or id=11763"
PL/pgSQL function inline_code_block line 11 at EXECUTE  

优化器选择2 (seq scan):

LOG:  duration: 107484.074 ms  plan:
Query Text: select * from t_in_test where                                                                                                                                                                                                         id=51946 or id=17129 or id=90027 or ...省略部分... or id=22127 or id=62334 or id=11722
Seq Scan on public.t_in_test  (cost=0.00..5183374.80 rows=200 width=37) (actual time=17.394..107483.942 rows=199 loops=1)
  Output: id, info
  Filter: ((t_in_test.id = 51946) OR (t_in_test.id = 17129) OR (t_in_test.id = 90027) OR ...省略部分... OR (t_in_test.id = 62334) OR (t_in_test.id = 11722))
  Rows Removed by Filter: 9999801
  Buffers: shared hit=83334
CONTEXT:  SQL statement "select * from t_in_test where                                                                                                                                                                                                         id=51946 or id=17129 or id=90027 or ...省略部分... or id=62334 or id=11722"
PL/pgSQL function inline_code_block line 11 at EXECUTE  

测试SQL写法4:

-- select * from table where id in (query);  

do language plpgsql $$
declare
begin
  perform * from t_in_test where id in (select trunc(random()*100000)::int as id from generate_series(1,200) t(id));
end;
$$;  

优化器选择1 (nestloop join):
适合小的扫描集,并且其中有一个表的JOIN列是带有主键或唯一约束的。

LOG:  duration: 1.314 ms  plan:
Query Text: SELECT * from t_in_test where id in (select trunc(random()*100000)::int as id from generate_series(1,200) t(id))
Nested Loop  (cost=32.94..1727.00 rows=5000040 width=37) (actual time=0.166..1.226 rows=200 loops=1)
  Output: t_in_test.id, t_in_test.info
  Buffers: shared hit=800
  ->  HashAggregate  (cost=32.50..34.50 rows=200 width=4) (actual time=0.149..0.189 rows=200 loops=1)
        Output: ((trunc((random() * '100000'::double precision)))::integer)
        Group Key: (trunc((random() * '100000'::double precision)))::integer
        ->  Function Scan on pg_catalog.generate_series t  (cost=0.00..20.00 rows=1000 width=0) (actual time=0.033..0.089 rows=200 loops=1)
              Output: (trunc((random() * '100000'::double precision)))::integer
              Function Call: generate_series(1, 200)
  ->  Index Scan using t_in_test_pkey on public.t_in_test  (cost=0.43..8.45 rows=1 width=37) (actual time=0.004..0.005 rows=1 loops=200)
        Output: t_in_test.id, t_in_test.info
        Index Cond: (t_in_test.id = ((trunc((random() * '100000'::double precision)))::integer))
        Buffers: shared hit=800
CONTEXT:  SQL statement "SELECT * from t_in_test where id in (select trunc(random()*100000)::int as id from generate_series(1,200) t(id))"
PL/pgSQL function inline_code_block line 4 at PERFORM  

优化器选择2 (hash join):
适合大的扫描集,同时两个表的JOIN列上面都没有索引的情况。

LOG:  duration: 2454.400 ms  plan:
Query Text: SELECT * from t_in_test where id in (select trunc(random()*100000)::int as id from generate_series(1,200) t(id))
Hash Join  (cost=37.00..220874.10 rows=5000040 width=37) (actual time=0.413..2454.343 rows=200 loops=1)
  Output: t_in_test.id, t_in_test.info
  Hash Cond: (t_in_test.id = ((trunc((random() * '100000'::double precision)))::integer))
  Buffers: shared hit=83334
  ->  Seq Scan on public.t_in_test  (cost=0.00..183334.80 rows=10000080 width=37) (actual time=0.010..1182.626 rows=10000000 loops=1)
        Output: t_in_test.id, t_in_test.info
        Buffers: shared hit=83334
  ->  Hash  (cost=34.50..34.50 rows=200 width=4) (actual time=0.221..0.221 rows=200 loops=1)
        Output: ((trunc((random() * '100000'::double precision)))::integer)
        Buckets: 1024  Batches: 1  Memory Usage: 16kB
        ->  HashAggregate  (cost=32.50..34.50 rows=200 width=4) (actual time=0.149..0.177 rows=200 loops=1)
              Output: ((trunc((random() * '100000'::double precision)))::integer)
              Group Key: (trunc((random() * '100000'::double precision)))::integer
              ->  Function Scan on pg_catalog.generate_series t  (cost=0.00..20.00 rows=1000 width=0) (actual time=0.033..0.088 rows=200 loops=1)
                    Output: (trunc((random() * '100000'::double precision)))::integer
                    Function Call: generate_series(1, 200)
CONTEXT:  SQL statement "SELECT * from t_in_test where id in (select trunc(random()*100000)::int as id from generate_series(1,200) t(id))"
PL/pgSQL function inline_code_block line 4 at PERFORM  

优化器选择3 (merge join):
适合大的扫描集,并且两个表的JOIN列都有索引。

LOG:  duration: 32.551 ms  plan:
Query Text: SELECT * from t_in_test where id in (select trunc(random()*100000)::int as id from generate_series(1,200) t(id))
Merge Join  (cost=42.58..368067.98 rows=5000040 width=37) (actual time=0.561..32.497 rows=200 loops=1)
  Output: t_in_test.id, t_in_test.info
  Merge Cond: (t_in_test.id = ((trunc((random() * '100000'::double precision)))::integer))
  Buffers: shared hit=1112
  ->  Index Scan using t_in_test_pkey on public.t_in_test  (cost=0.43..343022.64 rows=10000080 width=37) (actual time=0.016..20.499 rows=99905 loops=1)
        Output: t_in_test.id, t_in_test.info
        Buffers: shared hit=1108
  ->  Sort  (cost=42.15..42.65 rows=200 width=4) (actual time=0.268..0.296 rows=200 loops=1)
        Output: ((trunc((random() * '100000'::double precision)))::integer)
        Sort Key: ((trunc((random() * '100000'::double precision)))::integer)
        Sort Method: quicksort  Memory: 34kB
        Buffers: shared hit=4
        ->  HashAggregate  (cost=32.50..34.50 rows=200 width=4) (actual time=0.148..0.181 rows=200 loops=1)
              Output: ((trunc((random() * '100000'::double precision)))::integer)
              Group Key: (trunc((random() * '100000'::double precision)))::integer
              ->  Function Scan on pg_catalog.generate_series t  (cost=0.00..20.00 rows=1000 width=0) (actual time=0.035..0.078 rows=200 loops=1)
                    Output: (trunc((random() * '100000'::double precision)))::integer
                    Function Call: generate_series(1, 200)
CONTEXT:  SQL statement "SELECT * from t_in_test where id in (select trunc(random()*100000)::int as id from generate_series(1,200) t(id))"
PL/pgSQL function inline_code_block line 4 at PERFORM  

测试SQL写法5:

-- select * from table where id in ( values query );  

do language plpgsql $$
declare
  v_values text := 'values ( ';
  v int;
begin
  for v in select trunc(random()*100000)::int from generate_series(1,200) t(id)
  loop
    v_values := v_values ||v||'),(';
  end loop;
  v_values := rtrim( v_values,',(' );
  execute 'select * from t_in_test where id in ( select * from ('||v_values||') as t(id))';
end;
$$;  

优化器选择1 (nestloop join):

LOG:  duration: 1.272 ms  plan:
Query Text: select * from t_in_test where id in ( select * from (values ( 96474),(39030),(12481),(60519),...省略部分...,(23783),(9253)) as t(id))
Nested Loop  (cost=3.44..1697.50 rows=5000040 width=37) (actual time=0.130..1.195 rows=200 loops=1)
  Output: t_in_test.id, t_in_test.info
  Buffers: shared hit=802
  ->  HashAggregate  (cost=3.00..5.00 rows=200 width=4) (actual time=0.105..0.143 rows=200 loops=1)
        Output: "*VALUES*".column1
        Group Key: "*VALUES*".column1
        ->  Values Scan on "*VALUES*"  (cost=0.00..2.50 rows=200 width=4) (actual time=0.001..0.040 rows=200 loops=1)
              Output: "*VALUES*".column1
  ->  Index Scan using t_in_test_pkey on public.t_in_test  (cost=0.43..8.45 rows=1 width=37) (actual time=0.004..0.005 rows=1 loops=200)
        Output: t_in_test.id, t_in_test.info
        Index Cond: (t_in_test.id = "*VALUES*".column1)
        Buffers: shared hit=802
CONTEXT:  SQL statement "select * from t_in_test where id in ( select * from (values ( 96474),(39030),(12481),(60519),(70354),(33117),...省略部分...,(15818),(23783),(9253)) as t(id))"
PL/pgSQL function inline_code_block line 11 at EXECUTE  

优化器选择2 (hash join):

LOG:  duration: 2444.648 ms  plan:
Query Text: select * from t_in_test where id in ( select * from (values ( 95286),(76612),(56400),(99838),(2155),...省略部分...,(29527),(99252)) as t(id))
Hash Join  (cost=7.50..220844.60 rows=5000040 width=37) (actual time=0.222..2444.573 rows=200 loops=1)
  Output: t_in_test.id, t_in_test.info
  Hash Cond: (t_in_test.id = "*VALUES*".column1)
  Buffers: shared hit=83334
  ->  Seq Scan on public.t_in_test  (cost=0.00..183334.80 rows=10000080 width=37) (actual time=0.009..1174.724 rows=10000000 loops=1)
        Output: t_in_test.id, t_in_test.info
        Buffers: shared hit=83334
  ->  Hash  (cost=5.00..5.00 rows=200 width=4) (actual time=0.173..0.173 rows=200 loops=1)
        Output: "*VALUES*".column1
        Buckets: 1024  Batches: 1  Memory Usage: 16kB
        ->  HashAggregate  (cost=3.00..5.00 rows=200 width=4) (actual time=0.101..0.135 rows=200 loops=1)
              Output: "*VALUES*".column1
              Group Key: "*VALUES*".column1
              ->  Values Scan on "*VALUES*"  (cost=0.00..2.50 rows=200 width=4) (actual time=0.001..0.042 rows=200 loops=1)
                    Output: "*VALUES*".column1
CONTEXT:  SQL statement "select * from t_in_test where id in ( select * from (values ( 95286),(76612),(56400),...省略部分...,(29527),(99252)) as t(id))"
PL/pgSQL function inline_code_block line 11 at EXECUTE  

优化器选择3 (merge join):

LOG:  duration: 32.296 ms  plan:
Query Text: select * from t_in_test where id in ( select * from (values ( 18704),(70725),(55056),...省略部分...,(80068),(28737)) as t(id))
Merge Semi Join  (cost=10.58..368035.98 rows=5000040 width=37) (actual time=0.560..32.212 rows=200 loops=1)
  Output: t_in_test.id, t_in_test.info
  Merge Cond: (t_in_test.id = "*VALUES*".column1)
  Buffers: shared hit=1110
  ->  Index Scan using t_in_test_pkey on public.t_in_test  (cost=0.43..343022.64 rows=10000080 width=37) (actual time=0.023..20.733 rows=99962 loops=1)
        Output: t_in_test.id, t_in_test.info
        Buffers: shared hit=1110
  ->  Sort  (cost=10.14..10.64 rows=200 width=4) (actual time=0.105..0.134 rows=200 loops=1)
        Output: "*VALUES*".column1
        Sort Key: "*VALUES*".column1
        Sort Method: quicksort  Memory: 34kB
        ->  Values Scan on "*VALUES*"  (cost=0.00..2.50 rows=200 width=4) (actual time=0.002..0.035 rows=200 loops=1)
              Output: "*VALUES*".column1
CONTEXT:  SQL statement "select * from t_in_test where id in ( select * from (values ( 18704),(70725),(55056),...省略部分...,(28737)) as t(id))"
PL/pgSQL function inline_code_block line 11 at EXECUTE  

测试SQL写法6:

-- select * from table t1 join (query or values query) t2 on t1.id=t2.id;  

do language plpgsql $$
declare
begin
  perform * from t_in_test t1 join (select trunc(random()*100000)::int as id from generate_series(1,200) t(id)) t2 on (t1.id=t2.id);
end;
$$;  

优化器选择1 (nestloop join):

LOG:  duration: 1.327 ms  plan:
Query Text: SELECT * from t_in_test t1 join (select trunc(random()*100000)::int as id from generate_series(1,200) t(id)) t2 on (t1.id=t2.id)
Nested Loop  (cost=0.44..8404.50 rows=1000 width=41) (actual time=0.062..1.241 rows=200 loops=1)
  Output: t1.id, t1.info, ((trunc((random() * '100000'::double precision)))::integer)
  Buffers: shared hit=802
  ->  Function Scan on pg_catalog.generate_series t  (cost=0.00..20.00 rows=1000 width=0) (actual time=0.034..0.131 rows=200 loops=1)
        Output: (trunc((random() * '100000'::double precision)))::integer
        Function Call: generate_series(1, 200)
  ->  Index Scan using t_in_test_pkey on public.t_in_test t1  (cost=0.43..8.36 rows=1 width=37) (actual time=0.005..0.005 rows=1 loops=200)
        Output: t1.id, t1.info
        Index Cond: (t1.id = ((trunc((random() * '100000'::double precision)))::integer))
        Buffers: shared hit=802
CONTEXT:  SQL statement "SELECT * from t_in_test t1 join (select trunc(random()*100000)::int as id from generate_series(1,200) t(id)) t2 on (t1.id=t2.id)"
PL/pgSQL function inline_code_block line 4 at PERFORM  

优化器选择2 (hash join):

LOG:  duration: 4883.088 ms  plan:
Query Text: SELECT * from t_in_test t1 join (select trunc(random()*100000)::int as id from generate_series(1,200) t(id)) t2 on (t1.id=t2.id)
Hash Join  (cost=308335.80..308390.80 rows=1000 width=41) (actual time=4882.749..4883.023 rows=200 loops=1)
  Output: t1.id, t1.info, ((trunc((random() * '100000'::double precision)))::integer)
  Hash Cond: (((trunc((random() * '100000'::double precision)))::integer) = t1.id)
  Buffers: shared hit=83334
  ->  Function Scan on pg_catalog.generate_series t  (cost=0.00..20.00 rows=1000 width=0) (actual time=0.033..0.125 rows=200 loops=1)
        Output: (trunc((random() * '100000'::double precision)))::integer
        Function Call: generate_series(1, 200)
  ->  Hash  (cost=183334.80..183334.80 rows=10000080 width=37) (actual time=4767.895..4767.895 rows=10000000 loops=1)
        Output: t1.id, t1.info
        Buckets: 16777216  Batches: 1  Memory Usage: 804901kB
        Buffers: shared hit=83334
        ->  Seq Scan on public.t_in_test t1  (cost=0.00..183334.80 rows=10000080 width=37) (actual time=0.014..1325.338 rows=10000000 loops=1)
              Output: t1.id, t1.info
              Buffers: shared hit=83334
CONTEXT:  SQL statement "SELECT * from t_in_test t1 join (select trunc(random()*100000)::int as id from generate_series(1,200) t(id)) t2 on (t1.id=t2.id)"
PL/pgSQL function inline_code_block line 4 at PERFORM  

优化器选择3 (merge join):

LOG:  duration: 32.505 ms  plan:
Query Text: SELECT * from t_in_test t1 join (select trunc(random()*100000)::int as id from generate_series(1,200) t(id)) t2 on (t1.id=t2.id)
Merge Join  (cost=80.27..368117.67 rows=1000 width=41) (actual time=0.182..32.429 rows=200 loops=1)
  Output: t1.id, t1.info, ((trunc((random() * '100000'::double precision)))::integer)
  Merge Cond: (t1.id = ((trunc((random() * '100000'::double precision)))::integer))
  Buffers: shared hit=1102
  ->  Index Scan using t_in_test_pkey on public.t_in_test t1  (cost=0.43..343022.64 rows=10000080 width=37) (actual time=0.022..20.782 rows=99360 loops=1)
        Output: t1.id, t1.info
        Buffers: shared hit=1102
  ->  Sort  (cost=79.83..82.33 rows=1000 width=4) (actual time=0.154..0.180 rows=200 loops=1)
        Output: ((trunc((random() * '100000'::double precision)))::integer)
        Sort Key: ((trunc((random() * '100000'::double precision)))::integer)
        Sort Method: quicksort  Memory: 34kB
        ->  Function Scan on pg_catalog.generate_series t  (cost=0.00..20.00 rows=1000 width=0) (actual time=0.036..0.099 rows=200 loops=1)
              Output: (trunc((random() * '100000'::double precision)))::integer
              Function Call: generate_series(1, 200)
CONTEXT:  SQL statement "SELECT * from t_in_test t1 join (select trunc(random()*100000)::int as id from generate_series(1,200) t(id)) t2 on (t1.id=t2.id)"
PL/pgSQL function inline_code_block line 4 at PERFORM  

优化选择建议如下,可以根据需求来写SQL,最不建议写的是OR:
index scan:
离散扫描,适合小的扫描集。
bitmap scan:
比index scan多了Recheck的开销,以及按照ctid排序的开销。
适合大的扫描集,排序的目的是减少离散扫描,还可以用到块设备的prefetch。
seq scan:
适合非常庞大的扫描集。
bitmapindex scan + bitmapor|And:
使用or的写法,只能选择bitmap index scan,所以不如使用IN的写法。
nestloop join:
适合小的扫描集,并且其中有一个表的JOIN列是带有主键或唯一约束的。
hash join:
适合大的扫描集,同时两个表的JOIN列上面都没有索引的情况。
merge join:
适合大的扫描集,并且两个表的JOIN列都有索引。

如果你发现写好的SQL没有选择最优的执行计划,可以通过设置优化器开关,或者使用hint plan这个插件来指定优化器使用对应的scan或join method.

http://pghintplan.osdn.jp/pg_hint_plan.html

git clone git://git.osdn.jp/gitroot/pghintplan/pg_hint_plan.git
mv pg_hint_plan postgresql-9.5.0/contrib/
cd postgresql-9.5.0/contrib/pg_hint_plan
export PATH=/home/digoal/pgsql9.5.0/bin:$PATH
make
make install  

psql
postgres=# create extension pg_hint_plan;
CREATE EXTENSION  

postgres=# LOAD 'pg_hint_plan';  

postgres=# /*+ NestLoop(t1 t2) */ explain select * from t_in_test t1 join (select trunc(random()*100000)::int as id from generate_series(1,200) t(id)) t2 on (t1.id=t2.id);
LOG:  duration: 0.000 ms  plan:
Query Text: /*+ NestLoop(t1 t2) */ explain select * from t_in_test t1 join (select trunc(random()*100000)::int as id from generate_series(1,200) t(id)) t2 on (t1.id=t2.id);
Nested Loop  (cost=0.44..8404.50 rows=1000 width=41)
  Output: t1.id, t1.info, ((trunc((random() * '100000'::double precision)))::integer)
  ->  Function Scan on pg_catalog.generate_series t  (cost=0.00..20.00 rows=1000 width=0)
        Output: (trunc((random() * '100000'::double precision)))::integer
        Function Call: generate_series(1, 200)
  ->  Index Scan using t_in_test_pkey on public.t_in_test t1  (cost=0.43..8.36 rows=1 width=37)
        Output: t1.id, t1.info
        Index Cond: (t1.id = ((trunc((random() * '100000'::double precision)))::integer))
                                        QUERY PLAN
------------------------------------------------------------------------------------------
 Nested Loop  (cost=0.44..8404.50 rows=1000 width=41)
   ->  Function Scan on generate_series t  (cost=0.00..20.00 rows=1000 width=0)
   ->  Index Scan using t_in_test_pkey on t_in_test t1  (cost=0.43..8.36 rows=1 width=37)
         Index Cond: (id = ((trunc((random() * '100000'::double precision)))::integer))
(4 rows)  
时间: 2024-08-01 15:27:47

PostgreSQL in 语法 的优化器处理以及如何优化的相关文章

Oracle优化器的两种优化方式

Oracle的优化器有两种优化方式: 基于规则的优化方式:Rule-Based Optimization(RBO) 基于成本或者统计信息的优化方式(Cost-Based Optimization:CBO) RBO方式:优化器在分析SQL语句时,所遵循的是Oracle内部预定的一些规则.比如我们常见的,当一个where子句中的一列有索引时去走索引. CBO方式:CBO是在ORACLE7 引入,但到ORACLE8i 中才成熟.ORACLE 已经声明在ORACLE9i之后的版本中,RBO将不再支持.它

Oracle数据的优化器有两种优化方法:

Oracle数据的优化器有两种优化方法:基于代价/成本的优化器(CBO)和基于规则的优化器(RBO),系统在对SQL进行优化的时候,使用哪种优化决定于初始化参数OPTIMIZER_MODE,该参数值可以设置为:CHOOSE,RULE,ALL_ROWS,FIRST_ROWS.在Oracle9i 以后的版本中还引入了新的优化参数命令:FIRST_ROWS_1,FIRST_ROWS_10, FIRST_ROWS_100,FIRST_ROWS_1000.(具体的说明将在以后的BLOG文章中介绍)Orac

SQL优化器原理-Shuffle优化

这是MaxCompute有关SQL优化器原理的系列文章之一.我们会陆续推出SQL优化器有关优化规则和框架的其他文章.添加钉钉群"关系代数优化技术"(群号11719083)可以获取最新文章发布动态. 本文主要介绍MaxCompute Optimizer对Shuffle方面的相关优化. 1 简介 分布式系统中,Shuffle是重操作之一,直接影响到了SQL运行时的效率.Join.Aggregate等操作符都需要借助Shuffle操作符,确保相同数据分发到同一机器或Instance中,才可以

IBM原厂资深专家:DB2优化器和成本模型分析

  11月17日,IBM资深软件工程师刘俊老师在DB2用户群进行了一次"浅析DB2优化器和成本模型"的线上主题分享.小编特别整理出其中精华内容,供大家学习交流.    嘉宾简介    IBM资深软件工程师 自2005年以来一直从事DB2性能优化的产品研发,包括Visual Explain.Optimization Service Center.Optimization Expert等,在DB2查询优化和性能调优技术上具有多年实践经验 帮助IBM技术支持团队处理客户提交的DB2性能问题,

Oracle 12c数据库优化器统计信息收集的最佳实践

Oracle 12c数据库优化器统计信息收集的最佳实践 转载自     沃趣科技(ID:woqutech)  作者         刘金龙(译) 原文链接   http://www.oracle.com/technetwork/database/bi-datawarehousing/twp-bp-for-stats-gather-12c-1967354.pdf 导  语 Oracle优化器会为SQL语句产生所有可能的访问路径(执行计划),然后从中选择一条COST值最低的执行路径,这个cost值是

Oracle Optimizer:迁移到使用基于成本的优化器-----系列1.2

oracle|优化 Oracle Optimizer:迁移到使用基于成本的优化器-----系列1.2 3.2基于成本的优化器(CBO) 基于成本优化器遵循计算代价的方法学.所有的执行计划随成本标识,优化器选择成本最低的一个.在执行计划中,较高的成本将意味着较高的资源.成本越低,对查询来说越高效.CBO使用所有存储在数据字典中可用的统计资料信息和柱状图,用户提供提示和的参数设置来达成使用的成本,CBO生成所有可能访问方法的排列然后选择最合适的.排列的数量依赖于查询中出现的表数量,有时能达到约80,

ORACLE优化器简介

优化器有时也被称为查询优化器,这是因为查询是影响数据库性能最主要的部分,不要以为只有SELECT语句是查询.实际上,带有任何WHERE条件的DML(INSERT.UPDATE.DELETE)语句中都包含查询要求,在后面的文章中,当说到查询时,不一定只是指SELECT语句,也有可能指DML语句中的查询部分.优化器是所有关系数据库引擎中的最神秘.最富挑战性的部件之一,从性能的角度看也是最重要的部分,它性能的高低直接关系到数据库性能的好坏. 我们知道,SQL语句同其它语言(如C语言)的语句不一样,它是

MaxCompute理解数据、运算和用户的大脑:基于代价的优化器

MaxCompute简介 大数据计算服务(MaxCompute)是一种快速.完全托管的PB/EB级数据仓库解决方案,MaxCompute具备万台服务器扩展能力和跨地域容灾能力,是阿里巴巴内部核心大数据平台,承担了集团内部绝大多数的计算任务,支撑每日百万级作业规模.MaxCompute向用户提供了完善的数据导入方案以及多种经典的分布式计算模型,能够更快速的解决用户海量数据计算问题,有效降低企业成本,并保障数据安全. MaxCompute架构 MaxCompute基本的体系结构如上图所示,最底层就是

MySQL · 答疑解惑 · MySQL 优化器 range 的代价计算

本文我们从一个索引选择的问题出发,来研究一下 MySQL 中 range 代价的计算过程,进而分析这种计算过程中存在的问题. 问题现象 第一种情况:situation_unique_key_id mysql> show create table cpa_order\G *************************** 1. row *************************** Table: cpa_order Create Table: CREATE TABLE `cpa_ord