一些背景:
\n\n我有一个表,documents
其中包含超过 200,000 行,其中有一列body
,该列可能很长。我在这张表上放置了一个索引:
CREATE INDEX documents_body_tsvector_index ON documents USING GIN (to_tsvector('english', body));\n
Run Code Online (Sandbox Code Playgroud)\n\n有了这个索引,我的包含 WHERE 子句的查询速度to_tsvector('english', body) @@ to_tsquery('english', 'awesome')
非常快,这很棒。
问题:
\n\n虽然plainto_tsquery('english', 'awesome website')
运行速度很快,但phraseto_tsquery('english', 'awesome website') runs extremely slow. (I've also tried
to_tsquery('english', 'awesome <-> website') 也运行得很快,而且速度也非常慢。)
问题:
\n\n我怎样才能加快速度?我的索引做错了吗?我真的需要考虑到\xe2\x80\x94的位置,phraseto_tsquery
使用似乎是最好的方法。任何帮助将非常感激。
(编辑)解释:
\n\n这是快速查询:
\n\nEXPLAIN (ANALYZE, BUFFERS) SELECT COUNT(*)\nFROM documents\nWHERE to_tsvector('english', body) @@ plainto_tsquery('english', 'termone termtwo');\n\nAggregate (cost=57.16..57.17 rows=1 width=8) (actual time=4.386..4.387 rows=1 loops=1)\n Buffers: shared hit=2949\n -> Bitmap Heap Scan on documents (cost=36.04..57.15 rows=5 width=0) (actual time=1.609..4.096 rows=3053 loops=1)\n Recheck Cond: (to_tsvector('english'::regconfig, text) @@ '''termone'' & ''termtwo'''::tsquery)\n Heap Blocks: exact=2907\n Buffers: shared hit=2949\n -> Bitmap Index Scan on documents_text_tsvector_index (cost=0.00..36.04 rows=5 width=0) (actual time=1.265..1.265 rows=3053 loops=1)\n Index Cond: (to_tsvector('english'::regconfig, body) @@ '''termone'' & ''termtwo'''::tsquery)\n Buffers: shared hit=42\nPlanning time: 0.094 ms\nExecution time: 4.417 ms\n(11 rows)\n
Run Code Online (Sandbox Code Playgroud)\n\n这是慢速查询:
\n\nEXPLAIN (ANALYZE, BUFFERS) SELECT COUNT(*)\nFROM documents\nWHERE to_tsvector('english', body) @@ phraseto_tsquery('english', 'termone termtwo');\n\nAggregate (cost=57.16..57.17 rows=1 width=8) (actual time=120252.866..120252.866 rows=1 loops=1)\n Buffers: shared hit=38792\n -> Bitmap Heap Scan on documents (cost=36.04..57.15 rows=5 width=0) (actual time=3.065..120250.231 rows=1750 loops=1)\n Recheck Cond: (to_tsvector('english'::regconfig, body) @@ '''termone'' <-> ''termtwo'''::tsquery)\n Rows Removed by Index Recheck: 1303\n Heap Blocks: exact=2907\n Buffers: shared hit=38792\n -> Bitmap Index Scan on documents_text_tsvector_index (cost=0.00..36.04 rows=5 width=0) (actual time=1.491..1.491 rows=3053 loops=1)\n Index Cond: (to_tsvector('english'::regconfig, body) @@ '''termone'' <-> ''termtwo'''::tsquery)\n Buffers: shared hit=42\nPlanning time: 0.540 ms\nExecution time: 120252.938 ms\n(12 rows)\n
Run Code Online (Sandbox Code Playgroud)\n
短语搜索必须检查包含“awesom”和“websit”的每个文档,以确保标记以正确的顺序和接近度出现,因为索引中不存在顺序和接近度数据。重新解析、标记化、词干提取等这些文档的速度很慢。此外,由于大型文档存储在带外,因此它必须先从单独的 TOAST 表中读取大型数据,然后才能执行任何操作。如果没有词序和邻近度约束,它根本不需要进行任何重新检查,因为在这种情况下索引只返回精确的结果,不可能出现误报(只要 work_mem 足够大)。
您可以将 tsvector 存储在表中并保留该列,而不是使用功能索引(例如,行插入时的触发器和设置 的 body 更新时的触发器tsv = to_tsvector('english', body)
)。然后,您将直接在“tsv”列上构建索引,并直接针对该列编写查询:
ALTER TABLE documents add column tsv tsvector;
update documents set tsv = to_tsvector('english', body);
CREATE INDEX documents_tsv_tsvector_index ON documents USING GIN (tsv);
SELECT...WHERE tsv @@ phraseto_tsquery('english', 'awesome website')
Run Code Online (Sandbox Code Playgroud)
在这里,它仍然需要重新检查“tsv”的顺序和邻近性,这意味着它需要从磁盘中读取该大列并返回到内存中,但不需要重新解析它,因为它已经以标记化形式存储专为提高效率而设计。这带来的改进程度取决于您的瓶颈是用于将文档读回内存的 IO,还是用于进行解析的 CPU。
当然,这确实意味着您将在表中存储“body”和“tsv”,这将大大扩展其大小,但这可能是为了提高性能而付出的成本较低。如果您想做的只是 COUNT(*),那么您甚至可以仅存储 tsvector,而根本不存储原始“主体”。