2013-04-18 198 views
3

我有一个小的PostgreSQL数据库(〜3,000行)。PostgreSQL全文搜索:为什么搜索速度太慢?

我试图在其中一个文本字段('body')上设置全文搜索。

问题是任何查询都非常慢(35+秒!!!)。

我想这个问题来自于数据库选择顺序扫描模式的事实...

这是我的查询:

SELECT 
     ts_rank_cd(to_tsvector('italian', body), query), 
     ts_headline('italian', body, to_tsquery('torino')), 
     title, 
     location, 
     id_author 
    FROM 
     fulltextsearch.documents, to_tsquery('torino') as query 
    WHERE 
     (body_tsvector @@ query) 
    OFFSET 
     0 

这是EXPLAIN分析一下:

         QUERY PLAN          
---------------------------------------------------------------------------------------------------------------------------- 
Limit (cost=0.00..1129.81 rows=19 width=468) (actual time=74.059..13630.114 rows=863 loops=1) 
-> Nested Loop (cost=0.00..1129.81 rows=19 width=468) (actual time=74.056..13629.342 rows=863 loops=1) 
    Join Filter: (documents.body_tsvector @@ query.query) 
    -> Function Scan on to_tsquery query (cost=0.00..0.01 rows=1 width=32) (actual time=4.606..4.608 rows=1 loops=1) 
    -> Seq Scan on documents (cost=0.00..1082.09 rows=3809 width=591) (actual time=0.045..48.072 rows=3809 loops=1) 
Total runtime: 13630.720 ms 

这是我的表格:

mydb=# \d+ fulltextsearch.documents; 
               Table "fulltextsearch.documents" 
    Column  |  Type  |        Modifiers        | Storage | Description 
---------------+-------------------+-----------------------------------------------------------------------+----------+------------- 
id   | integer   | not null default nextval('fulltextsearch.documents_id_seq'::regclass) | plain | 
id_author  | integer   |                  | plain | 
body   | character varying |                  | extended | 
title   | character varying |                  | extended | 
location  | character varying |                  | extended | 
date_creation | date    |                  | plain | 
body_tsvector | tsvector   |                  | extended | 
Indexes: 
    "fulltextsearch_documents_tsvector_idx" gin (to_tsvector('italian'::regconfig,  COALESCE(body, ''::character varying)::text)) 
    "id_idx" btree (id) 
Triggers: 
    body_tsvectorupdate BEFORE INSERT OR UPDATE ON fulltextsearch.documents FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger('body_tsvector', 'pg_catalog.italian', 'body') 
Has OIDs: no 

我敢肯定我错过了一些明显的东西....

任何线索?

=== UPDATE ======================================== ===============================

感谢您的建议,我想出了这个(更好)的查询:

SELECT 
    ts_rank(body_tsvector, query), 
    ts_headline('italian', body, query), 
    title, 
    location 
FROM 
    fulltextsearch.documents, to_tsquery('italian', 'torino') as query 
WHERE 
    to_tsvector('italian', coalesce(body,'')) @@ query 

这是相当好,但总是非常慢(13+秒...)。

我注意到,注释掉“ts_headline()”行查询是闪电般的。

这是EXPLAIN分析一下,并最终使用索引,但不会帮我多...:

EXPLAIN ANALYZE SELECT 
clock_timestamp() - statement_timestamp() as elapsed_time, 
    ts_rank(body_tsvector, query), 
    ts_headline('italian', body, query), 
    title, 
    location 
FROM 
    fulltextsearch.documents, to_tsquery('italian', 'torino') as query 
WHERE 
    to_tsvector('italian', coalesce(body,'')) @@ query 

Nested Loop (cost=16.15..85.04 rows=19 width=605) (actual time=102.290..13392.161 rows=863 loops=1) 
    -> Function Scan on query (cost=0.00..0.01 rows=1 width=32) (actual time=0.008..0.009 rows=1 loops=1) 
    -> Bitmap Heap Scan on documents (cost=16.15..84.65 rows=19 width=573) (actual time=0.381..4.236 rows=863 loops=1) 
     Recheck Cond: (to_tsvector('italian'::regconfig, (COALESCE(body, ''::character varying))::text) @@ query.query) 
     -> Bitmap Index Scan on fulltextsearch_documents_tsvector_idx (cost=0.00..16.15 rows=19 width=0) (actual time=0.312..0.312 rows=863 loops=1) 
       Index Cond: (to_tsvector('italian'::regconfig, (COALESCE(body, ''::character varying))::text) @@ query.query) 
Total runtime: 13392.717 ms 

回答

4

你少了两根(相当明显)的东西:

1您已在您的to_tsvector()中设置'italian',但您未指定它to_tsquery()

保持两者一致。

2您已将COALESCE(body, ...)编入索引,但这不是您要搜索的内容。

这个规划器并不神奇 - 如果这就是你所寻找的目标,你只能使用索引。

+2

策划者是在指标选择简单,保守。它不会查找默认的tsearch2语言来查看是否是“意大利语”,然后将语言未指定的版本与语言指定的意大利语版本对待,它需要两个函数调用都是* same *。事实上,在这方面,规划师可能会有点朦胧 - 上次我查了一下,关于如何写一个表达式有很多简单的变化 - 比如多余的括号 - 这会导致规划人员不知道它与索引匹配。 –

+0

@Richard Huxton感谢您的回答。通过“搜索”你的意思是FROM子句?如果是这样,请您为我的情况发布一个正确的FROM子句样本?对不起,我还是很困惑... – MarcoS

+1

http://www.postgresql.org/docs/current/static/textsearch-controls.html#TEXTSEARCH-HEADLINE“ts_headline使用原始文档,而不是tsvector摘要,所以它可能很慢,应该小心使用......“ –

0

最后,在你的答案和评论以及一些使用Google搜索的帮助下,我通过在完整结果集的子集上运行ts_headline()(我认为是一个非常繁重的函数)(结果页面我感兴趣的):

SELECT 
     id, 
     ts_headline('italian', body, to_tsquery('italian', 'torino')) as headline, 
     rank, 
     title, 
     location 
    FROM (
     SELECT 
      id, 
      body, 
      title, 
      location, 
      ts_rank(body_tsvector, query) as rank 
     FROM 
      fulltextsearch.documents, to_tsquery('italian', 'torino') as query 
     WHERE 
      to_tsvector('italian', coalesce(body,'')) @@ query 
     LIMIT 10 
     OFFSET 0 
    ) as s 
0

我通过预先计算的ts_rank_cd并将其存储在语料库中的流行术语(高次)表解决了这个问题。搜索查看此表以获取查询字词的排序文档等级。如果不存在(对于不太流行的术语),它将默认创建ts_rank_cd。

请看看这篇文章。

https://dba.stackexchange.com/a/149701