为什么Solr比Postgres快得多?

时间:2012-04-07 08:40:26

标签: performance postgresql solr lucene rdbms

我最近从Postgres切换到了Solr,我们的查询速度提高了约50倍。我们运行的查询涉及多个范围,我们的数据是车辆清单。例如:“查找里程数<50,000,$ 5,000&lt;价格&lt; $ 10,000,make = Mazda ......”的所有车辆

我在Postgres的所有相关列上创建了索引,所以它应该是一个相当公平的比较。查看Postgres中的查询计划虽然它仍然只是使用单个索引然后扫描(我假设它不能使用所有不同的索引)。

据我了解,Postgres和Solr使用模糊相似的数据结构(B树),它们都将数据缓存在内存中。所以我想知道这么大的性能差异来自何处。

架构有什么不同可以解释这一点?

5 个答案:

答案 0 :(得分:129)

首先,Solr不使用B树。 Lucene(Solr使用的底层库)索引由只读segments组成。对于每个段,Lucene维护一个术语词典,该词典由段中出现的术语列表组成,按字典顺序排序。在这个术语词典中查找术语是使用二进制搜索进行的,因此单项查找的成本为O(log(t)),其中t是术语数。相反,使用标准RDBMS的索引需要O(log(d)),其中d是文档的数量。当许多文档对某些领域共享相同的值时,这可能是一个巨大的胜利。

此外,几年前Lucene的提交者Uwe Schindler为非常高效的numeric range queries增加了支持。对于numeric field的每个值,Lucene存储具有不同精度的多个值。这允许Lucene非常有效地运行范围查询。由于您的用例似乎很多地利用数值范围查询,这可以解释为什么Solr如此快速。 (有关更多信息,请阅读非常有趣的javadoc,并提供相关研究论文的链接。)

但是Solr只能这样做,因为它没有RDBMS所具有的所有约束。例如,Solr一次更新单个文档非常糟糕(它更喜欢批量更新)。

答案 1 :(得分:36)

您对调整PostgreSQL实例或查询所做的工作并没有多说。通过以更优化的格式调整和/或重新设置查询,看到PostgreSQL查询速度提高50倍并不罕见。

就在本周,有一份报告正在编写中,有人用Java和多个查询编写了一个报告,根据它在四小时内达到的程度,将需要大约一个月才能完成。 (它需要打五个不同的表,每个表都有数亿行。)我使用几个CTE和一个窗口函数重写它,以便它在不到十分钟的时间内运行并直接从查询中生成所需的结果。这是加速4400倍。

对于您的问题,最佳答案可能与每个产品中执行的搜索技术细节无关,而是与易用性有关为您的特定用例。显然,你能够找到使用Solr进行搜索的快速方法,而不是PostgreSQL,并且它可能不仅仅是那个。

我提供了一个简短的例子,说明如何在PostgreSQL中进行多个标准的文本搜索,以及一些小的调整如何能够产生很大的性能差异。为了保持快速和简单,我只是将文本形式的 War and Peace 运行到测试数据库中,每个“文档”都是一个文本行。如果必须松散地定义数据,则可以使用hstore类型或JSON列将相似技术用于任意字段。如果单独的列具有自己的索引,则使用索引的好处往往要大得多。

-- Create the table.
-- In reality, I would probably make tsv NOT NULL,
-- but I'm keeping the example simple...
CREATE TABLE war_and_peace
  (
    lineno serial PRIMARY KEY,
    linetext text NOT NULL,
    tsv tsvector
  );

-- Load from downloaded data into database.
COPY war_and_peace (linetext)
  FROM '/home/kgrittn/Downloads/war-and-peace.txt';

-- "Digest" data to lexemes.
UPDATE war_and_peace
  SET tsv = to_tsvector('english', linetext);

-- Index the lexemes using GiST.
-- To use GIN just replace "gist" below with "gin".
CREATE INDEX war_and_peace_tsv
  ON war_and_peace
  USING gist (tsv);

-- Make sure the database has statistics.
VACUUM ANALYZE war_and_peace;

设置索引后,我会显示一些搜索,包括行计数和两种索引类型的计时:

-- Find lines with "gentlemen".
EXPLAIN ANALYZE
SELECT * FROM war_and_peace
  WHERE tsv @@ to_tsquery('english', 'gentlemen');

84行,要点:2.006 ms,杜松子酒:0.194 ms

-- Find lines with "ladies".
EXPLAIN ANALYZE
SELECT * FROM war_and_peace
  WHERE tsv @@ to_tsquery('english', 'ladies');

184行,要点:3.549 ms,杜松子酒:0.328 ms

-- Find lines with "ladies" and "gentlemen".
EXPLAIN ANALYZE
SELECT * FROM war_and_peace
  WHERE tsv @@ to_tsquery('english', 'ladies & gentlemen');

1行,要点:0.971 ms,杜松子酒:0.104 ms

现在,由于GIN索引比GiST索引快10倍,您可能想知道为什么有人会使用GiST来索引文本数据。答案是GiST通常维护得更快。因此,如果您的文本数据具有高度不稳定性,则GiST索引可能会在整体负载上获胜,而如果您只对搜索时间或读取主要工作负载感兴趣,则GIN索引将获胜。

如果没有索引,上述查询需要从17.943 ms到23.397 ms,因为它们必须扫描整个表并检查每一行是否匹配。

GIN索引搜索“女士”和“绅士”的行比完全相同的数据库中的表扫描快172倍。显然,索引的好处在于文档更大,而不是用于此测试。

当然,设置是一次性的。通过触发器来维护tsv列,所做的任何更改都可以立即搜索到,而无需重做任何设置。

使用慢速PostgreSQL查询,如果您显示表结构(包括索引),问题查询以及查询运行EXPLAIN ANALYZE的输出,则有人几乎总能发现问题并建议如何获取它运行得更快。


更新(16年12月9日)

我没有提到我以前获得的时间,但根据日期可能会是9.2主要版本。我刚刚遇到这个旧线程,并使用版本9.6.1在相同的硬件上再次尝试,以查看是否有任何干预性能调整有助于此示例。仅一个参数的查询仅在性能上增加了大约2%,但是在使用“女士”“绅士”的行时,速度加倍到0.053毫秒(即53微秒) GIN(倒置)指数。

答案 2 :(得分:6)

Solr主要用于搜索数据,而不是用于存储。这使它能够丢弃RDMS所需的大部分功能。所以它(或更确切地说lucene)专注于纯粹索引数据。

毫无疑问,Solr能够从索引中搜索和检索数据。这是导致自然问题的后者(可选)功能......“我可以将Solr用作数据库吗?”

答案是肯定的,我建议您参考以下内容:

我个人认为Solr最好被认为是我的应用程序和数据库中掌握的数据之间的可搜索缓存。这样我就可以获得两全其美。

答案 3 :(得分:6)

这个最大的区别是Lucene / Solr索引就像一个单表数据库,不支持关系查询(JOIN)。请记住,索引通常只支持搜索,而不是数据的主要来源。因此,您的数据库可能处于“第三范式”,但索引将完全取消规范化,并且主要包含需要搜索的数据。

另一个可能的原因是数据库通常存在内部碎片,他们需要在巨大的请求上执行太多的半随机I / O任务。

这意味着,例如,考虑到数据库的索引体系结构,查询会导致索引进而导致数据。如果要恢复的数据被广泛传播,结果将花费很长时间,这似乎是数据库中发生的事情。

答案 4 :(得分:1)

请阅读thisthis

Solr(Lucene)创建一个inverted index,这是检索数据变得更快的地方。我read PostgreSQL也有类似的功能,但不确定你是否使用过它。

您观察到的性能差异也可以计入“正在搜索的内容?”,“用户查询的内容是什么?”