为什么将 tsquery 放在 WITH 中会突然使它花费 2 倍的时间? (postgresql)
Why does putting a tsquery inside a WITH suddenly make it take 2x as long? (postgresql)
我有一个非常简单的查询:
SELECT count(*) FROM link_list ll
WHERE ll.tsvector @@ to_tsquery('english', 'advertising|slogan|publicity|ads|advertise|brand|promo|ad|propaganda|sales|promote|logo|hype|commercial|consumerism|pamphlet|viral|salesperson|selling|blurb|advert|merchandise|marking|products|campaign|packaging|billboard|advertisement|promotion|questionnaire|marketing')
它运行得很快(这是 EXPLAIN ANALYZE 的输出)。它使用 GIN 索引,并且完全按照预期工作。
生活是美好的。
但是现在,让我稍微调整一下查询,现在它需要 2 倍的时间!
WITH query AS
(
SELECT to_tsquery('english',('advertising|slogan|publicity|ads|advertise|brand|promo|ad|propaganda|sales|promote|logo|hype|commercial|consumerism|pamphlet|viral|salesperson|selling|blurb|advert|merchandise|marking|products|campaign|packaging|billboard|advertisement|promotion|questionnaire|marketing')) query
)
SELECT count(*) FROM link_list ll
WHERE ll.tsvector @@ (SELECT query FROM query);
(来自 EXPLAIN ANALYZE 的输出)
我会只使用第一个查询...但不幸的是,同义词列表必须动态生成,我从另一个table中提取。
出于某些奇怪的原因,将 tsquery
放在 WITH
中会使 Postgresql 无法有效地使用索引(它认为这将是一项不需要索引的快速而肮脏的工作, 结果完全错了)。
世界上为什么会发生这种情况?
这些处决似乎都没有那么快。
您对 WITH 的使用阻止了并行计划。如果您的瓶颈是 IO(这似乎很可能),您可以通过将 effective_io_concurrency 设置为 > 1 的值来获得并行 IO 而无需并行查询。
设置 JIT 所花费的时间是更快计划的 10% 以上,可能完全是浪费。您可以 set jit = off
(或在 .conf 文件中全局关闭它)以节省时间。
您对有损块的重新检查也很浪费时间。你应该增加 work_mem 来摆脱那些。但是浪费的大部分是 CPU 时间,所以如果瓶颈是 IO,而不是 CPU,效果会很小。它仍然必须访问同一组块。 (一个例外是,如果 TOAST 被大量使用,则不需要从 TOAST 组装未重新检查的行,因此避免了那些 TOAST 块读取。)
我有一个非常简单的查询:
SELECT count(*) FROM link_list ll
WHERE ll.tsvector @@ to_tsquery('english', 'advertising|slogan|publicity|ads|advertise|brand|promo|ad|propaganda|sales|promote|logo|hype|commercial|consumerism|pamphlet|viral|salesperson|selling|blurb|advert|merchandise|marking|products|campaign|packaging|billboard|advertisement|promotion|questionnaire|marketing')
它运行得很快(这是 EXPLAIN ANALYZE 的输出)。它使用 GIN 索引,并且完全按照预期工作。
生活是美好的。
但是现在,让我稍微调整一下查询,现在它需要 2 倍的时间!
WITH query AS
(
SELECT to_tsquery('english',('advertising|slogan|publicity|ads|advertise|brand|promo|ad|propaganda|sales|promote|logo|hype|commercial|consumerism|pamphlet|viral|salesperson|selling|blurb|advert|merchandise|marking|products|campaign|packaging|billboard|advertisement|promotion|questionnaire|marketing')) query
)
SELECT count(*) FROM link_list ll
WHERE ll.tsvector @@ (SELECT query FROM query);
(来自 EXPLAIN ANALYZE 的输出)
我会只使用第一个查询...但不幸的是,同义词列表必须动态生成,我从另一个table中提取。
出于某些奇怪的原因,将 tsquery
放在 WITH
中会使 Postgresql 无法有效地使用索引(它认为这将是一项不需要索引的快速而肮脏的工作, 结果完全错了)。
世界上为什么会发生这种情况?
这些处决似乎都没有那么快。
您对 WITH 的使用阻止了并行计划。如果您的瓶颈是 IO(这似乎很可能),您可以通过将 effective_io_concurrency 设置为 > 1 的值来获得并行 IO 而无需并行查询。
设置 JIT 所花费的时间是更快计划的 10% 以上,可能完全是浪费。您可以 set jit = off
(或在 .conf 文件中全局关闭它)以节省时间。
您对有损块的重新检查也很浪费时间。你应该增加 work_mem 来摆脱那些。但是浪费的大部分是 CPU 时间,所以如果瓶颈是 IO,而不是 CPU,效果会很小。它仍然必须访问同一组块。 (一个例外是,如果 TOAST 被大量使用,则不需要从 TOAST 组装未重新检查的行,因此避免了那些 TOAST 块读取。)