Introduction
Percolate Queries को Persistent Queries, Prospective Search, दस्तावेज़ रूटिंग, उल्टे खोज या अवरुद्ध खोज के नाम से भी जाना जाता है।
सामान्य रूप से खोज करने का तरीका यह है कि हम उन दस्तावेजों को संग्रहीत करें जिनमें हम खोज करना चाहते हैं और उनके खिलाफ प्रश्न करें। हालाँकि, कुछ मामले ऐसे हैं जब हम एक आगंतुक नए दस्तावेज़ पर एक प्रश्न लागू करना चाहते हैं ताकि मेल खाने का संकेत मिल सके। कुछ परिदृश्य ऐसे हैं जहाँ यह आवश्यक है। उदाहरण के लिए, एक निगरानी प्रणाली केवल डेटा एकत्र नहीं करती है, बल्कि विभिन्न घटनाओं पर उपयोगकर्ता को सूचित करने की अपेक्षाएँ भी होती हैं। यह किसी मैट्रिक के लिए कुछ थ्रेशोल्ड तक पहुँचने या मॉनिटर किए गए डेटा में दिखाई देने वाले किसी विशेष मान का हो सकता है। एक और समान मामला समाचार संग्रहण है। आप उपयोगकर्ता को किसी ताजा समाचार के बारे में सूचित कर सकते हैं, लेकिन उपयोगकर्ता केवल कुछ श्रेणियों या विषयों के बारे में सूचित होना चाह सकता है। आगे बढ़ते हुए, वे केवल कुछ “कीवर्ड” में रुचि रख सकते हैं।
Percolate Queries करने वाले एप्लिकेशन अक्सर उच्च लोड का सामना करते हैं: उन्हें प्रति सेकंड हजारों दस्तावेज़ों को प्रोसेस करना होता है और प्रत्येक दस्तावेज़ के खिलाफ परीक्षण करने के लिए सैकड़ों हजारों प्रश्न हो सकते हैं।
Elasticsearch और Manticore Search दोनों Percolate Queries प्रदान करते हैं।
जबकि Percolate Queries Elasticsearch में कुछ समय से मौजूद हैं, Manticore में इसे 2.6.0 में जोड़ा गया, जो कि Sphinx में अभी भी गायब है। आज मैं कुछ प्रदर्शन माप लेने के लिए कार्यों की संख्याओं को निर्धारित करना चाहूंगा जो प्रत्येक प्रौद्योगिकी प्रदान कर सकती है।
Test case
बहुत सारी कंपनियाँ जो सोशल मीडिया मार्केटिंग उद्योग में काम करती हैं, आमतौर पर 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 है और कोई नहीं जानता कि एक ट्विटर संदेश सामान्यतः कैसा दिखता है):
$ 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"
तो ये दस्तावेज़ होंगे जिनका हम परीक्षण करेंगे। दूसरी चीज़ जो हमें बेहद जरूरत है, वह वास्तव में percolate queries हैं। मैंने ट्वीट्स में से शीर्ष 1000 सबसे लोकप्रिय शब्दों में से 100K यादृच्छिक प्रश्न उत्पन्न करने का निर्णय लिया, प्रत्येक प्रश्न में 2 AND कीवर्ड और 2 NOT कीवर्ड होंगे। यहाँ मैंने जो पाया है (query syntax Manticore Search और Elasticsearch के बीच थोड़ा भिन्न है इसलिए मुझे समान प्रश्नों के 2 सेट बनाने पड़े):
$ 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
ये प्रश्न बेकार हैं, लेकिन प्रदर्शन परीक्षण के लिए यह समझ में आता है।
Inserting the queries
अगला कदम है प्रश्नों को Manticore Search और Elasticsearch में सम्मिलित करना। यह आसानी से इस तरह किया जा सकता है:
Manticore Search
$ 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
अचरज की बात यह है कि Elasticsearch में 600K प्रश्नों को सम्मिलित करने में Manticore Search की तुलना में 17 गुना अधिक समय लगा।
आइए देखें कि खोज प्रदर्शन कैसा है। निष्कर्ष में गलती न करने के लिए, पहले कुछ धारणाओं पर सहमत हो जाएं:
- दोनों Manticore Search और Elasticsearch के कॉन्फ़िगर जैसे हैं जैसे वे बॉक्स से बाहर आते हैं या आधारभूत दस्तावेज़ीकरण के अनुसार, यानी कॉन्फ़िगर में कोई अनुकूलन नहीं किया गया है
- लेकिन चूंकि Elasticsearch डिफ़ॉल्ट रूप से मल्टी-थ्रेडेड है और Manticore Search नहीं है, मैंने Manticore Search के कॉन्फ़िगरेशन में “dist_threads = 8” जोड़ा है ताकि मौके को समान बनाया जा सके
- परीक्षणों में से एक यह होगा कि सर्वर का पूरी तरह से उपयोग किया जाए अधिकतम थ्रूपुट को मापने के लिए। इस परीक्षण में यदि हम देखते हैं कि इंजनों में से किसी एक में अंतर्निहित मल्टी-थ्रेडिंग अधिकतम थ्रूपुट नहीं देती है, तो हम इसे मल्टी-प्रोसेसिंग को मल्टी-थ्रेडिंग में जोड़कर मदद करेंगे।
- Elasticsearch के लिए पर्याप्त जावा हीप आकार महत्वपूर्ण है। अनुक्रमणिका डिस्क पर केवल 23MB लेती है, लेकिन हम Elasticsearch को 32GB RAM का उपयोग करने देंगे, केवल सावधानी बरतने के लिए।
- परीक्षणों के लिए हम इस ओपन-सोर्स स्ट्रेस टेस्टिंग टूल का उपयोग करेंगे - https://github.com/Ivinco/stress-tester
- दोनों परीक्षण प्लगइन्स केवल एक बार प्रति बच्चे कनेक्शन खोलते हैं और सभी पाए गए दस्तावेज़ों को लाते हैं जो लेटेंसी में शामिल है।
दस्तावेज़ चलाना
Elasticsearch
तो चलिए शुरू करते हैं और सबसे पहले यह समझते हैं कि Elasticsearch अधिकतम थ्रूपुट क्या दे सकता है। हम जानते हैं कि सामान्यतः जब आप बैच का आकार बढ़ाते हैं, तो थ्रूपुट बढ़ता है, चलिए इसे समवर्तीता = 1 (-c=1
) के तहत पहले 10000 दस्तावेज़ों (--limit=10000
) के आधार पर मापते हैं जो 600K से डाउनलोड किए गए हैं और खोजों के संग्रह में उपयोग किए गए हैं।
$ 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
वास्तव में थ्रूपुट बढ़ता है, लेकिन केवल बैच के आकार 6-10 तक, फिर यह घटता है। और समग्र थ्रूपुट बहुत कमजोर दिखता है। दूसरी ओर, लेटेंसी बहुत अच्छी दिखती है, विशेष रूप से सबसे कम बैच के आकार संख्याओं के लिए - बैच के आकार 1 - 10 के लिए 3-20ms। साथ ही, मैं देख सकता हूं कि सर्वर केवल 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
बहुत बेहतर: सर्वर पूरी तरह से लोड हो गया है और थ्रूपुट अब अधिक है जैसे कि लेटेंसी भी। और हम अभी भी देखते हैं कि 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 दस्तावेज़ों को प्रोसेस कर सकता है औसत लेटेंसी ~32ms के साथ। पहले 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
Manticore Search का थ्रूपुट बैच के आकार 200 तक बढ़ता रहता है, इसी समय लेटेंसी इतनी अधिक नहीं बढ़ती (मिलीसेकंड से सेकंड तक) जैसा कि Elasticsearch के साथ। हालांकि, यह बैच के आकार 1 के लिए Elasticsearch से 2ms अधिक है।
The throughput is even more different comparing to Elasticsearch. We can see that it’s much lower for batch size < 20, but after that when Elasticsearch starts degrading Manticore Searchs’ throughput grows until 10K docs per second.
As well as with the initial Elastic’s test we see that the server is not fully utilized (80% in this case comparing to Elastic’s 25%). Let’s increase the concurrency from 1 to 2 to load the server better:
$ 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
This increased the throughput for batch size 200 from 10K to 13157 docs per second, the latency increased too though.
Let’s stick with this batch size and concurrency and run an extended test with 100K documents:
$ ./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
We can see that:
- Total matches doesn’t differ much (519K vs 510K in Elastic) which means we compare things properly, the slight difference is probably caused by minor difference in text tokenization
- The throughput is 12949 documents per second
- The average latency is ~29ms
The following 2 charts show comparison between Elasticsearch and Manticore Search at max throughput.
Median (50th percentile) latency is few milliseconds better in Elasticsearch, avg is a little bit better in Manticore Search. 95p and 99p latency differ a lot: Elasticsearch wins here.
But when it comes to the throughput Manticore Search outperforms Elasticsearch by more than 8 times.
Conclusions
- If you need to process thousands of documents per second and can afford tens of milliseconds latency (namely 32ms) Manticore Search can provide much higher throughput: ~13K rps vs 1463 rps with Elastic
- If your goal is extremely low and stable latency (few ms) and you don’t have lots of documents or can afford distributing your load among many servers (e.g. 8 Elasticsearch servers instead of 1 running Manticore Search) to provide the desired latency Elasticsearch can provide as low as 3.27ms latency at throughput of 288 docs per second. Manticore Search can only give 5.2ms at 188 rps or 29ms at 13K rps.