为什么Solr比Postgres快得多?

我最近从Postgres转到Solr,在我们的查询中看到了大约50倍的速度。 我们运行的查询涉及多个范围,我们的数据是车辆清单。 例如:“查找里程数<50,000,$ 5,000 <价格<$ 10,000,使=马自达的所有车辆…”

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

据我了解,Postgres和Solr使用模糊的类似数据结构(B-tree),并且它们都将数据caching在内存中。 所以我想知道这么大的性能差异是从哪里来的。

体系结构有什么不同可以解释这一点

首先,Solr不使用B树。 Lucene(Solr使用的底层库)索引是由只读段组成的 。 对于每个片段,Lucene都维护一个术语字典,该术语字典由出现在片段中的术语列表组成,按字典顺序sorting。 在这个术语中查找术语词典是使用二分查找的,所以单项查找的代价是O(log(t)) ,其中t是术语的数量。 相反,使用标准RDBMS的索引代价为O(log(d)) ,其中d是文档的数量。 当许多文件在某些​​领域具有相同的价值时,这可能是一个巨大的胜利。

而且,Lucene提交者Uwe Schindler在几年前增加了对高性能数值范围查询的支持。 对于数字字段的每个值,Lucene存储具有不同精度的几个值。 这允许Lucene非常有效地运行范围查询。 由于你的用例似乎可以大量的利用数值范围查询,这也许可以解释为什么Solr速度更快。 (欲了解更多信息,请阅读非常有趣的javadocs,并提供相关研究论文的链接。)

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

你并没有真正说出你调整你的PostgreSQL实例或你的查询所做的事情。 通过以更好的优化格式调整和/或重新设置查询,在PostgreSQL查询上看到50倍的速度并不罕见。

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

或许你的问题的最佳答案与如何在每个产品中执行search的技术细节没有任何关系,但更多的是与你的特定用例的易用性有关 。 显然,你可以用比PostgreSQL更less的麻烦find用Solrsearch的快速方法,它可能不会归结为任何东西。

我包含了一个关于在PostgreSQL中如何search多个条件的简短例子,以及一些小小的调整可以使性能有很大的不同。 为了保持它的快速和简单,我只是将战争与和平的文本forms运行到一个testing数据库中,每个“文档”是一个单独的文本行。 如果数据必须被松散地定义,那么类似的技术可以用于使用hstoretypes或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; 

一旦build立索引,我会显示一些search行计数和时间与两种types的索引:

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

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

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

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

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

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

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

如果没有索引,上面的查询需要从17.943毫秒到23.397毫秒,因为它们必须扫描整个表并检查每行中的匹配。

用“女士”和“先生们”对GIN进行索引search的行比完全相同的数据库中的表扫描快172倍。 显然,索引的好处将会比用于这个testing的文档更大。

这个设置当然是一次性的。 使用触发器来维护tsv列,所做的任何更改都将立即被search到,而无需重做任何设置。

使用慢PostgreSQL查询时,如果显示表结构(包括索引),问题查询以及运行EXPLAIN ANALYZE查询的输出,某人几乎总能发现问题并提出如何使其运行得更快。


更新 (十二月9 '16)

我没有提到我以前的时间是什么,但根据date可能是9.2版本的主要版本。 我只是发生在这个旧的线程上,并在同一个硬件上使用版本9.6.1再次尝试,看看是否有任何干预性能调优帮助这个例子。 只有一个参数的查询只在性能上增加了大约2%,但在使用GIN(反向)索引时,search“女士们” “先生们”的行速度翻倍到0.053毫秒(即53微秒)。

这个最大的不同在于Lucene / Solr索引就像一个没有任何对关系查询(JOIN)的支持的单表数据库。 请记住,索引通常仅用于支持search,而不是数据的主要来源。 所以你的数据库可能处于“第三范式”,但索引将被完全去规范化,主要包含需要search的数据。

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

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

Solr主要用于search数据,而不是用于存储。 这使得它可以放弃RDMS所需的大部分function。 所以它(或者说lucene )专注于纯粹的索引数据。

正如你无疑发现的那样,Solr能够从索引中search和检索数据。 后者(可选)能够导致自然的问题……“我可以使用Solr作为数据库吗?

答案是肯定的,我向你推荐以下内容:

我个人的观点是,Solr最好被认为是我的应用程序和数据库中掌握的数据之间的可searchcaching。 这样我就得到了两全其美的好处。

请阅读这个和这个 。

Solr(Lucene)创build一个倒排索引 ,在这个索引处检索数据变得相当快。 我读了PostgreSQL也有类似的function,但不知道你是否使用过。

您观察到的性能差异也可以解释为“正在search什么?”,“用户查询是什么?”。