【问题标题】:Full-text search in Postgres or CouchDB?Postgres 或 CouchDB 中的全文搜索?
【发布时间】:2011-07-14 05:38:42
【问题描述】:

我使用 geonames.org 并导入了他们所有的德国城市和所有地区的数据。

如果我输入“汉堡”,它会列出“汉堡中心,汉堡机场”等等。该应用程序位于无法访问 Internet 的封闭网络中,因此我无法访问 geonames.org 网络服务并且必须导入数据。 :( 这座城市及其所有地区都是自动完成的。所以每次按键都会产生一个 XHR 请求等等。

现在我的客户问是否有可能将世界上的所有数据都包含在其中。最后,大约 5.000.000 行有 45.000.000 个替代名称等。

Postgres 每次查询大约需要 3 秒,这使得自动完成无法使用。

现在我想到了 CouchDb,已经使用它了。我的问题:

我想发布“Ham”,并且希望 CouchDB 获取所有以“Ham”开头的文档。如果我输入“汉堡”,我希望它返回汉堡等等。

CouchDB 是适合它的数据库吗?您可以推荐哪些其他数据库以低延迟(可能在内存中)和数百万个数据集响应?数据集不会定期更改,它是相当静态的!

【问题讨论】:

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


    【解决方案1】:

    如果我理解你的问题,可能你所需要的一切都已经内置在 CouchDB 中了。

    1. 获取名称以 e.g. 开头的一系列文档“火腿”。您可以使用request with a string rangestartkey="Ham"&endkey="Ham\ufff0"
    2. 如果您需要更全面的搜索,您可以创建一个包含其他地点名称作为关键字的视图。因此,您可以再次使用上述技术查询范围。

    这是一个视图函数:

    function(doc) {
        for (var name in doc.places) {
            emit(name, doc._id);
        }
    }
    

    另请参阅有关 CouchDB typeahead and autocomplete search 的 CouchOne 博客文章以及有关 CouchDB autocomplete 的邮件列表上的讨论。

    【讨论】:

    • +1 如果您还没有安装 CouchDB-Lucene,我总是先尝试这种技术。它不是 Lucene,但它非常简单且非常灵活。我冒昧地在您的答案中添加了一个有用的链接。
    • 一个链接似乎被破坏了。
    【解决方案2】:

    使用 PostgreSQL 优化搜索

    您的搜索一开始就锚定,并且不需要模糊搜索逻辑。这不是全文搜索的典型用例。

    如果它变得更加模糊或者您的搜索没有在一开始就锚定,请在此处查看更多信息:

    在 PostgreSQL 中,您可以利用 高级索引功能,这会使查询非常快。特别注意operator classesindexes on expressions

    1) text_pattern_ops

    假设您的列是文本类型,您可以为 文本模式运算符 使用特殊索引,如下所示:

    CREATE INDEX name_text_pattern_ops_idx
    ON tbl (name text_pattern_ops);
    
    SELECT name
    FROM   tbl
    WHERE  name ~~ ('Hambu' || '%');
    

    这是假设您使用 C 以外的数据库区域设置进行操作 - 在您的情况下很可能是 de_DE.UTF-8。您可以还使用区域设置“C”设置数据库。我引用manual here

    如果您使用 C 语言环境,则不需要 xxx_pattern_ops 运算符类,因为具有默认运算符类的索引是 可用于 C 语言环境中的模式匹配查询。

    2) 表达式索引

    我想您还希望使该搜索不区分大小写。所以让我们再迈出一步,让它成为表达式的索引:

    CREATE INDEX lower_name_text_pattern_ops_idx
    ON tbl (lower(name) text_pattern_ops);
    
    SELECT name
    FROM   tbl
    WHERE  lower(name) ~~ (lower('Hambu') || '%');
    

    要使用索引,WHERE 子句必须匹配索引表达式。

    3) 优化索引大小和速度

    最后,您可能还想对前导字符数施加限制,以最小化索引大小并进一步加快速度:

    CREATE INDEX lower_left_name_text_pattern_ops_idx
    ON tbl (lower(left(name,10)) text_pattern_ops);
    
    SELECT name
    FROM   tbl
    WHERE  lower(left(name,10)) ~~ (lower('Hambu') || '%');
    

    left() 是在 Postgres 9.1 中引入的。在旧版本中使用substring(name, 1,10)

    4) 覆盖所有可能的请求

    超过 10 个字符的字符串呢?

    SELECT name
    FROM   tbl
    WHERE  lower(left(name,10)) ~ (lower(left('Hambu678910',10)) || '%');
    AND    lower(name) ~~ (lower('Hambu678910') || '%');
    

    这看起来是多余的,但您需要以这种方式拼写出来才能真正使用索引。索引搜索会将其缩小到几个条目,附加子句过滤其余条目。尝试找到最佳位置。取决于数据分布和典型用例。 10 个字符似乎是一个很好的起点。对于超过 10 个字符,left() 有效地变成了一种非常快速且简单的散列算法,足以满足许多(但不是全部)用例。

    5) 使用CLUSTER 优化磁盘表示

    因此,主要的访问模式将是根据我们的索引lower_left_name_text_pattern_ops_idx 检索一堆相邻的行。而你大部分时间阅读,几乎从不写作。这是CLUSTER教科书案例。 The manual:

    当一个表被聚集时,它会根据索引信息在物理上重新排序。

    对于像您这样的大表,这可以显着缩短响应时间,因为要获取的所有行都位于磁盘上的相同或相邻块中。

    第一次通话:

    CLUSTER tbl USING lower_left_name_text_pattern_ops_idx;
    

    将保存要使用的索引的信息,随后的调用将重新聚集表:

    CLUSTER tbl;
    CLUSTER;    -- cluster all tables in the db that have previously been clustered.
    

    如果不想重复:

    ALTER TABLE tbl SET WITHOUT CLUSTER;
    

    但是,CLUSTER 在表上使用排他锁。如果这是一个问题,请查看pg_repackpg_squeeze,它们可以在没有排他锁的情况下执行相同的操作。

    6) 防止结果中出现过多行

    要求搜索字符串至少包含 3 或 4 个字符。为了完整起见,我添加了这个,您可能还是会这样做。
    LIMIT返回的行数:

    SELECT name
    FROM   tbl
    WHERE  lower(left(name,10)) ~~ (lower('Hambu') || '%')
    LIMIT  501;
    

    如果您的查询返回超过 500 行,请告诉用户缩小搜索范围。

    7) 优化过滤方法(算子)

    如果您绝对必须挤出最后的每一微秒,您可以使用text_pattern_ops family 的运算符。像这样:

    SELECT name
    FROM   tbl
    WHERE  lower(left(name, 10)) ~>=~ lower('Hambu')
    AND    lower(left(name, 10)) ~<=~ (lower('Hambu') || chr(2097151));
    

    你在最后一个特技中获得的收益很少。通常,标准运算符是更好的选择。


    如果您这样做,搜索时间将减少到几毫秒。

    【讨论】:

      【解决方案3】:

      我认为更好的方法是将数据保存在数据库(Postgres 或 CouchDB)中,并使用全文搜索引擎(如 LuceneSolrElasticSearch)对其进行索引。

      话虽如此,有一个project integrating CouchDB with Lucene

      【讨论】:

      • +1 当您搜索文本时,没有什么可以与全文搜索索引竞争。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-03
      • 2012-07-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-16
      相关资源
      最近更新 更多