40 milionów wyszukiwań dziennie, setki tysięcy uaktualizacji indeksu czyni z Allegro.pl drugą co do wielkości wyszukiwarkę w Polsce. Nie wszyscy wiedzą, że główną wyszukiwarkę ofertową w Allegro.pl napędza Apache SOLR. Opowiemy o naszych doświadczeniach z optymalizacją zapytań do SOLRa, o tym jak udało nam się znacznie zmniejszyć czasy odpowiedzi i zwiększyć stabilność działania naszego searcha. W czasie prezentacji poruszymy kwestie związane z tworzeniem schematu, wykorzystaniem odpowiednich typów danych, wykorzystania cachea, efektywnego filtrowania i innych optymalizacji, które udało nam się wdrożyć z sukcesem.
Wygłoszono w trakcie czwartej edycji Allegro Tech Talks w Poznaniu.
Adam Dudczak - Starszy programista w Grupie Allegro, pracuje z Javą i technologiami powiązanymi od 2004 roku. Na codzień pracuje nad wyszukiwarką allegro.pl. Jeden z liderów Poznań JUG (http://www.jug.poznan.pl) i współorganizator konferencji GeeCON (http://geecon.org).
Przemysław Szeremiota - Starszy programista w Grupie Allegro od 2008. Zaczynał pracę w zespole wydajności programując w C/C++, przeżył fascynację Javascriptem i zaznał Javy, obecnie doskonale czuje się pływając w mieszance Basha i JQ. W wolnych chwilach przetłumaczył ponad 70 książek technicznych.
9. SOLR w Allegro to 44 piękne kobiety
SOLR w Allegro to ponad 100 serwerów
10. SOLR w Allegro to 44 piękne kobiety
SOLR w Allegro to ponad 100 serwerów
15 mln komunikatów dziennie
dodatkowe 10k komunikatów na sekundę
11. SOLR w Allegro to 44 piękne kobiety
SOLR w Allegro to ponad 100 serwerów
p99 czasów odpowiedzi 300ms
p99 czasów odpowiedzi >1000ms
15 mln komunikatów dziennie
dodatkowe 10k komunikatów na sekundę
12. SOLR w Allegro to 44 piękne kobiety
SOLR w Allegro to ponad 100 serwerów
p99 czasów odpowiedzi 300ms
p99 czasów odpowiedzi >1000ms
Indeks o wielkości 20GB
Indeks o wielkości 40GB
15 mln komunikatów dziennie
dodatkowe 10k komunikatów na sekundę
13. Silniki wyszukiwawcze w Allegro.pl
1. Apache SOLR standalone jako podstawa głównych
indeksów naszej wyszukiwarki (11 indeksów)
2. Cluster SOLR CLOUD wykorzystywany do indeksów
pomocniczych (10 indeksów)
3. Kilkadziesiąt clustrów Elastic Search wykorzystywany
przez zespoły developerskie jako rozwiązanie as a service
13
15. BEZ PRACY…
15
schemaless (adjective sche·ma·less -ləs): definiowanie schematu
bezwiednie, nieświadomie
• Ułatwia szybką implementację wyszukiwarki w aplikacji
• Nieźle nadaje się do niektórych typowych/modnych zastosowań
wyszukiwarek (logi, time-series)
16. … NIE MA KOŁACZY!
16
• Schemat w SOLR istnieje zawsze
• Projektowanie schematu: projektowanie podstaw funkcjonalności
wyszukiwarki
• Trafność wyników wymaga słowników, synonimów, sprytnych
sposobów indeksowania, tokenizerów, filtrów, analizatorów…
• Szlifowanie schematu: szlifowanie skuteczności i wydajności
wyszukiwarki (pola indeksowane/pola docValues)
17. KAŻDY SZCZEGÓŁ SIĘ LICZY
17
“Od rana już czymś niedobrym pachniało w powietrzu”
18. KAŻDY SZCZEGÓŁ SIĘ LICZY
18
• Pola przeznaczone do wyszukiwania pełnotekstowego
(indeksowane)
• Pola przeznaczone do sortowania (docValues?)
• Pola przeznaczone do facetingu (docValues?)
• Pola wykorzystywane w filtrach (indeksowane/docValues)
20. TO CACHE OR NOT TO CACHE?
20
• document cache — cache pól składowanych odsyłanych w
ramach wyników zapytania
• query cache — cache całych wyników, z opcją “nadmiaru”
• filter cache — cache “filtrów” (zapytań cząstkowych)
Trzy gotowce do cachowania w SOLR:
21. TO CACHE OR NOT TO CACHE?
21
• Przyspiesza odsyłanie wyników, ale może lepiej nie odsyłać
kompletnych dokumentów, tylko identyfikatory do udekorowania
na froncie?
• Przechowywać w nim całe dokumenty, czy tylko wybrane pola?
Cache dokumentów
<documentCache class="solr.LFUCache" size="10" initialSize="1" autowarmCount="1"/>
22. TO CACHE OR NOT TO CACHE?
22
• Wyniki zapytań gotowe do ponownego użycia, ale czy potrafimy
wycelować w ten sam backend?
• Opcja “nadmiaru” przyspiesza pobieranie następnej strony
wyników, ale czy potrafimy wycelować w ten sam backend?
Cache zapytań
<queryResultCache class="solr.LFUCache" size="50" initialSize="50" autowarmCount="50"/>
23. TO CACHE OR NOT TO CACHE?
23
• Wyniki cząstkowe gotowe do ponownego użycia
• Niezbędny do operacji facetingu
Cache filtrów
<filterCache class="solr.LFUCache" size="600" initialSize="600" autowarmCount=“600" />
/select?q=name:nokia&fq=price:[30 TO 50]
/select?q=name:iphone&fq=published:[NOW-1DAY TO NOW]
/select?q=name:nokia&fq=price:[30 TO 40]&fq=type:auction
24. TO CACHE OR NOT TO CACHE?
24
• Duża wariancja w czasach wykonania dla nietrafionych zapytań/
filtrów
• Pamięć JVM
• CPU/czas w GC
• CPU/czas przy kompletowaniu zbioru wynikowego filtra
Koszt intensywnego cache-owania
28. NOT TOO CACHE!!!
28
• Wyniki zapytań lepiej cachować na load-balancerach
• Do pobierania następnych stron lepiej użyć mechanizmu cursor-
mark
• Filtry nieselektywne lepiej wyznaczać w obrębie query
fq={!cache=false}type:auction
• Filtry częstozmienne lepiej wyznaczać w trybie postfilter
fq={!frange cache=false cost=100 l=30 u=50}price
30. DocValue nie tylko do sort/facetingu
“DocValues are a way of recording field values
internally that is more efficient for some purposes,
such as sorting and faceting, than traditional
indexing.”
30
31. DocValues, ale o co chodzi?
tytuł:
nokia -> 1, 3, 5, 6, 7
lumia -> 3, 5,
cena:
120 -> 1
140 -> 3
500 -> 5
…
query: nokia lumia sort: cena ASC
31
32. DocValues, ale o co chodzi?
tytuł:
nokia -> 1, 3, 5, 6, 7
lumia -> 3, 5,
cena:
120 -> 1
140 -> 3
500 -> 5
…
pasujące wyniki: 3,5
32
Indeks odwrotny rządzi!
33. ale co z sortowaniem?
wyniki: 3, 5
cena:
140 -> 3, 6
500 -> 5, 10
…
cena_sort:
3 -> 140
5 -> 500
…
33
W przypadku indeksu odwrotnego musimy
przejrzeć całą listę termów i znaleźć
interesujące nas dokumenty
34. DocValues
• Dostępne w Lucene od wersji 4.0
• Polecane szczególnie do sortowania, facetowania, highligthingu
• Pozwalają bez większego problemu pobierać wartości z indeksu
<field name="state" type="string" indexed="false" stored="false" docValues="true" />
34
36. Postfiltry - krótkie wprowadzenie
• Postfiltr to filtr wykonywany po zakończeniu przetwarzania kryteriów z q i fq
• Ewaluowane kryterium może zawierać “kosztowną” logikę związaną z
zawartością dokumentu lub kontekstem użytkownika
&fq={!cache=false cost=200}price:200
&fq={!frange l=300 cache=false cost=200}price
&fq={!frange l=5 cache=false cost=200}div(log(popularity),sqrt(geodist()))
36
(source: http://yonik.com/advanced-filter-caching-in-solr/)
41. Dlaczego sortowanie indeksu?
• Sortowanie indeksu to zabójcza kombinacja w połączeniu z Early Terminating
Collectorem. O co chodzi?
41
42. Indeks bez sortowania
Segment 1:
- dokument 4: cena 430
- dokument 1: cena 100
- dokument 5: cena 540
Segment 2:
- dokument 6: cena 510
- dokument 3: cena 110
- dokument 2: cena 440
query: *:*, sort: cena ASC, rows=2
42
Musimy przejrzeć cały indeks, żeby znaleźć
2 najtańsze przedmioty
43. Indeks posortowany po cenie
Segment 1:
- dokument 1: cena 100
- dokument 4: cena 430
- dokument 5: cena 540
Segment 2:
- dokument 3: cena 110
- dokument 2: cena 440
- dokument 6: cena 510
query: *:*, sort: cena ASC, rows=2
43
Nie musimy przetwarzać wszystkiego
45. Dlaczego sortowanie indeksu?
• W praktyce niezbyt przydatne
• Jeden porządek sortowania w indeksie
• Wiele metod sortowania w aplikacji
• Ograniczona użyteczność Early Terminating Collectora w przypadku wielu
porządków sortowania
45
46. Czasem niespodzianki potrafią być miłe…
Rozmiar indeksu się zmniejszył, dzięki temu wszystko zaczęło
działać szybciej i zjadało mniej zasobów.
46
48. Stabilność przede wszystkim
• Profil wykorzystania wyszukiwarki
• Większości przypadków użytkownicy przechodzą od zapytań
ogólnych do szczegółowych (dodają filtry, zmieniają frazę)
• Oglądają nie więcej niż 20 stron wyników
• To jest nasz biznes!
48
49. Apache Solr i dalekie strony
• Apache Solr w przypadku przeglądania dalekich strony wyników
nie radzi sobie tak jakbyśmy chcieli
• Im dalej… tym wolniej i drożej
•start=0, rows=10
•start=1000, rows=10
•start=10000, rows=10
49
50. Separuj zapytania
• Zapytania o dalsze strony są kierowane do dedykowanej puli
maszyn
• Zapytania o ciężkie porządki sortowania do dedykowanej puli
maszyn
50
58. Motywacja
• Nie dajemy rady indeksować sygnałów zaangażowania w Lucene
• SOLR-owy mechanizm ExternalFileField jest powolny
• Mimo wstrętu do programowania, trza było zakasać rękawy
58
59. Sorted binary file field
• Sygnał zaangażowania to offerID -> wartość
• Lucene operuje na relacjach luceneID -> offer
• W Lucene relacja offerID -> luceneID jest niestała
59
offerID -> luceneID?
luceneID -> offerID?
60. luceneID offerID name category bidcount
1 1432 nokia Telefony 0
2 5760 audi Moto 0
3 9811 nike Moda 0
offerID listed
1432 60
5760 22
9811 342
lucene sort_by(“listed”):
foreach (luceneID in docset):
docset[luceneID].sortVal = listedSource.getValue(luceneID)
sort(docset, .sortVal)
luceneID offerID name category bidcount
1 1432 nokia Telefony 0
2 5760 audi Moto 0
3 9811 nike Moda 0
4 1432 nokia Telefony 1
65. Sorted Binary File Field - przygotowanie
Wartości:
1;3
2;56
3;89
from struct import pack
import sys
import json
for line in sys.stdin:
try:
vals = line.split(';')
sys.stdout.write(pack('>qf', long(vals[0]), float(vals[1])))
except:
sys.stderr.write("error: " + line)
65
66. Sorted Binary File Field
Wyliczanie sygnałów zaangażowania odbywa się offline (np.
Hadoop/MapReduce)
Umożliwia wynoszenie z indeksu innych często zmiennych
pól (cena? sztuk?)
Umożliwia stosunkowo łatwe importowanie dodatkowych
sygnałów
66
67. Do czego wykorzytujemy?
• Liczba odsłon oferty na listingu
• Liczba dodań oferty do koszyków
• Liczba dodań oferty do obserwowanych
• Liczba odsłon oferty
• Ocena sprzedawcy
• Jakość obrazków oferty
67