⚠️ 此页面为自动翻译,翻译可能不完美。

Percolate queries: Manticore Search vs Elasticsearch

引言

倒排查询也被称为持久查询、前瞻性搜索、文档路由、反向搜索或逆向搜索。

通常进行搜索的方式是存储我们想要搜索的文档,并对它们执行查询。然而,有时我们希望将查询应用于传入的新文档以触发匹配。在某些场景中这是需要的。例如,监控系统不仅收集数据,还希望在不同事件发生时通知用户。这可能包括达到某个指标的阈值或监控数据中出现的特定值。另一个类似的案例是新闻聚合。您可以通知用户有关任何新鲜新闻的信息,但用户可能只希望收到特定类别或主题的通知。更进一步,他们可能只对某些“关键词”感兴趣。

执行倒排查询的应用程序通常处理高负载:它们需要每秒处理数千个文档,并且可能有数十万个查询需要对每个文档进行测试。

Elasticsearch 和 Manticore Search 都提供了倒排查询功能。

虽然倒排查询在 Elasticsearch 中已经存在了一段时间,但在 Manticore 中是在 2.6.0 版本中添加的,Sphinx 仍然缺少该功能。今天我想进行一些性能测试,以确定每种技术可以提供的具体数值。

测试案例

许多从事社交媒体营销行业的公司通常对分析 Twitter 的数据流(即 firehose 或 decahose)感兴趣,换句话说,他们希望搜索全球用户在 twitter.com 上发布的所有或部分消息,以了解受众对其客户品牌、产品或其他内容的兴趣程度。为了使我们的测试更接近现实,我们也将使用示例推文。我在 archive.org 上找到了一个不错的存档,地址是 https://archive.org/details/tweets-20170508080715-crawl448 ,这给了我大约 600K 条随机推文:

$ wc -l ~/twitter/text
607388 /home/snikolaev/twitter/text

这些文档是 JSON 解码的,但为了让你了解它们的外观(是的,让我们想象一下现在还是 2005 年,没有人知道一条 Twitter 消息通常是什么样子):

$ head text
"\u8266\u968a\u304c\u5e30\u6295\u3057\u305f\u3002"
"RT @BTS_jp_official: #BTS \u65e5\u672c7th\u30b7\u30f3\u30b0\u30eb\u300c\u8840\u3001\u6c57\u3001\u6d99 -Japanese ver.-\u300dMV\n\u21d2 https:\/\/t.co\/jQGltkBQhj \niTunes\uff1a https:\/\/t.co\/2OGIYrrkaE\n#\u9632\u5f3e\u5c11\u5e74\u56e3 #\u8840\u6c57\u6d99"
"RT @79rofylno: \uc6b0'3'\nhttps:\/\/t.co\/4nrRh4l8IG\n\n#\uc7ac\uc708 #jaewin https:\/\/t.co\/ZdLk5BiHDn"
"Done with taxation supporting doc but still so mannnyyy work tak siap lagi \ud83d\ude2d"
"RT @RenfuuP: \ud64d\uc900\ud45c \uc9c0\uc9c0\uc728 20%\uc778 \uac1d\uad00\uc801 \uc774\uc720 https:\/\/t.co\/rLC9JCZ9XO"
"RT @iniemailku83: Tidak butuh lelaki yg kuat. Tp butuh lelaki yg bisa membuat celana dalam basah. Haaa"
"5\/10 02\u6642\u66f4\u65b0\uff01BUMP OF CHICKEN(\u30d0\u30f3\u30d7)\u306e\u30c1\u30b1\u30c3\u30c8\u304c27\u679a\u30ea\u30af\u30a8\u30b9\u30c8\u4e2d\u3002\u4eca\u3059\u3050\u30c1\u30a7\u30c3\u30af https:\/\/t.co\/acFozBWrcm #BUMPOFCHICKEN #BUMP #\u30d0\u30f3\u30d7"
"---https:\/\/t.co\/DLwBKzniz6"
"RT @KamalaHarris: Our kids deserve the truth. Their generation deserves access to clean air and clean water.\nhttps:\/\/t.co\/FgVHIx4FzY"
"#105 UNIVERSITY East Concourse Up Escalator is now Out of Service"

因此,这些将是我们的测试文档。第二件事是我们非常需要实际的倒排查询。我决定从推文中提取的前 1000 个最常用词中生成 100K 个随机查询,每个查询包含 2 个 AND 关键词和 2 个 NOT 关键词。这是我得到的结果(由于 Manticore Search 和 Elasticsearch 的查询语法略有不同,我不得不构建两组类似的查询):

$ head queries_100K_ms.txt queries_100K_es.txt
== queries_100K_ms.txt ==
ll 0553 -pop -crying
nuevo against -watch -has
strong trop -around -would
most mad -cosas -guess
heart estamos -is -esc2017
money tel -didn -suis
omg then -tel -get
eviniz yates -wait -mtvinstaglcabello
9 sa -ao -album
tout re -oi -trop

== queries_100K_es.txt ==
ll AND 0553 -pop -crying
nuevo AND against -watch -has
strong AND trop -around -would
most AND mad -cosas -guess
heart AND estamos -is -esc2017
money AND tel -didn -suis
omg AND then -tel -get
eviniz AND yates -wait -mtvinstaglcabello
9 AND sa -ao -album
tout AND re -oi -trop

这些查询本身没有意义,但为了性能测试的目的,它们是有意义的。

插入查询

下一步是将这些查询插入到 Manticore Search 和 Elasticsearch 中。这可以很容易地通过以下方式完成:

$ IFS=$'\n'; time for n in `cat queries_100K_ms.txt`; do mysql -P9314 -h0 -e "insert into pq values('$n');"; done;
real 6m37.202s
user 0m20.512s
sys 1m38.764s

Elasticsearch

$ IFS=$'\n'; id=0; time for n in `cat queries_100K_es.txt`; do echo $n; curl -s -XPUT http://localhost:9200/pq/docs/$id?refresh -H 'Content-Type: application/json' -d "{\"query\": {\"query_string\": {\"query\": \"$n\" }}}" > /dev/null; id=$((id+1)); done;
...

mayo AND yourself -ils -tan
told AND should -man -go
well AND week -won -perfect

real 112m58.019s
user 11m4.168s
sys 6m45.024s

令人惊讶的是,将 600K 个查询插入到 Elasticsearch 中所花费的时间是 Manticore Search 的 17 倍。

让我们看看搜索性能如何。为了避免得出错误的结论,我们首先达成几个假设:

  • Manticore Search 和 Elasticsearch 的配置都仅使用开箱即用的配置或按照基本文档进行配置,即两种配置都没有进行任何优化
  • 但由于 Elasticsearch 默认是多线程的,而 Manticore Search 不是,我向 Manticore Search 的配置中添加了 "dist_threads = 8" 以平衡机会
  • 其中一个测试将充分利用服务器以测量最大吞吐量。在该测试中,如果我们看到任一引擎内置的多线程功能未能提供最大吞吐量,我们将通过添加多进程到多线程中来帮助它
  • 足够的 Java 堆内存对 Elasticsearch 来说很重要。索引在磁盘上仅占用 23MB,但我们让 Elasticsearch 使用 32GB 的 RAM 以防万一
  • 对于测试,我们将使用这个开源的压力测试工具 - https://github.com/Ivinco/stress-tester
  • 两个测试插件每个子进程只打开一次连接,并获取所有找到的文档,这包括在延迟中

运行文档

Elasticsearch

那么,让我们开始吧,首先了解 Elasticsearch 能提供多大的最大吞吐量。我们知道通常吞吐量随着批量大小的增加而增加,让我们用并发数 = 1 (-c=1) 来测量,基于从 archive.org 下载的 600K 条数据中的前 10000 条文档 (--limit=10000) 来构建查询语料库。

$ for batchSize in 1 4 5 6 10 20 50 100 200; do ./test.php --plugin=es_pq_twitter.php --data=/home/snikolaev/twitter/text -b=$batchSize -c=1 --limit=10000 --csv; done;
concurrency;batch size;total time;throughput;elements count;latencies count;avg latency, ms;median latency, ms;95p latency, ms;99p latency, ms
1;1;34.638;288;10000;10000;3.27;2.702;6.457;23.308
1;4;21.413;466;10000;10000;8.381;6.135;19.207;57.288
1;5;20.184;495;10000;10000;9.926;7.285;22.768;60.953
1;6;19.773;505;10000;10000;11.634;8.672;26.118;63.064
1;10;19.984;500;10000;10000;19.687;15.826;57.909;76.686
1;20;23.438;426;10000;10000;46.662;40.104;101.406;119.874
1;50;30.882;323;10000;10000;153.864;157.726;232.276;251.269
1;100;69.842;143;10000;10000;696.237;390.192;2410.755;2684.982
1;200;146.443;68;10000;10000;2927.343;3221.381;3433.848;3661.143

img确实,吞吐量在增加,但仅到批量大小 6-10 时,之后就会下降。总体吞吐量看起来非常弱。另一方面,延迟表现非常好,尤其是对于最小的批量大小 - 批量大小 1-10 时延迟为 3-20 毫秒。同时,我注意到服务器仅使用了 25%,这对延迟来说是好的,但我们还想看到最大吞吐量,因此让我们用并发数 = 8 重新运行测试:

$ for batchSize in 1 4 5 6 10 20 50 100 200; do ./test.php --plugin=es_pq_twitter.php --data=/home/snikolaev/twitter/text -b=$batchSize -c=8 --limit=10000 --csv; done;
8;1;12.87;777;10000;10000;8.771;4.864;50.212;63.838
8;4;7.98;1253;10000;10000;24.071;12.5;77.675;103.735
8;5;7.133;1401;10000;10000;27.538;15.42;79.169;99.058
8;6;7.04;1420;10000;10000;32.978;19.097;87.458;111.311
8;10;7.374;1356;10000;10000;57.576;51.933;117.053;172.985
8;20;8.642;1157;10000;10000;136.103;125.133;228.399;288.927
8;50;11.565;864;10000;10000;454.78;448.788;659.542;781.465
8;100;25.57;391;10000;10000;1976.077;1110.372;6744.786;7822.412
8;200;52.251;191;10000;10000;7957.451;8980.085;9773.551;10167.927
1;200;146.443;68;10000;10000;2927.343;3221.381;3433.848;3661.143

img好很多:服务器已完全加载,吞吐量现在也更高,延迟也有所改善。我们仍然看到 Elasticsearch 在批量大小 6-10 之后性能下降:

让我们坚持使用批量大小 = 6,看起来这是本次测试的最佳值。让我们进行更长时间的测试并处理 100K 条文档 (--limit=100000):

$ ./test.php --plugin=es_pq_twitter.php --data=/home/snikolaev/twitter/text -b=6 -c=8 --limit=100000
Time elapsed: 0 sec, throughput (curr / from start): 0 / 0 rps, 0 children running, 100000 elements left
Time elapsed: 1.001 sec, throughput (curr / from start): 1510 / 1510 rps, 8 children running, 98440 elements left
Time elapsed: 2.002 sec, throughput (curr / from start): 1330 / 1420 rps, 8 children running, 97108 elements left
Time elapsed: 3.002 sec, throughput (curr / from start): 1433 / 1424 rps, 8 children running, 95674 elements left

...

Time elapsed: 67.099 sec, throughput (curr / from start): 1336 / 1465 rps, 8 children running, 1618 elements left
Time elapsed: 68.1 sec, throughput (curr / from start): 1431 / 1465 rps, 8 children running, 184 elements left

FINISHED. Total time: 68.345 sec, throughput: 1463 rps
Latency stats:
count: 100000 latencies analyzed
avg: 31.993 ms
median: 19.667 ms
95p: 83.06 ms
99p: 102.186 ms

Plugin's output:
Total matches: 509883
Count: 100000

我们得到了一个数字 - Elasticsearch 每秒可以处理 1463 条文档,平均延迟约为 32 毫秒。 前 100K 条文档与索引中的 100K 条查询相比,产生了 509883 次匹配。我们需要这个数字,以便与 Manticore Search 进行比较,确保它们之间的差异不会太大,从而进行正确的比较。

Manticore Search

现在让我们看看 Manticore Search:

$ for batchSize in 1 4 5 6 10 20 50 100 200 300; do ./test.php --plugin=pq_twitter.php --data=/home/snikolaev/twitter/text -b=$batchSize -c=1 --limit=10000 --csv; done;
concurrency;batch size;total time;throughput;elements count;latencies count;avg latency, ms;median latency, ms;95p latency, ms;99p latency, ms
1;1;54.343;184;10000;10000;5.219;5.207;5.964;6.378
1;4;14.197;704;10000;10000;5.441;5.416;6.117;6.596
1;5;11.405;876;10000;10000;5.455;5.444;6.041;6.513
1;6;9.467;1056;10000;10000;5.466;5.436;6.116;6.534
1;10;5.935;1684;10000;10000;5.679;5.636;6.364;6.845
1;20;3.283;3045;10000;10000;6.149;6.042;7.097;8.001
1;50;1.605;6230;10000;10000;7.458;7.468;8.368;8.825
1;100;1.124;8899;10000;10000;10.019;9.972;11.654;12.542
1;200;0.95;10530;10000;10000;17.369;17.154;20.985;23.355
1;300;1.071;9334;10000;10000;29.058;28.667;34.449;42.163

imgManticore Search 的吞吐量在批量大小达到 200 时继续增长,同时延迟并没有像 Elasticsearch 那样显著增加(从毫秒到秒)。不过,对于批量大小 1 来说,它的延迟比 Elasticsearch 高 2 毫秒。

与Elasticsearch相比,吞吐量甚至更加不同。我们可以看到,当批次大小小于20时,它的吞吐量要低得多,但之后当Elasticsearch开始降级时,Manticore Search的吞吐量增长到每秒10K文档。

与最初的Elastic测试一样,我们看到服务器并未被充分利用(在这种情况下为80%,而Elastic为25%)。让我们将并发数从1增加到2,以更好地加载服务器:

$ for batchSize in 1 4 5 6 10 20 50 100 200 300; do ./test.php --plugin=pq_twitter.php --data=/home/snikolaev/twitter/text -b=$batchSize -c=2 --limit=10000 --csv; done;
concurrency;batch size;total time;throughput;elements count;latencies count;avg latency, ms;median latency, ms;95p latency, ms;99p latency, ms
2;1;44.715;223;10000;10000;8.445;8.017;12.682;15.386
2;4;11.852;843;10000;10000;8.906;8.493;13.411;16.085
2;5;9.468;1056;10000;10000;9.001;8.566;13.476;16.284
2;6;8.004;1249;10000;10000;9.073;8.626;13.647;15.848
2;10;4.931;2028;10000;10000;9.202;8.799;13.525;15.622
2;20;2.803;3567;10000;10000;9.924;9.352;15.322;18.252
2;50;1.352;7395;10000;10000;12.28;11.946;18.048;27.884
2;100;0.938;10659;10000;10000;17.098;16.832;22.914;26.719
2;200;0.76;13157;10000;10000;27.474;27.239;35.103;36.61
2;300;0.882;11337;10000;10000;47.747;47.611;63.327;70.952

img这将批次大小为200的吞吐量从10K提升到了13157文档每秒,但延迟也增加了。

让我们保持这个批次大小和并发数,并运行一个包含100K文档的扩展测试:

$ ./test.php --plugin=pq_twitter.php --data=/home/snikolaev/twitter/text -b=200 -c=2 --limit=100000
Time elapsed: 0 sec, throughput (curr / from start): 0 / 0 rps, 0 children running, 100000 elements left
Time elapsed: 1.001 sec, throughput (curr / from start): 12587 / 12586 rps, 2 children running, 87000 elements left
Time elapsed: 2.002 sec, throughput (curr / from start): 12990 / 12787 rps, 2 children running, 73800 elements left
Time elapsed: 3.002 sec, throughput (curr / from start): 13397 / 12991 rps, 2 children running, 60600 elements left
Time elapsed: 4.003 sec, throughput (curr / from start): 12992 / 12991 rps, 2 children running, 47600 elements left
Time elapsed: 5.004 sec, throughput (curr / from start): 12984 / 12989 rps, 2 children running, 34600 elements left
Time elapsed: 6.005 sec, throughput (curr / from start): 12787 / 12956 rps, 2 children running, 21800 elements left
Time elapsed: 7.005 sec, throughput (curr / from start): 12999 / 12962 rps, 2 children running, 8800 elements left

FINISHED. Total time: 7.722 sec, throughput: 12949 rps
Latency stats:
count: 100000 latencies analyzed
avg: 28.631 ms
median: 28.111 ms
95p: 37.679 ms
99p: 44.615 ms

Plugin's output:
Total matches: 518874
Count: 100000

我们可以看到:

  • 总匹配数差异不大(519K与Elastic的510K),这意味着我们正确地进行了比较,细微的差异可能是由于文本分词的微小差异造成的
  • 吞吐量为每秒12949文档
  • 平均延迟约为29毫秒

以下两张图表显示了Elasticsearch和Manticore Search在最大吞吐量下的对比。
img
中位数(50百分位)延迟Elasticsearch略好几毫秒,平均延迟Manticore Search稍好一些。95百分位和99百分位延迟差异很大:Elasticsearch在这里胜出。
img
但就吞吐量而言,Manticore Search的性能比Elasticsearch高出8倍多。

结论

  • 如果您需要每秒处理数千个文档并且可以接受几十毫秒的延迟(即32毫秒),Manticore Search可以提供更高的吞吐量:约13K rps与Elastic的1463 rps
  • 如果您的目标是极低且稳定的延迟(几毫秒)并且您没有大量文档或可以接受将负载分布在许多服务器上(例如8个Elasticsearch服务器而不是运行Manticore Search的1个服务器)以提供所需的延迟,Elasticsearch可以在每秒288文档的吞吐量下提供低至3.27ms的延迟。Manticore Search只能在188 rps时提供5.2ms或在13K rps时提供29ms

安装Manticore Search

安装Manticore Search