Apache Solr und Join-Abfragen
Die Vor- und Nachteile von Join-Abfragen in Apache Solr.
Experimentieren mit der Leistung von Solr-Join-Abfragen
Vor kurzem hatten wir einen Kunden, der im Voraus wissen wollte, ob Apache Solr Join-Unterstützung bietet und wenn ja, wie diese funktioniert. Natürlich wollte der Kunde eine Join-Abfrage auf die schmerzhafteste Weise verwenden, also machte ich mich daran, einen Prototyp zu erstellen. Natürlich stieß ich dabei auf einige Probleme, aber eine der Freuden der Arbeit bei Lucidworks ist, dass ich Zugang zu vielen der Leute habe, die den Code geschrieben haben – etwas, das ich sehr schätze! Dadurch, dass ich Zugang zu diesen Leuten habe, sehe ich viel klüger aus, als ich bin….
Jedenfalls habe ich auf meinem 2009er MacBook Pro einige eher unwissenschaftliche Experimente durchgeführt, aber genug, um mir ein Bild davon zu machen, wie die Join-Abfrage in einem bestimmten Fall funktioniert. Ich werde kurz beschreiben, was ich getan habe und was die Ergebnisse waren.
Die Einrichtung
Für dieses Experiment habe ich einen Index erstellt, der aus 26 Millionen Dokumenten besteht. Sie wurden in Gruppen aufgeteilt, ein Textdokument und 5 Metadaten-Dokumente. Das Textdokument enthielt 1K halb zufällige englische Wörter (die ich einfach aus einer „Liste aus dem Internet“ ausgewählt habe). Halb zufällig deshalb, weil ich sie ein wenig gewichtet habe, um mehr häufige als seltene Wörter zu haben, aber es hat sich herausgestellt, dass der Suchteil des Prozesses nicht der zeitaufwendigste ist, so dass wir das so ziemlich ignorieren können.
Es gibt 5 Metadaten-Dokumente, die mit dem Textdokument durch Solrs <uniqueKey> (analog zu einem RDBMS-Primärschlüssel) verbunden sind. Stellen Sie sich das so vor, dass die Metadaten-Dokumente einen Fremdschlüssel zum Textdokument <uniqueKey> haben. Die Metadaten-Dokumente hatten auch ein Integer-Feld im Bereich von 0-10.000. Der Zweck dieser Einrichtung bestand darin, Abfragen zu erstellen, die die Textdokumente zurückgaben, für die ein Metadatendokument existierte, das Zugriff gewährte. Die Komplexität der Zugriffsgewährung ist… äh… gering, ich habe einfach eine Bereichsabfrage gemacht. „Nicht realistisch“, sagen Sie. Da haben Sie Recht. Ich wollte nicht, dass eine komplexe Verarbeitung die Betrachtung von Joins behindert, also habe ich das Ganze einfach gehalten.
Die Form der Join-Abfrage war:
q=text_all:(1 to 3 random words)&fl=id,score&sort=score desc&fq={!join from=join_id to=id}access:[7434 TO 7514]
Siehe: http://wiki.apache.org/solr/Join. Ich habe ein kleines Kabelbaumprogramm geschrieben, um HTTP-Abfragen an die Solr-Instanz (4.x von vor ein paar Monaten) zu senden. Ich konnte die Anzahl der gleichzeitigen Threads konfigurieren, die Abfragen auslösen. Beachten Sie, dass ich dieses Formular getestet habe, weil es für den Kunden galt, aber ich vermute, dass die anderen Formulare das gleiche Problem haben.
Kleiner Haftungsausschluss
Wie ich bereits erwähnt habe, ist eine der Freuden der Arbeit bei Lucid, Zugang zu Leuten zu haben, die den Code genau verstehen. Ich unterhielt mich also mit dem Autor des Joins (Yonik Seeley) und fand natürlich heraus, dass das von mir getestete Szenario in Bezug auf die Leistung das schlechteste war. Joins sind O(num_terms_in_fields), und die Verwendung des <uniqueKey> als mein Join-Feld garantiert, dass es viele, viele, viele Begriffe gibt. Diese Ergebnisse sind also der ungünstigste Fall. Leider gehören sie auch zu den am häufigsten vorkommenden.
Themen | Abfragen (gesamt) | Durchschnittliche QTime(Sekunden) | Verstrichene Zeit | Abfragen/Sekunde |
1 | 20 | 4.9 | 98 | 0.2 |
2 | 40 | 5.9 | 123 | 0.3 |
5 | 100 | 15.3 | 310 | 0.3 |
10 | 200 | 31.5 | 649 | 0.3 |
Eine Anmerkung zu diesen eher kontra-intuitiven Zahlen. Sobald die CPU ihr Maximum erreicht hat, beginnt die QTime zu steigen, aber die QPS-Rate bleibt ziemlich konstant. Auf einem Dual-Core-Rechner sehen wir das bei 2 Threads. Die Zeilen mit 5 und 10 Threads (Client) zeigen einfach, dass jede einzelne Anfrage von Ende zu Ende länger dauert, aber es werden mehr Anfragen gleichzeitig von Solr bedient.
Als ich den Join-Teil herausgenommen habe, stieg die Leistung um das 15-fache. Ich habe die CPU überwacht, und sie war mit 2 Threads ausgelastet, was durchaus Sinn macht. Ich habe jConsole laufen lassen und konnte keine Auffälligkeiten in Bezug auf den Speicher und die Müllabfuhr feststellen, aber das war nur eine oberflächliche Untersuchung.
Aber was bedeutet das?
Das Fazit ist, dass Sie wirklich mit der Leistung der Verknüpfung in Ihrer Situation experimentieren sollten, bevor Sie sich für sie als Lösung für alle Ihre Probleme entscheiden. Ich würde erwarten, dass die Zahlen für Felder mit weniger eindeutigen Werten viel besser sind. Aber Solr ist ein lausiges RDBMS, und jedes Mal, wenn Sie es als RDBMS verwenden wollen, sollten Sie sich bemühen, Ihr Problem so zu überdenken, dass Solr sich nicht wie ein solches verhält. Diese Zahlen, vorausgesetzt, sie sind repräsentativ für Ihre spezielle Situation, könnten durchaus ein Killer sein. Andererseits sind sie vielleicht in Ordnung, wenn Sie eine kleine Gruppe von Benutzern bedienen, für die die Zeit, die sie mit dem Warten auf das Ergebnis einer Abfrage verbringen, gut angelegt ist. Es kommt darauf an ™.
Es könnte auch bedeuten, dass der Fall, den die Solr Join-Funktionalität lösen sollte, für dieses spezielle Problem einen unnötig restriktiven Ansatz verfolgt. Ich vermute, dass es durchaus möglich ist, dass eine Spezialisierung des Join-Codes für die to-id auf einen <uniqueKey> die Leistung radikal verändern würde. Eines der Merkmale von Open-Source-Code ist, dass Lösungen für das unmittelbare Problem implementiert und dann bei Bedarf für andere Fälle verfeinert werden.
Das „Umdenken“ umfasst oft mindestens vier Phasen:
- Denken Sie gründlich über das Problem nach. Kann es durch geschickte Indizierung gelöst werden? DB-Anwender mögen es nicht, Daten zu plätten, aber das ist oft ein praktikabler Ansatz.
- Fragen Sie sich, ob die Funktionalität wirklich etwas ist, das die Benutzererfahrung verbessert. Oft sind Facettierung und Filterung „gut genug“. RDBMS-Konzepte sind für Ihre Benutzer nicht besonders „natürlich“. Fragen Sie also Ihre UI-Design-Experten, was dem Benutzer wirklich helfen würde.
- Führen Sie in Ihrer Situation Prototypen durch und sprechen Sie mit Ihren Produktmanagern , bevor Sie sich unwiderruflich für diesen Weg entscheiden. Die Leute von eXtreme Programming betonen immer wieder, dass es ihnen hilft, bessere Entscheidungen zu treffen, wenn Sie Ihre PMs über die Kosten einer Funktion aufklären, auf die sie bestehen. Wenn Sie fragen: „Für das, was Sie wollen, benötigen Sie die 5-fache Menge an Hardware und einen zusätzlichen Monat für die Implementierung, ist XXX dann gut genug?
- Fragen Sie „Ist Solr die richtige Lösung?“ Ich liebe Solr/Lucene. Die Arbeit mit diesem Ökosystem bezahlt meine Rechnungen. Ich bewundere die Arbeit, die die Leute in den kleinsten Details des Codes leisten. Aber Solr und Lucene sind für manche Aufgaben nicht geeignet. Es kann sein, dass das Problem, das Sie zu lösen versuchen, mit einem RDBMS besser zu lösen wäre. Es kann sein, dass eine Art Hybrid zwischen Solr und einer <fügen Sie hier Ihre Lieblingslösung ein> besser funktioniert. Es kann sein, dass Solr überhaupt nicht Teil der Lösung für dieses Problem sein sollte. Nicht alle Nägel sollten mit Solr eingeschlagen werden.
Soweit ich weiß, liegt dieses Verhalten in der Art und Weise, wie der Verknüpfungscode implementiert ist, begründet und die Anzahl der übereinstimmenden Dokumente ist nicht der begrenzende Faktor (was sich in meinen Experimenten bestätigt hat). Ich frage mich, ob man die Tatsache nutzen könnte, dass das Verknüpfungsfeld ein <uniqueKey> ist, um eine Spezialisierung zu implementieren. Hmmm, ich werde mit Yonik sprechen müssen, aber ich vermute, dass dies eines dieser Dinge ist, die einfach erscheinen, aber schnell unhaltbar werden. Und schon wieder versuchen wir, Solr dazu zu bringen, sich wie eine DB….. zu verhalten.
Sie können Joins immer noch verwenden!
Verstehen Sie das nicht so, dass ich sage: „Verwenden Sie keine Joins“. Sie sollten sich vielmehr darüber im Klaren sein, dass sie implementiert wurden, um ein bestimmtes Problem zu lösen, und nicht die allgemeine Many-to-Many-Beziehung. Der Algorithmus tut, was er tun muss, um dieses Problem zu lösen, aber wenn er auf andere Probleme angewandt wird, ist er möglicherweise nicht leistungsfähig genug, um auf Ihre Situation anzuwenden. Testen, testen, testen!!!
Addendum
Als ich mir die obigen Ergebnisse ansah, fragte ich mich, was passieren würde, wenn ich die Anzahl der eindeutigen Werte im Verknüpfungsfeld einschränken würde, denn es ist zu erwarten, dass die Verknüpfung mit einem Feld mit vielen eindeutigen Werten nur Ärger bringt. Wenn Sie also das obige Beispiel mit 5 Threads auf ein neues Verknüpfungsfeld anwenden, das nur 200 eindeutige Werte enthält (statt 5.000.000 eindeutige Werte), erhalten Sie deutlich bessere Ergebnisse. Wenn ich die gleiche Art von Test durchführe, erhalte ich eine etwa 10-fache Steigerung der QPS-Rate:
- Gewinde: 5
- Rückfragen: 110
- Durchschnittliche QTime: 0.5 Sekunden
- Uhrzeit: 53
- Abfragen/Sek: 2
Dies unterstreicht, dass Sie, bevor Sie entscheiden, ob Joins die Antwort auf Ihre Frage sind, einen realistischen Datensatz testen sollten.
Dieser Beitrag wurde ursprünglich am 20. Juni 2012 veröffentlicht.