【问题标题】:PostgreSQL Full Text Search: why search is sooo slow?PostgreSQL 全文搜索:为什么搜索这么慢?
【发布时间】:2013-04-11 18:30:38
【问题描述】:

我有一个小型 PostgreSQL 数据库(~~3,000 行)。

我正在尝试在其中一个文本字段(“正文”)上设置全文搜索。

问题是任何查询都非常慢(超过 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

这是解释分析:

                                      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

我确定我遗漏了一些明显的东西......

有什么线索吗?

.

.

.

=== 更新 ==================================== ====================================

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

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 ANALYZE,最终使用了索引,但对我帮助不大……:

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

【问题讨论】:

    标签: postgresql full-text-search full-text-indexing


    【解决方案1】:

    您遗漏了两件事(相当明显):

    1 您在to_tsvector() 中设置了'italian',但您没有在to_tsquery() 中指定它

    保持两者一致。

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

    规划器并不神奇 - 只有当您正在搜索时,您才能使用索引。

    【讨论】:

    • 规划器在索引选择上简单保守。它不会查找默认的 tsearch2 语言来查看是否为italian,然后将未指定语言的版本视为与指定语言版本相同的语言版本,它需要两个函数调用都相同。事实上,在这方面,规划器可能有点暗淡——上次我检查时,关于如何编写表达式有很多简单的变化——比如多余的括号——这会导致规划器无法识别它与索引匹配.
    • @Richard Huxton 感谢您的回答。 “搜索”是指 FROM 子句?如果是这样,您能否针对我的情况发布一个正确的 FROM 子句示例?对不起,我还很困惑...
    • postgresql.org/docs/current/static/… "ts_headline 使用的是原始文档,而不是 tsvector 汇总,所以会比较慢,请谨慎使用..."
    【解决方案2】:

    最后,在您的答案和 cmets 的帮助下,通过谷歌搜索,我确实通过在完整结果集的一个子集(我的结果页面'感兴趣):

        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
    

    【讨论】:

      【解决方案3】:

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

      请看这篇文章。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-05-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多