1

我正在开发一个大型 PostgreSQL 项目,不幸的是,在生产中(一个有 90 列的表中有 300 万条记录)在大约 2 分钟内运行一个查询(具有 2 个条件的选择/连接)。

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

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))"
4

1 回答 1

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 大小添加到操作系统显示为缓存的内容中。

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除非您的机器的 RAM 非常有限,否则通常应该是 32MB。 checkpoint_segments在不了解更多信息的情况下很难估计,但如果您检查日志和统计信息并发现检查点发生得太频繁,您可能需要增加此值,直到检查点发生基于checkpoint_timeout.

于 2012-04-13T16:19:53.193 回答