cbe*_*ner 63 lucene postgresql performance rdbms solr
我最近从Postgres切换到了Solr,在查询中加速了~50倍.我们运行的查询涉及多个范围,我们的数据是车辆清单.例如:"查找里程<50,000,$ 5,000 <价格<$ 10,000,make = Mazda ......"的所有车辆
我在Postgres的所有相关专栏上创建了索引,所以它应该是一个相当公平的比较.看看Postgres中的查询计划虽然它仍然只是使用单个索引然后扫描(我假设因为它无法使用所有不同的索引).
据我了解,Postgres和Solr使用模糊相似的数据结构(B树),它们都将数据缓存在内存中.所以我想知道这么大的性能差异来自哪里.
架构有什么不同可以解释这一点?
jpo*_*ntz 131
首先,Solr不使用B树.Lucene(Solr使用的底层库)索引由只读段组成.对于每个段,Lucene维护一个术语词典,该词典由段中出现的术语列表组成,按字典顺序排序.在这个术语词典中查找术语是使用二进制搜索进行的,因此单项查找的成本是O(log(t))
其中t是术语的数量.相反,使用标准RDBMS的索引成本O(log(d))
,其中d是文档的数量.当许多文档对某些字段共享相同的值时,这可能是一个巨大的胜利.
此外,Lucene提交者Uwe Schindler 几年前增加了对非常高性能数值范围查询的支持.对于数字字段的每个值,Lucene存储具有不同精度的多个值.这允许Lucene非常有效地运行范围查询.由于您的用例似乎充分利用了数值范围查询,这可以解释为什么Solr如此快速.(有关更多信息,请阅读非常有趣的javadoc,并提供相关研究论文的链接.)
但是Solr只能这样做,因为它没有RDBMS所具有的所有约束.例如,Solr一次更新单个文档非常糟糕(它更喜欢批量更新).
kgr*_*ttn 37
你没有真正说出你为调整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;
Run Code Online (Sandbox Code Playgroud)
设置索引后,我会显示一些搜索,其中包含两种类型索引的行计数和计时:
-- Find lines with "gentlemen".
EXPLAIN ANALYZE
SELECT * FROM war_and_peace
WHERE tsv @@ to_tsquery('english', 'gentlemen');
Run Code Online (Sandbox Code Playgroud)
84行,要点:2.006 ms,杜松子酒:0.194 ms
-- Find lines with "ladies".
EXPLAIN ANALYZE
SELECT * FROM war_and_peace
WHERE tsv @@ to_tsquery('english', 'ladies');
Run Code Online (Sandbox Code Playgroud)
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');
Run Code Online (Sandbox Code Playgroud)
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年9月9日)
我没有提到我过去获得的时间,但根据日期可能会是9.2主要版本.我刚刚遇到这个旧线程,并使用版本9.6.1在相同的硬件上再次尝试,以查看是否有任何干预性能调整有助于此示例.仅一个参数的查询仅在性能上增加了大约2%,但是当使用GIN(倒置)索引时,搜索具有"女士" 和 "绅士"的行的速度加倍到0.053毫秒(即53微秒).
Solr主要用于搜索数据,而不是用于存储.这使它能够丢弃RDMS所需的大部分功能.所以它(或者更确切地说是lucene)专注于纯粹的索引数据.
毫无疑问,Solr能够从索引中搜索和检索数据.这是后者(可选)能力导致自然问题......"我可以将Solr用作数据库吗?"
答案是肯定的,我建议您参考以下内容:
我个人认为Solr最好被认为是我的应用程序和数据库中掌握的数据之间的可搜索缓存.这样我就可以获得两全其美.
最大的区别是Lucene/Solr索引就像单表数据库,不支持关系查询(JOIN).请记住,索引通常只支持搜索,而不是数据的主要来源.因此,您的数据库可能处于"第三范式",但索引将完全取消规范化,并且主要包含需要搜索的数据.
另一个可能的原因是数据库通常存在内部碎片,他们需要在巨大的请求上执行太多的半随机I/O任务.
这意味着,例如,考虑到数据库的索引体系结构,查询会导致索引进而导致数据.如果要恢复的数据广泛传播,结果将花费很长时间,这似乎是数据库中发生的事情.
归档时间: |
|
查看次数: |
26351 次 |
最近记录: |