postgreSQL优化

时间:2012-04-13 12:54:18

标签: performance postgresql query-optimization

我正在开发一个大型的PostgreSQL项目,不幸的是在生产中(在一个包含90列的表中有3百万条记录)在大约2分钟内运行查询(具有2个条件的select / join)。

假设我的查询没有什么可以优化的,我可以修改任何设置以使其运行得更快吗? 这是数据库的配置,我不知道什么适合我的需求:

version PostgreSQL 8.4.4 on i686-pc-linux-gnu, compiled by GCC gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-44), 32-bit
checkpoint_completion_target    0.9
checkpoint_segments 10
custom_variable_classes pg_stat_statements
effective_cache_size    1GB
lc_collate  fr_FR.UTF-8
lc_ctype    fr_FR.UTF-8
listen_addresses    *
log_autovacuum_min_duration 0
log_line_prefix %t [%p]: [%l-1] user=%u,db=%d
log_min_duration_statement  30s
logging_collector   on
maintenance_work_mem    128MB
max_connections 100
max_stack_depth 2MB
pg_stat_statements.max  1000
pg_stat_statements.save on
pg_stat_statements.track    all
random_page_cost    1.5
server_encoding UTF8
shared_buffers  128MB
TimeZone    Europe/Paris
track_functions pl
wal_buffers 1MB
work_mem    8MB

查询:

SELECT distinct
    ((Table_Commande_Historique.COD_STECIAL
    || ',' || Table_Commande_Historique.COD_MCIAL
    || ',' || Table_Commande_Historique.NUM_REC_CLI
    || ',' || Table_Commande_Historique.NUM_DNT_CLI
    || ',' || Table_Commande_Historique.NUM_DDE)) cle
FROM G1DDE2_DDE Table_Commande_Historique
inner join "K2VER2_VER" ver
  on ( Table_Commande_Historique.NUM_REC_CLI
         = (string_to_array(ver.num_cle,','))[3]::int
    OR Table_Commande_Historique.NUM_DNT_CLI
         = (string_to_array(ver.num_cle,','))[3]::int
    OR ver.num_cle = (Table_Commande_Historique.COD_MCIAL
                     || ',' || Table_Commande_Historique.NUM_REC_CLI)
    OR ver.num_cle = (Table_Commande_Historique.COD_MCIAL
                     || ',' || Table_Commande_Historique.NUM_DNT_CLI) );

索引:

CREATE INDEX idx_combo1
  ON g1dde2_dde
  USING btree
  (((cod_mcial || ','::text) || num_rec_cli) );

  CREATE INDEX idx_combo2
  ON g1dde2_dde
  USING btree
  (((cod_mcial || ','::text) || num_dnt_cli) );

  CREATE INDEX idx_dnt
  ON g1dde2_dde
  USING btree
  (num_dnt_cli );

  CREATE INDEX idx_rec
  ON g1dde2_dde
  USING btree
  (num_rec_cli );

  CREATE INDEX idx_k2ver3sb
  ON "K2VER2_VER"
  USING btree
  (num_cle );

说明:

"HashAggregate  (cost=197.97..201.77 rows=69 width=29)"
"  ->  Nested Loop  (cost=1.29..197.35 rows=248 width=29)"
"        ->  Seq Scan on "K2VER2_VER" ver  (cost=0.00..2.58 rows=58 width=19)"
"        ->  Bitmap Heap Scan on g1dde2_dde table_commande_historique  (cost=1.29..2.84 rows=5 width=29)"
"              Recheck Cond: ((table_commande_historique.num_rec_cli = ((string_to_array((ver.num_cle)::text, ','::text))[3])::integer) OR (table_commande_historique.num_dnt_cli = ((string_to_array((ver.num_cle)::text, ','::text))[3])::integer) OR ((ver.num_cle)::text = (((table_commande_historique.cod_mcial)::text || ','::text) || (table_commande_historique.num_rec_cli)::text)) OR ((ver.num_cle)::text = (((table_commande_historique.cod_mcial)::text || ','::text) || (table_commande_historique.num_dnt_cli)::text)))"
"              ->  BitmapOr  (cost=1.29..1.29 rows=5 width=0)"
"                    ->  Bitmap Index Scan on idx_rec  (cost=0.00..0.32 rows=2 width=0)"
"                          Index Cond: (table_commande_historique.num_rec_cli = ((string_to_array((ver.num_cle)::text, ','::text))[3])::integer)"
"                    ->  Bitmap Index Scan on idx_dnt  (cost=0.00..0.32 rows=1 width=0)"
"                          Index Cond: (table_commande_historique.num_dnt_cli = ((string_to_array((ver.num_cle)::text, ','::text))[3])::integer)"
"                    ->  Bitmap Index Scan on idx_combo1  (cost=0.00..0.32 rows=1 width=0)"
"                          Index Cond: ((ver.num_cle)::text = (((table_commande_historique.cod_mcial)::text || ','::text) || (table_commande_historique.num_rec_cli)::text))"
"                    ->  Bitmap Index Scan on idx_combo2  (cost=0.00..0.32 rows=1 width=0)"
"                          Index Cond: ((ver.num_cle)::text = (((table_commande_historique.cod_mcial)::text || ','::text) || (table_commande_historique.num_dnt_cli)::text))"

1 个答案:

答案 0 :(得分:3)

version PostgreSQL 8.4.4

这是一个问题。请阅读此内容并考虑使用次要版本来修复安全漏洞和错误:

http://www.postgresql.org/support/versioning/

这些有时会包含引入性能问题的错误。要查看您将获得哪些修复,您可以在此处查看8.4.5至8.4.11注释:

http://www.postgresql.org/docs/8.4/static/release.html

某些基于RAM的设置可能太低,但不知道系统上有多少RAM以及其他运行的内容,也无法建议具体的数字。

shared_buffers  128MB

Linux上专用数据库服务器的通常建议是将其设置为系统总RAM的25%,最大可能为8GB,并根据基准进行调整。

effective_cache_size    1GB

这不会分配任何RAM,但允许计划程序根据对同一查询中较早读取的仍然存在于缓存中的可能性的估计,从文件中反复读取文件。我建议你将你的shared_buffers大小添加到OS显示为缓存的内容。

work_mem    8MB

这个很棘手。它可以通过多种方式帮助查询性能,但是高值往往会将事物从缓存中推出,从而增加磁盘访问。您还需要考虑每个查询可以多次分配这么多空间(针对不同的查询步骤),因此您通常应允许每个连接允许一次分配此大小。这是一个的原因,使用连接池将大量用户汇集到有限数量的实际数据库连接通常是有益的。如果你能负担得起更大的尺寸,它很可能会帮助这个查询,因为它可能会使位图索引扫描变得“有损”并需要重新检查索引条件。

您没有设置cpu_tuple_cost,但我发现默认设置通常太低,无法提供最佳计划。鉴于您的大表有90列,我建议将其从0.01增加到0.05。

您没有设置effective_io_concurrency,但这可能有所帮助。我会测试不同的值。 (当然,在运行测试来比较替代方案的性能时,请注意缓存问题。)

maintenance_work_mem    128MB

根据您拥有多少RAM,这可能是合理的,也可能是不合理的。它不会影响您的直接问题,但增加它可能有助于autovacuum更有效地运行,并有助于索引构建运行得更快。

这些可能有点低:

checkpoint_segments 10
wal_buffers 1MB

它们不是您当前问题的一部分,但它们有时会导致额外的磁盘写入,因此调整它们可能会付出代价。除非您的计算机内存非常有限,否则wal_buffers通常应为32MB。 checkpoint_segments很难在不知道更多的情况下进行估算,但如果您检查日志和统计信息并发现检查点发生得太频繁,您可能希望在检查点基于checkpoint_timeout发生检查点之前将其增加。