PostgreSQL - 奇怪的查询规划器行为
PostgreSQL - weird query planner behavior
假设我有这样的查询:
SELECT *
FROM clients c
INNER JOIN clients_balances cb ON cb.id_clients = c.id
LEFT JOIN clients com ON com.id = c.id_companies
LEFT JOIN clients com_real ON com_real.id = c.id_companies_real
LEFT JOIN rate_tables rt_orig ON rt_orig.id = c.orig_rate_table
LEFT JOIN rate_tables rt_term ON rt_term.id = c.term_rate_table
LEFT JOIN payment_terms pt ON pt.id = c.id_payment_terms
LEFT JOIN paygw_clients_profiles cpgw ON (cpgw.id_clients = c.id AND cpgw.id_companies = c.id_companies_real)
WHERE
EXISTS (SELECT * FROM accounts WHERE (name LIKE 'x' OR accname LIKE 'x' OR ani LIKE 'x') AND id_clients = c.id)
AND c."type" = '0'
AND c."id" > 0
ORDER BY c."name";
此查询在生产环境中使用时 运行 大约需要 35 秒("clients" 有大约 100 万条记录)。但是,如果我取出任何连接 - 查询将只需要大约 300 毫秒来执行。
我试过查询计划器设置,但无济于事。
这里有一些解释分析输出:
http://explain.depesz.com/s/hzy (slow - 48049.574 ms)
http://explain.depesz.com/s/FWCd (fast - 286.234 ms, rate_tables JOIN removed)
http://explain.depesz.com/s/MyRf (fast - 539.733 ms, paygw_clients_profiles JOIN removed)
看起来在快速情况下,规划器从 EXISTS 语句开始,总共只需要对两行执行连接。然而,在缓慢的情况下,它会首先连接所有 table,然后通过 EXISTS 过滤。
我需要做的是在合理的时间内完成此查询 运行,所有七个连接都已到位。
CentOS 6.3 上的 Postgres 版本是 9.3.10。
谢谢。
更新
像这样重写查询:
SELECT *
FROM clients c
INNER JOIN clients_balances cb ON cb.id_clients = c.id
INNER JOIN accounts a ON a.id_clients = c.id AND (a.name = 'x' OR a.accname = 'x' OR a.ani = 'x')
LEFT JOIN clients com ON com.id = c.id_companies
LEFT JOIN clients com_real ON com_real.id = c.id_companies_real
LEFT JOIN rate_tables rt_orig ON rt_orig.id = c.orig_rate_table
LEFT JOIN rate_tables rt_term ON rt_term.id = c.term_rate_table
LEFT JOIN payment_terms pt ON pt.id = c.id_payment_terms
LEFT JOIN paygw_clients_profiles cpgw ON (cpgw.id_clients = c.id AND cpgw.id_companies = c.id_companies_real)
WHERE
c."type" = '0' AND c.id > 0
ORDER BY c."name";
使其运行快,但是,这不是acceptable,因为帐户过滤参数是可选的,如果没有匹配,我仍然需要结果table.使用 "LEFT JOIN accounts" 而不是 "INNER JOIN accounts" 会再次破坏性能。
按照 Tome Lane 的建议,我将以下两个参数:join_collapse_limit 和 from_collapse_limit 更改为 10 而不是默认的 8,这解决了问题。
假设我有这样的查询:
SELECT *
FROM clients c
INNER JOIN clients_balances cb ON cb.id_clients = c.id
LEFT JOIN clients com ON com.id = c.id_companies
LEFT JOIN clients com_real ON com_real.id = c.id_companies_real
LEFT JOIN rate_tables rt_orig ON rt_orig.id = c.orig_rate_table
LEFT JOIN rate_tables rt_term ON rt_term.id = c.term_rate_table
LEFT JOIN payment_terms pt ON pt.id = c.id_payment_terms
LEFT JOIN paygw_clients_profiles cpgw ON (cpgw.id_clients = c.id AND cpgw.id_companies = c.id_companies_real)
WHERE
EXISTS (SELECT * FROM accounts WHERE (name LIKE 'x' OR accname LIKE 'x' OR ani LIKE 'x') AND id_clients = c.id)
AND c."type" = '0'
AND c."id" > 0
ORDER BY c."name";
此查询在生产环境中使用时 运行 大约需要 35 秒("clients" 有大约 100 万条记录)。但是,如果我取出任何连接 - 查询将只需要大约 300 毫秒来执行。
我试过查询计划器设置,但无济于事。
这里有一些解释分析输出:
http://explain.depesz.com/s/hzy (slow - 48049.574 ms)
http://explain.depesz.com/s/FWCd (fast - 286.234 ms, rate_tables JOIN removed)
http://explain.depesz.com/s/MyRf (fast - 539.733 ms, paygw_clients_profiles JOIN removed)
看起来在快速情况下,规划器从 EXISTS 语句开始,总共只需要对两行执行连接。然而,在缓慢的情况下,它会首先连接所有 table,然后通过 EXISTS 过滤。
我需要做的是在合理的时间内完成此查询 运行,所有七个连接都已到位。
CentOS 6.3 上的 Postgres 版本是 9.3.10。
谢谢。
更新
像这样重写查询:
SELECT *
FROM clients c
INNER JOIN clients_balances cb ON cb.id_clients = c.id
INNER JOIN accounts a ON a.id_clients = c.id AND (a.name = 'x' OR a.accname = 'x' OR a.ani = 'x')
LEFT JOIN clients com ON com.id = c.id_companies
LEFT JOIN clients com_real ON com_real.id = c.id_companies_real
LEFT JOIN rate_tables rt_orig ON rt_orig.id = c.orig_rate_table
LEFT JOIN rate_tables rt_term ON rt_term.id = c.term_rate_table
LEFT JOIN payment_terms pt ON pt.id = c.id_payment_terms
LEFT JOIN paygw_clients_profiles cpgw ON (cpgw.id_clients = c.id AND cpgw.id_companies = c.id_companies_real)
WHERE
c."type" = '0' AND c.id > 0
ORDER BY c."name";
使其运行快,但是,这不是acceptable,因为帐户过滤参数是可选的,如果没有匹配,我仍然需要结果table.使用 "LEFT JOIN accounts" 而不是 "INNER JOIN accounts" 会再次破坏性能。
按照 Tome Lane 的建议,我将以下两个参数:join_collapse_limit 和 from_collapse_limit 更改为 10 而不是默认的 8,这解决了问题。