从列中获取 ts_query 语言时,不使用 PostgreSQL GIN 索引

jaa*_*ap3 7 postgresql full-text-search gin-index

我有一个存储一些多语言内容的表:

CREATE TABLE search (
  content text NOT NULL,
  language regconfig NOT NULL,
  fulltext tsvector
);

CREATE INDEX search_fulltext ON search USING GIN(fulltext);

INSERT INTO search (language, content) VALUES 
  ('dutch', 'Als achter vliegen vliegen vliegen vliegen vliegen vliegen achterna'),
  ('dutch', 'Langs de koele kali liep een kale koeli met een kilo kali op zijn kale koeli-kop.'),
  ('dutch', 'Moeder sneed zeven scheve sneden brood'),
  ('english', 'I saw Susie sitting in a shoe shine shop. Where she sits she shines, and where she shines she sits.'),
  ('english', 'How can a clam cram in a clean cream can?'),
  ('english', 'Can you can a can as a canner can can a can?');

UPDATE search SET fulltext = to_tsvector(language, content);
Run Code Online (Sandbox Code Playgroud)

为了确保我始终使用正确的语言进行搜索,我使用以下查询:

SELECT FROM search WHERE fulltext @@ to_tsquery(language, 'shine');
(1 row)

SELECT FROM search WHERE fulltext @@ to_tsquery(language, 'vlieg');
(1 row)
Run Code Online (Sandbox Code Playgroud)

因为对语言进行硬编码不会给出正确的结果:

SELECT FROM search WHERE fulltext @@ to_tsquery('dutch', 'shine');
(0 rows)

SELECT FROM search WHERE fulltext @@ to_tsquery('english', 'vlieg');
(0 rows)
Run Code Online (Sandbox Code Playgroud)

然而问题是 PostgreSQL 在使用第一组查询时不使用 GIN 索引,而是进行顺序扫描:

SET enable_seqscan = OFF;(注意:由于行数较少,我已禁用这些示例的扫描)

EXPLAIN ANALYZE SELECT * FROM search WHERE fulltext @@ to_tsquery(language, 'shine');
---
Seq Scan on search  (cost=0.00..17.35 rows=2 width=136) (actual time=0.040..0.044 rows=1 loops=1)
    Filter: (fulltext @@ to_tsquery(language, 'shine'::text))
    Rows Removed by Filter: 5
Planning time: 0.039 ms
Execution time: 0.064 ms
(5 rows)
Run Code Online (Sandbox Code Playgroud)

虽然在硬编码语言时确实如此:

EXPLAIN ANALYZE SELECT FROM search WHERE fulltext @@ to_tsquery('dutch', 'vlieg');
---
Bitmap Heap Scan on search  (cost=12.63..23.66 rows=82 width=0) (actual time=0.044..0.044 rows=1 loops=1)
  Recheck Cond: (fulltext @@ '''vlieg'''::tsquery)
  Heap Blocks: exact=1
  ->  Bitmap Index Scan on search_fulltext  (cost=0.00..12.61 rows=82 width=0) (actual time=0.037..0.037 rows=1 loops=1)
        Index Cond: (fulltext @@ '''vlieg'''::tsquery)
Planning time: 0.128 ms
Execution time: 0.065 ms
(7 rows)
Run Code Online (Sandbox Code Playgroud)

所以我的问题是:是否有可能使用 中的一列来ts_query使用正确的语言配置,并且仍然让 Postgres 使用 GIN 索引?

我正在使用 PostgreSQL 9.4。

编辑

这是真实表中的执行计划:

使用语言配置列:

Seq Scan on search  (cost=0.00..8727.25 rows=188 width=0) (actual time=0.725..352.307 rows=1689 loops=1)
  Filter: (fulltext @@ to_tsquery(language_config, 'example'::text))
  Rows Removed by Filter: 35928
Planning time: 0.053 ms
Execution time: 352.915 ms
Run Code Online (Sandbox Code Playgroud)

当对语言进行硬编码时:

Bitmap Heap Scan on search  (cost=28.65..4088.92 rows=1633 width=0) (actual time=0.514..10.475 rows=1684 loops=1)
  Recheck Cond: (fulltext @@ '''exampl'''::tsquery)
  Heap Blocks: exact=1522
  ->  Bitmap Index Scan on search_fulltext  (cost=0.00..28.24 rows=1633 width=0) (actual time=0.333..0.333 rows=1684 loops=1)
        Index Cond: (fulltext @@ '''exampl'''::tsquery)
Planning time: 0.180 ms
Execution time: 10.564 ms
Run Code Online (Sandbox Code Playgroud)

编辑#2

用 Postgres 9.5 尝试过,结果相同

3ma*_*uek 0

language当使用to_tsquery查询列时,Pg 强制进行 seqscan 是有道理的,因为每次迭代都需要所有检查行的语言列的值

对于这种特殊情况,您可能需要更改查询方式:

so=# EXPLAIN ANALYZE SELECT * FROM search WHERE 
to_tsvector(language,fulltext::text) @@ 'shine'::tsquery;
Run Code Online (Sandbox Code Playgroud)

当您使用 动态查询时language,您可能希望to_tsvector即时发出。我测试了这个索引,它比索引 tsvector 快一点。您可以重新执行此操作content,但解析将花费一些额外的时间(根据文本的大小,执行可能会有很大差异)。

CREATE EXTENSION btree_gin;
CREATE INDEX search_fulltext_new ON search USING 
GIN(to_tsvector(language,fulltext::text), language);
Run Code Online (Sandbox Code Playgroud)

这是解释:

so=# EXPLAIN ANALYZE SELECT * FROM search WHERE 
to_tsvector(language,fulltext::text) @@ 'shine'::tsquery;
Bitmap Heap Scan on search  (cost=12.95..410.41 rows=123 width=168) (actual time=0.652..9.256 rows=4096 loops=1)
   Recheck Cond: (to_tsvector(language, (fulltext)::text) @@ '''shine'''::tsquery)
   Heap Blocks: exact=512
    ->  Bitmap Index Scan on search_fulltext_new  (cost=0.00..12.92 rows=123 width=0) (actual time=0.584..0.584 rows=4096 loops=1)
     Index Cond: (to_tsvector(language, (fulltext)::text) @@ '''shine'''::tsquery)
 Planning time: 0.164 ms
 Execution time: 10.416 ms
Run Code Online (Sandbox Code Playgroud)

它还允许您按语言过滤:

so=# EXPLAIN ANALYZE SELECT * FROM search WHERE 
to_tsvector(language,fulltext::text) @@ 'shine'::tsquery AND language = 
'english'::regconfig;
Run Code Online (Sandbox Code Playgroud)

根据值分布,我可以建议的是按语言对表进行分区(这是为了进一步优化,而不是解决这个问题)。通过这样做,您将不需要即时进行此转换,并且语言过滤将通过约束排除来完成,从而避免对父表进行任何 seqscan。