Benchmarking der neuen Solr ‚Near Realtime‘-Verbesserungen.
Ich habe an der Integration von Solr in das Lucene-Benchmark-Modul gearbeitet und den Code so weit gebracht, dass ich einige anständige Solr NRT-Tests durchführen kann. Vor kurzem habe ich auch an der Umgestaltung des Solr UpdateHandlers gearbeitet und ich möchte mir einige der Ergebnisse dieser Änderungen genauer ansehen. Die Aktualisierungen des UpdateHandlers brachten eine Reihe von Vorteilen mit sich, von denen die meisten die Fähigkeit von Solr, NRT ohne clevere (und in der Regel komplizierte) Workarounds durchzuführen, deutlich verbessern. Wahrscheinlich muss ich noch einige Dinge überprüfen, einige I’s setzen und T’s ankreuzen, aber ich dachte, ich teile einen ersten Einblick in meine Untersuchung der Leistungsänderungen.
Um zu sehen, wie sich die jüngsten Änderungen auf die Leistung von Solr ausgewirkt haben, beschloss ich, die neueste Version von Solr trunk mit einer Version zu vergleichen, die kurz vor der Aufnahme der UpdateHandler-Änderungen in Solr trunk erstellt wurde. Ich nahm den Algorithmus für die NRT-Tests in Lucene und begann, ihn für die Verwendung mit Solr zu optimieren. Ich habe meinen Intel Core 2 Quad @ 2,66 GHz für meinen Test verwendet. Er wird zwar alt, aber er kann immer noch ein paar Bits bewegen. Die Ergebnisse dieser Untersuchung folgen.
Der Prozess
Als Erstes habe ich also die gewünschte Version von Solr (Revision 1141518) sowie die neueste Version von Solr trunk (zu diesem Zeitpunkt Revision 1144942) ausgecheckt. Dann wendete ich meinen Solr-Benchmark-Patch auf jeden Checkout an und erstellte den folgenden Benchmark-Algorithmus:
{ StartSolrServer SolrClearIndex [ "PreLoad" { SolrAddDoc > : 50000] : 4 SolrCommit Wait(220) [ "WarmupSearches" { SolrSearch > : 4 ] : 1 # Get a new near-real-time reader, sequentially as fast as possible: [ "UpdateIndexView" { SolrCommit > : *] : 1 & # Index with 2 threads, each adding 100 docs per sec [ "Indexing" { SolrAddDoc > : * : 100/sec ] : 2 & # Redline search (from queries.txt) with 4 threads [ "Searching" { SolrSearch > : * ] : 4 & # Wait 60 sec, then wrap up Wait(60) } StopSolrServer RepSumByPref Indexing RepSumByPref Searching RepSumByPref UpdateIndexView
Dieser Algorithmus startet den Solr-Beispielserver (ich verwende für diesen Test die Standardeinstellungen), löscht den aktuellen Index und lädt dann 200.000 Wikipedia-Dokumente in den Index. Das ist bei weitem kein großer Index, aber er wird uns helfen, die Zeiteinflüsse durch verschiedene Commit- und Merge-Aktivitäten gut genug zu sehen, um einige einfache Urteile zu fällen. Nach dem Commit wartet der Algorithmus 220 Sekunden. Dies ist bei der neuesten Trunk-Version erforderlich, da Commits nicht mehr auf den Abschluss von Merges im Hintergrund warten, so dass wir lange genug warten, bis diese Merges abgeschlossen sind und den Benchmark nicht stören. Bei der älteren Version ist dies nicht notwendig – der Commit-Aufruf wartet, bis die Hintergrundzusammenführungen abgeschlossen sind, um zurückzukehren.
Als Nächstes führen wir 4 Suchvorgänge durch, um den Index ein wenig aufzuwärmen, bevor wir einen Hintergrund-Thread starten, der fortlaufend und so schnell wie möglich nacheinander Commits aufruft. Dann starten wir zwei weitere Hintergrund-Threads, die jeweils Wikipedia-Dokumente mit einer Zielrate von 100 Dokumenten pro Sekunde hinzufügen. Dann starten wir 4 Hintergrund-Threads, die jeweils so schnell wie möglich Solr abfragen. Wir setzen dieses Sperrfeuer eine Minute lang fort und sehen uns dann die Ergebnisse an.
Das Vorher-Bild
Dies ist ein Diagramm der „Aktualisierungszeiten“ – die Zeit, die benötigt wurde, um jede Übertragung durchzuführen und eine neue Ansicht des Index zu öffnen. In diesem Fall wurde der Index in der Minute, in der wir den Benchmark laufen ließen, 400 Mal aktualisiert. Im Großen und Ganzen sieht die Aktualisierungszeit gar nicht so schlecht aus. Die durchschnittliche „Aktualisierungszeit“ beträgt nur 150 ms. Da Lucene und Solr hauptsächlich pro Segment arbeiten, kann dieser Prozess natürlich ziemlich schnell sein. Und dies ist wirklich ein ziemlich kleiner Index. Allerdings gibt es in dieser Minute eine beunruhigende Spitze – eine „Refresh“-Zeit dauerte etwa 23 Sekunden! Der Grund dafür ist, dass die Übergabe eine Zusammenführung im Hintergrund ausgelöst hat und Solr gewartet hat, bis diese Zusammenführung im Hintergrund abgeschlossen ist, bevor ein neuer IndexSearcher geöffnet und die Übergabesperre freigegeben wurde. Aber es kommt noch schlimmer: Nicht nur die Aktualisierungszeit war schmerzhaft, sondern während die Übertragungssperre gehalten wurde, konnte keiner unserer beiden Indizierungs-Threads ein Dokument in den Index aufnehmen! Sie wurden praktisch abgewürgt. In dieser Minute waren wir nur in der Lage, die Wikipedia-Dokumente mit 13,91 Dokumenten pro Sekunde zu indizieren. Das liegt weit unter unserem Ziel von 100 Dokumenten pro Sekunde für jeden Thread! Außerdem gab es einen sehr großen Zeitblock, in dem überhaupt keine Indizierung stattfand. Weniger beunruhigend ist, dass unsere 4 Threads in der Lage waren, 11,24 Abfragen pro Sekunde durchzuführen (dies kann je nach der ‚Herausforderung‘ der Datei queries.txt stark variieren) [UPDATE 4.9.2011: die Suchrate ist aufgrund eines Problems mit dem ursprünglichen Benchmark sehr niedrig – viele Abfragen endeten mit Fehlern – ohne so viele Fehler steigt die Suchleistung drastisch an]. Aber insgesamt ist dies keine optimale Nutzung der Ressourcen dieses Desktops.
Das Nachher-Bild
Jetzt versuchen wir es mit dem neuen UpdateHandler. Der neue UpdateHandler blockiert keine Aktualisierungen mehr, während eine Übertragung im Gange ist. Er wartet auch nicht mehr, bis die Zusammenführung im Hintergrund abgeschlossen ist, bevor er einen neuen IndexSearcher öffnet und zurückkehrt.
Die Ergebnisse sind nicht schlecht – eine niedrige durchschnittliche Aktualisierungszeit von 116,74 ms, aber auch kein 23-Sekunden-Spike. Es gibt zwar immer noch Spitzen, aber sie sind nicht allzu häufig und bleiben schlimmstenfalls unter 2,5 Sekunden. Mikro-Spikes.
Aber es kommt noch besser: Unsere Indizierungsrate liegt jetzt bei 125,48 Dokumenten pro Sekunde (gegenüber 13,91 vorher). Das ist eine fantastische Steigerung – und wahrscheinlich ohne große Lücken, in denen keine Indizierungsaktivitäten stattfanden. Die Suchleistung sank auf 2,8 Abfragen pro Sekunde (von 11,24), aber das liegt zweifellos größtenteils an der zusätzlichen Indizierungsaktivität, die stattfinden konnte. Die CPUs konnten jetzt viel mehr Arbeit erledigen als vorher. Da die Indizierungs-Threads mehr CPU-Ressourcen beanspruchten, wurden den Abfragen weniger Ressourcen zugewiesen.
Das Nachher-Bild mit Lucene NRT
Während ich den UpdateHandler änderte, war es eine einfache, natürliche Erweiterung, die Verwendung der NRT-Funktion von Lucene zuzulassen, wenn neue Ansichten des Indexes geöffnet werden. Diese Funktion ermöglicht es Ihnen, bestimmte Schritte zu überspringen, die bei einer vollständigen Übertragung durchgeführt werden. Der Nachteil ist, dass nichts garantiert auf einem stabilen Speicher liegt, aber der Vorteil sind sehr schnelle Aktualisierungszeiten.
Um diesen Vorteil in Solr zu nutzen, haben wir ein neues Konzept eingeführt, das ich „Soft Commit“ nenne. Ein Soft-Commit kehrt schnell zurück, überträgt die Dokumente aber nicht dauerhaft in den Stable Storage. Sie müssen auch gelegentlich ‚harte‘ Commits aufrufen, um in den stabilen Speicher zu übertragen. Ein ’soft‘ Commit aktualisiert jedoch die Ansicht des Index durch SolrCore.
Sie können in solrconfig.xml jetzt auch eine ‚weiche‘ automatische Übergabe einrichten. So können Sie z.B. einen Soft-Commit einrichten, der etwa jede Sekunde stattfindet, und einen Standard-Commit, der alle 5 Minuten stattfindet.
Um die neue Soft-Commit-Funktion zu testen, habe ich die Hintergrund-Commit-Zeile im Algorithmus in:
[ „UpdateIndexView“ { SolrCommit(soft) >: *] : 1 &
und führten den Benchmark erneut aus.
Wenn Sie sich das Diagramm ansehen, sehen Sie, dass die Mikrospitzen vielleicht etwas seltener auftreten. Noch wichtiger ist jedoch, dass die durchschnittliche Aktualisierungszeit von etwa 117 ms auf nur 49 ms gesunken ist. Im alten Fall konnten wir die Ansicht 6,67 Mal in einer Minute aktualisieren – im neuen Fall ohne Soft Commit waren es 8,56 Mal pro Minute – und im neuen Fall *mit* Soft Commit konnten wir die Indexansicht 22,18 Mal in einer Minute aktualisieren. Außerdem konnten wir weiterhin 130 Dokumente pro Sekunde indizieren und dabei 3,64 Abfragen pro Sekunde durchführen.
Ein weiterer Benchmark
Lassen Sie uns einen weiteren interessanten Benchmark durchführen. In den vorherigen Tests haben wir versucht, 200 Dokumente pro Sekunde zu indizieren – was mein armer Rechner nicht geschafft hat. Wir haben also wirklich nur das Maximum an Indizierung erreicht. In diesem Test habe ich die Indizierungsrate auf einen Wert eingestellt, der erreichbar ist, und nicht auf einen Wert, der die CPU völlig überfordert – und ich habe mir die Ergebnisse angesehen. Bei diesem Benchmark wurden wieder „weiche“ Übertragungen verwendet.
Die Dinge sehen ziemlich gut aus – ein Durchschnitt von nur 7,5 ms pro Aktualisierung. Das entspricht einer Aktualisierungsrate von 132,6 Mal pro Sekunde. Auch die Abfragen pro Sekunde sind von etwas mehr als dreieinhalb auf fast 6 pro Sekunde gestiegen. Das ist ein interessantes Ergebnis, das zeigt, dass es noch einige interessante Untersuchungen mit verschiedenen Einstellungen und Algorithmusänderungen zu tun gibt.
Fazit
Es sieht so aus, als ob NRT in Solr einen schönen Schritt nach vorne gemacht hat und diese Verbesserungen in Solr 4.0 verfügbar sein werden. Es gibt jedoch noch mehr zu tun – bestimmte Funktionen, wie Facettierung und Funktionsabfragen (edit: wenn Sie ord verwenden), funktionieren noch nicht alle pro Segment. Das bedeutet, dass die Verwendung dieser Funktionen beim ‚Auffrischen‘ der Indexansicht mehr Zeit in Anspruch nehmen kann, als Ihnen lieb ist. Wir hoffen, dass wir die meisten dieser Fälle in Zukunft noch weiter verbessern können. Aber selbst wenn Sie diese Funktionen verwenden, können Sie mit diesen Änderungen in vielen Fällen die Latenzzeit zwischen Indexierung und Suche deutlich verringern, ohne auf clevere Tricks wie das Jonglieren mit SolrCore zurückgreifen zu müssen.
Ich hoffe, dies war ein unterhaltsamer Einblick in einige der Verbesserungen. Es gibt noch eine Menge zu untersuchen.