Skalierung von Lucene und Solr

Sie müssen Solr skalieren? Hier erfahren Sie, wie Sie große Indizes und Abfragen mit hohem Volumen verwalten können.

Während viele Lucene/Solr-Anwendungen nie über einen einzelnen, gut konfigurierten Rechner hinauswachsen werden, ist es eine Tatsache, dass immer mehr Anwendungen aufgrund der Indexgröße oder des Abfragevolumens die Grenze eines einzelnen Rechners überschreiten. Mark Miller erläutert die besten Praktiken für die Leistung und Skalierung von Lucene und Solr. Er erklärt, wie Sie das Beste aus einem einzelnen Rechner herausholen und wie Sie mehrere Rechner nutzen können, um große Indizes, große Abfragevolumen oder beides zu bewältigen.

Einführung

Für die weniger Bekannten: Lucene ist eine sehr kompakte und leistungsstarke Suchbibliothek, während Solr eine Suchmaschine für Unternehmen ist, die auf der Lucene-Bibliothek aufbaut. Lucene bietet Ihnen eine hervorragende Kerntechnologie für die Informationsbeschaffung in einem kompakten Paket, und Solr baut darauf auf. Dazu gehören: eine plattformunabhängige Schnittstelle, Facettierung, Replikation, Caching, verteilte Suche in großem Maßstab und vieles mehr. Dieser Artikel setzt voraus, dass Sie mit den Lucene/Solr-Grundlagen vertraut sind, sollte aber für diejenigen, die sich mit der Skalierbarkeit des Lucene-Stacks beschäftigen, relativ leicht zugänglich sein.

Lucene und Solr sind beide hoch skalierbare Suchlösungen. Abhängig von einer Vielzahl von Faktoren kann ein einzelner Rechner problemlos einen Lucene/Solr-Index mit 5 – 80+ Millionen Dokumenten hosten, während eine verteilte Lösung Suchreaktionszeiten von unter einer Sekunde für Milliarden von Dokumenten bieten kann. In diesem Bereich kann der Abfragedurchsatz durch Indexreplikation auf jedem einzelnen Server angepasst werden.

Das Standardverfahren für die Skalierung von Lucene/Solr ist wie folgt: Maximieren Sie zunächst die Leistung auf einem einzelnen Rechner. Als nächstes fangen Sie ein hohes Abfragevolumen durch Replikation auf mehrere Rechner auf. Wenn der Index für einen einzelnen Rechner zu groß wird, teilen Sie den Index auf mehrere Rechner auf (oder splitten Sie den Index). Schließlich replizieren Sie bei hohem Abfragevolumen und großer Indexgröße jeden Shard (ein Shard ist ein Server in einer verteilten Konfiguration).

Im Diagramm Skalierungsprogression können Sie sich ein besseres Bild davon machen, wie diese Progression funktioniert. Es beginnt mit einem einzigen Rechner, der alle Abfragen bedient und alle Indexaktualisierungen durchführt. Als nächstes gibt es die Master/Slave-Konfiguration, bei der der Master alle Aktualisierungen vornimmt und alle Indexänderungen an die Slaves repliziert. Die Slaves für den Master bearbeiten alle Abfragen. Sie können einen Index auch auf mehrere Rechner aufteilen (bei der Verwendung von verteiltem Solr Shards genannt), wobei jeder Shard Indexaktualisierungen und Abfragen übernimmt. Schließlich können Sie jeden Shard für die Replikation einrichten, wobei wiederum jeder Shard-Master Aktualisierungen vornimmt und alle Slaves für jeden Shard Abfragen bearbeiten.

Skalierung der Progression

Dies sind die vier Best-Practice-Konfigurationen für das Wachstum einer Lucene/Solr-Anwendung.

Maximierung eines einzelnen Servers

Der Schlüssel zur Maximierung der Leistung mit Lucene/Solr ist die Konfiguration. Die Entwickler von Lucene/Solr haben sich zum Ziel gesetzt, für den typischen Anwendungsfall eine hervorragende Leistung zu bieten, aber die richtige Abstimmung auf Ihre spezielle Umgebung kann zu erheblichen Leistungsverbesserungen führen. Bei jeder Lucene/Solr-Installation spielen viele Variablen eine Rolle, und es gibt viele Konfigurations- und Architekturüberlegungen, die Sie in Abhängigkeit von diesen Variablen anstellen sollten. Praktisch bedeutet das, dass Sie die meisten Dinge für Ihre spezielle Umgebung testen müssen, um herauszufinden, was am besten funktioniert. Es gibt jedoch nicht nur schlechte Nachrichten – es gibt viele Verallgemeinerungen und viele Tipps, die Sie lernen können, wenn Sie versuchen herauszufinden, wie Sie die maximale Leistung herausholen können. Ich werde auf einige der Ideen eingehen, die bei der Suchleistung eine Rolle spielen. Sie sollten diese Ideen aufgreifen, sich eingehender mit denjenigen befassen, die auf Ihre Situation zutreffen (beginnen Sie mit den Links am Ende dieses Artikels), und dann optimieren und testen, um zu sehen, ob Sie Ihren Anforderungen gerecht werden. Lucene bietet im Contrib-Bereich ein sehr leistungsfähiges Benchmark-Modul, das für Sie nützlich sein könnte.

Es gibt zwei Hauptbereiche der Leistung: die Suchseite und die Indizierungsseite. Dieser Artikel konzentriert sich auf Dinge, die die Leistung der Suchseite beeinflussen, geht aber auch auf Indexierungsentscheidungen ein, die eine Rolle bei der Suchleistung spielen.

Verwalten Sie Ihren Index

Es ist wichtig, dass Sie Ihren Index von Anfang an mit Blick auf die Leistung einrichten. Achten Sie darauf, dass Sie zu Beginn die richtigen Einstellungen wählen, um zu vermeiden, dass Sie Ihre Inhalte komplett neu indizieren müssen, was bei großen Indizes sehr zeitaufwendig sein kann. Es ist auch wichtig zu überlegen, wie Sie Ihren Index pflegen werden – Lucene/Solr geben Ihnen viel Kontrolle über die Indexstruktur, und es liegt an Ihnen, diese Macht für die beste Leistung zu nutzen.

Begriffshäufigkeiten

Abhängig von Ihren Daten können viele Felder von der Verwendung von Fieldable.omitTf (Boolean) profitieren. Dies indiziert das Feld ohne Termhäufigkeiten, Positionen oder Nutzdaten. Dies ist oft bei sehr kurzen Feldern oder bei Feldern ohne Volltext sinnvoll. Die Leistung wird durch das Weglassen nicht nützlicher Datenstrukturen verbessert. Diese Optimierung wurde erst vor kurzem zu Lucene hinzugefügt und ist in Solr noch nicht verfügbar. Es wird jedoch in SOLR-739 daran gearbeitet und sollte bald verfügbar sein.

Normen

Verwenden Sie omitNorms überall dort, wo es sinnvoll ist. Normen ermöglichen Indexerhöhungen und die Normalisierung der Feldlänge. Damit können Sie Feldern zum Zeitpunkt der Indexierung einen Boost verleihen und kürzere Dokumente besser bewerten. Genau wie bei omitTf ist dies für kurze oder nicht vollständige Textfelder möglicherweise nicht sinnvoll. Normen werden im Index als Byte-Wert pro Dokument und Feld gespeichert. Wenn Normen in einen IndexReader geladen werden, werden sie für jedes Feld in ein byte[maxdoc]-Array geladen. Selbst wenn also nur ein einziges von 400 Millionen Dokumenten ein Feld enthält, wird immer noch byte[maxdoc] für dieses Feld geladen, was möglicherweise eine Menge RAM verbraucht. Ziehen Sie in Erwägung, die Normen für bestimmte Felder zu deaktivieren, insbesondere wenn Sie eine große Anzahl von Feldern im Index haben. Jedes Feld, das sehr kurz ist (d.h. kein wirkliches Volltextfeld – IDs, Namen, Schlüsselwörter usw.) ist ein guter Kandidat. Bei einem großen Index müssen Sie möglicherweise einige harte Entscheidungen treffen und die Normen für wichtige Volltextfelder ebenfalls deaktivieren. Ein Beispiel dafür, wie viel Arbeitsspeicher wir benötigen, ist ein Feld in einem Index mit 10 Millionen Dokumenten, das knapp 10 MB RAM beansprucht. Einhundert solcher Felder benötigen fast ein Gigabyte RAM. Bei Lucene können Sie die Normen beim Hinzufügen eines Feldes zu einem Dokument weglassen und bei Solr, indem Sie die richtige Felddefinition in Ihrer Schema.xml-Datei verwenden.

Lazy Field Loading

Wenn Lucene und Solr ein Dokument aus dem Index laden (z.B. zum Hervorheben und Anzeigen von Treffern), werden alle gespeicherten Felder für dieses Dokument auf einmal geladen. Wenn Sie nicht immer alle Felder des Dokuments verwenden und viele, zum Teil große Felder haben, können Sie einen Geschwindigkeitsschub erzielen, indem Sie einen FieldSelector in Lucene oder Lazy Field Loading in Solr verwenden (das im Verborgenen einen FieldSelector verwendet). Dadurch kann Lucene nicht benötigte Felder beim Laden des Dokuments überspringen. Dies ist nicht immer eine Ersparnis, wenn es nicht viel zu überspringen gibt, aber unter den richtigen Umständen kann dies zu einer dramatischen Verbesserung der Ladeleistung für gespeicherte Felder führen. Stellen Sie sich vor, Sie haben eine Vielzahl kleiner Felder für die Anzeige von Trefferlisten gespeichert, aber auch einige große Felder mit dem gesamten Originalinhalt, z.B. für die Anzeige von Dokumenten. Beim Laden der Trefferlistenfelder können Sie eine Menge Zeit sparen, indem Sie die großen Inhaltsfelder überspringen.

Stopp-Wörter

Wenn Sie sich der Obergrenze einer einzelnen Maschine nähern, können extrem häufige Begriffe (sogenannte Stoppwörter) in einer falschen Abfrage sehr teuer werden. Wenn eine SHOULD-Klausel, die in jedem Dokument vorkommt, Teil einer BooleanQuery auf oberster Ebene ist, führt dies zu einem Treffer und einer Bewertung für jedes Dokument in Ihrem Index. Während die Leistung für Standardabfragen auf einem Index, der an die Grenzen einer Maschine stößt, immer noch unter einer Sekunde liegen kann, können Sie feststellen, dass Abfragen, die mit den meisten Dokumenten im Index übereinstimmen, der Leistung sehr abträglich sein können. Meiner Erfahrung nach kann der Unterschied zwischen einer Antwortzeit von unter einer Sekunde und über 10 Sekunden bei einem sehr großen Index (über 10 Millionen Dokumente, ohne Cache-Treffer) dramatisch sein. Wenn Sie sich dafür entscheiden, Stoppwörter zur Indexierungszeit zu entfernen (was in der Regel nicht empfohlen wird), und Sie gezwungen sind, an der Grenze eines einzelnen Rechners zu arbeiten, sollten Sie sich Ihre Stoppwortliste gut überlegen. Wenn Sie sich dafür entscheiden, Stoppwörter nicht zu entfernen (die meisten finden sie zumindest für die Phrasensuche immer noch nützlich), sollten Sie eine Option zum Entfernen von Stoppwörtern zur Abfragezeit vorsehen. Am besten wäre es, sie nur dann zu entfernen, wenn sie eine OR-Klausel auf oberster Ebene in einer BooleanQuery auf oberster Ebene sind. Im Contrib-Bereich von Lucene gibt es einen Analyzer, der die Entfernung von Stoppwörtern zur Abfragezeit durchführt: org.apache.lucene.analysis.query.QueryAutoStopWordAnalyzer. Testen Sie unbedingt ein wenig für Ihre Situation – Lucene wird Sie oft überraschen, wenn es um die Leistung geht. Wenn Sie eine wirklich große Installation mit Stoppwörtern aufbauen möchten, können Sie die Leistung der Phrasensuche verbessern, indem Sie effizientere Indizierungsschemata in Betracht ziehen (z.B. die Indizierung von Stoppwörtern als Bi-Gramme / Bi-Wörter, um seltenere Begriffe zu erzeugen). Zwei weitere Optionen sind der Aufbau einer verteilten Installation, bei der jeder Rechner einen kleineren Index enthalten kann, oder der Kauf eines neuen Servers, der das Volumen bewältigen kann.

Index-Optimierung

Ein Lucene-Index setzt sich aus 1-n Segmenten zusammen. Ein einzelnes Segment kann fast wie ein einzelner Lucene-Index selbst behandelt werden, aber nicht ganz. Ein einzelnes Segment ist nur kurz ein in sich geschlossener invertierter Index. Die segmentierte Natur eines Lucene/Solr-Index ermöglicht effiziente Aktualisierungen, denn anstatt bestehende Segmente zu ändern, können Sie einfach neue hinzufügen. Im Laufe der Zeit können diese Segmente dann für einen effizienteren Zugriff zusammengeführt werden. Je mehr Segmente Sie durchsuchen müssen, desto langsamer ist die Suche. Wenn Sie nicht das zusammengesetzte Dateiformat verwenden, bedeuten weniger Segmente auch viel weniger offene Dateien. Dadurch wird verhindert, dass Ihr Betriebssystem bei einem großen Index die Fehlermeldung „Zu viele offene Dateien“ ausgibt. Wenn Sie diese Ausnahmen erhalten, müssen Sie möglicherweise das Limit für offene Dateien in Ihrem Betriebssystem anheben oder die Anzahl der Segmente mit den unten beschriebenen Techniken verringern. Natürlich können Sie für einen geringen Leistungsverlust auch das Compound File Format verwenden (Lucene und Solr verwenden dieses Format standardmäßig), bei dem die Segmente in eine einzige Datei geschrieben werden, wodurch die Anzahl der Dateien in Ihrem Index erheblich reduziert wird.

Wenn Sie einen Lucene-Index optimieren, wird jedes einzelne Indexsegment zu einem großen Segment zusammengeführt. Dadurch wird die Suche effizienter – es werden nicht nur weniger Dateien berührt, sondern mit einem einzigen Segment kann Lucene viele kleine Schritte überspringen, die notwendig sind, um mehrere Segmente als einen einzigen Index zu behandeln. Wenn Sie einen FieldCache verwenden (z.B. zum Sortieren), beeinträchtigen diese kleinen Schritte das Laden des IndexReader FieldCache erheblich. Dies wird wahrscheinlich in kommenden Versionen behoben werden, aber bis dahin bedeutet dies, dass ein optimierter Index derzeit sehr vorteilhaft für das Laden von FieldCache ist. Siehe die Diskussion in LUCENE-1483.

Die Optimierung sowohl in Lucene als auch in Solr ist ein E/A-intensiver Vorgang, der bei einem großen Index einige Zeit in Anspruch nehmen kann. Sie könnten auch eine partielle Optimierung in Erwägung ziehen. Mit einer Teiloptimierung können Sie Lucene/Solr mitteilen, wie viele Ergebnissegmente Sie haben möchten. Auf diese Weise können Sie die Suchgeschwindigkeit schrittweise verbessern, ohne sich auf eine vollständige Optimierung bis hin zu einem einzelnen Segment festzulegen.

Eine weitere Strategie, um die Anzahl der Segmente niedrig zu halten, ist die Verwendung eines niedrigen Merge-Faktors auf Ihrem IndexWriter, wenn Sie dem Index etwas hinzufügen. Der Merge-Faktor bestimmt, wie viele Segmente Ihr Index umfassen muss. Ein Wert von weniger als 10 kann dazu beitragen, dass die Suchvorgänge angenehm und schnell bleiben. Der Nachteil ist, dass das Hinzufügen zum Index nun etwas länger dauert, da die Zusammenführung häufiger erfolgen muss, um die Anzahl der Segmente niedrig zu halten. Bei einem Mergefaktor von 2 (dem niedrigsten zulässigen Wert) würden Sie beispielsweise nie mehr als zwei Segmente haben.

Verwalten Sie Ihr Gedächtnis

Ein großer Index kann eine Menge RAM benötigen. Sie sollten sich mit einigen der Strukturen in Lucene/Solr vertraut machen, die erhebliche Ressourcen beanspruchen können, damit Sie Ihre Lucene/Solr-Umgebung optimal verwalten können. Wenn Sie nicht genau wissen, wie viel Arbeitsspeicher benötigt wird und wie dieser zugewiesen werden sollte, werden Sie wahrscheinlich auf zahlreiche Leistungsprobleme stoßen.

Lucene Caches

In einer groß angelegten Suchanwendung können Caches sehr wichtig werden. Es ist ein gängiges Thema, wenn es um die Leistung geht, und man versucht, Festplatten-IO zu vermeiden. Lucene bietet nur begrenzte Unterstützung für das Caching und Sie möchten vielleicht selbst eine Caching-Schicht einrichten. Genau dafür hat sich Solr entschieden.

FieldCache

Lucene verwendet FieldCache um effizient auf alle Werte eines Feldes im Speicher zuzugreifen, anstatt sie auf der Festplatte zu speichern. Dies ist für die Sortierung erforderlich und kann u.a. für die Facettierung von Solr verwendet werden. Bei einem großen Index kann der FieldCache eine beträchtliche Menge an Arbeitsspeicher beanspruchen, vor allem, wenn Sie einen für viele Felder laden (wenn Sie z.B. nach vielen Feldern sortieren). Viele Lucene/Solr-Benutzer hatten schon einmal das Problem, dass der Speicher nicht ausreicht, um den FieldCache zu verwenden.

Ein FieldCache speichert den Wert (und möglicherweise die Ordnungszahl) für jedes Dokument im Index im Speicher. Dies ermöglicht schnelle Vergleiche eines Wertes für ein bestimmtes Dokumentfeld. Eine Ordnungszahl gibt einfach die Reihenfolge an und kann z.B. für Strings verwendet werden. Anstelle von Tom, Dick, Clark könnten Sie 3, 2, 1 verwenden – die Sortierung ist dann schneller und die richtige Reihenfolge bleibt erhalten. Bei anderen Typen (Integer, Long, usw.) kann auch der Wert selbst eine gute Ordnungszahl sein.

Der größte Teil eines FieldCache ist einfach ein Array, dessen Größe davon abhängt, wie viele Dokumente sich im Index befinden (einschließlich gelöschter Dokumente, die nicht herausgemischt wurden). Wenn Sie also nach einem Long-Feld für einen Index mit 10 Millionen Dokumenten sortieren, werden 10 Millionen Long-Felder in ein long[]-Array geladen: Das sind ungefähr 76,29 Megabyte. Multiplizieren Sie das mit der Anzahl der Long-Felder, auf denen ein FieldCache aufgebaut ist, um den gesamten Speicherverbrauch des Long FieldCache zu erhalten. Wiederholen Sie dies für Ihre anderen Feldtypen, um eine Vorstellung von der Gesamtnutzung zu erhalten. Ein weiteres Beispiel: Ein int[]-Array auf einem 100 Millionen Dokumente umfassenden Index verbraucht über 380 Megabyte.

Der Typ String ist ein wenig komplizierter als die anderen. Wenn Sie einen String-basierten FieldCache ohne Gebietsschema haben (d.h. Sie sortieren nach einem String-Feld, aber Sie geben kein Gebietsschema für String-Vergleiche an), wird ein Array mit allen eindeutigen Begriffen im Index (String[]) geladen und dann ein zweites Array mit ganzen Zahlen für jedes Dokument im Index. Das zweite Array ist voll von Ordnungszahlen, die auf das Array mit den eindeutigen Begriffen verweisen. Dies ist ein weniger effizienter Zugriff auf die Werte (zwei Array-Dereferenzierungen), aber im Fall eines einzelnen IndexReaders ermöglicht es Ihnen die Sortierung anhand von Ganzzahlen anstelle von Strings, da Sie anhand des Ordinal-Arrays von Ganzzahlen vergleichen können. Wenn Sie ein Gebietsschema für den String FieldCache angeben, wird ein String[]-Array mit dem Begriff aus jedem Dokument für dieses Feld im Index gefüllt, genau wie bei den anderen primitiven Typen. Ordinale Vergleiche funktionieren nicht, wenn Sie ein Gebietsschema verwenden. Die String[]-Darstellung speichert einen Index bei der Suche in einem Array, ist aber immer noch langsamer, weil Sie beim Sortieren Strings und nicht ganzzahlige Ordnungszahlen vergleichen müssen.

String-Feld-Cache

Dies zeigt die wichtigsten RAM-essenden Datenstrukturen für einen String FieldCache, der keine Vergleiche mit einem Locale durchführt. Das erste Array enthält alle eindeutigen Begriffe im Index für das FieldCache-Feld, und das zweite Array ist ein Index in das erste Array für jedes der Dokumente im Index. Wie Sie sehen können, können Sie das Dokument nur durch Vergleiche der Ganzzahlen im zweiten Array sortieren. Mit einem MultiSearcher können Sie dies jedoch nicht tun – Ordnungszahlen aus verschiedenen Indizes können nicht sinnvoll verglichen werden.

Filter

Lucene wird mit einem CachingWrapperFilter geliefert, der Lucene-Filter zwischenspeichert, wobei der Filter an das Leben eines IndexReaders gebunden ist. Wenn der Filter zum ersten Mal verwendet wird, ist er etwas langsam, da er berechnet, welche Dokumente mit dem Filter übereinstimmen, und dann die Ergebnisse in einer WeakHashMap zwischenspeichert. Nachfolgende Abfragen überspringen diese Schritte jedoch und sind recht schnell, da sie direkt mit dem zwischengespeicherten Filter arbeiten. Wenn Sie den CachingWrapperFilter mit einem QueryWrapperFilter kombinieren, können Sie ziemlich effizient und einfach jede beliebige Gruppe von Dokumenten herausfiltern, die Sie möchten.

Dokumente

Es ist auch eine gute Idee, Lucene-Dokumente zwischenzuspeichern. Die Document-Klasse in Lucene ermöglicht den Zugriff auf gespeicherte Felder, und wenn Lucene zum Lesen dieser Felder auf die Festplatte zugreifen muss, kann das wirklich ziemlich ineffizient sein. Wenn Sie gespeicherte Felder für die Anzeige von Trefferlisten auf einer stark frequentierten Website bereitstellen, kann der Zugriff auf die Festplatte ziemlich schnell ins Stocken geraten. Die Hits-Klasse in Lucene bot früher eine begrenzte Zwischenspeicherung von Dokumenten. Diese Klasse wurde jedoch zugunsten der TopDoc-APIs veraltet, so dass Sie sich Ihren eigenen Dokumenten-Cache zusammenstellen müssen.

Wie immer ist es hilfreich, sich an Solr zu orientieren, wenn es um bewährte Verfahren für eine Lucene-Anwendung geht.

Solr Caches

Neben benutzerdefinierten Caches verfügt Solr über drei Arten von integrierten Caches. Wenn Sie eine Zwischenspeicherung benötigen (und das tun Sie in der Regel), kann die richtige Einrichtung Ihrer Caches sehr wichtig für die Leistung sein. Wenn Sie sich in einer Situation befinden, in der die Zwischenspeicherung nicht sehr vorteilhaft ist, z.B. wenn Sie so gut wie nie dieselbe Abfrage zweimal stellen, kann auch das Ausschalten der Zwischenspeicherung die Leistung verbessern.

Jeder Cache sollte sorgfältig geprüft werden:

  • FilterCache – ungeordnete Dokument-IDs. Dies ist für die Zwischenspeicherung von Filterabfragen. Dieser Cache speichert genügend Informationen, um für eine bestimmte Abfrage die richtigen Dokumente aus dem gesamten Index herauszufiltern. Die Verwendung von Mengenüberschneidungen auf diesen gefilterten IDs ermöglicht eine effiziente Kombination von Filterabfragen. Die Reihenfolge der zurückgegebenen Dokumente wird nicht zwischengespeichert, so dass eine Abfrage, die sich auf Relevanz- oder Sortierfelder stützt, nicht zwischengespeichert werden kann. Wenn Sie mit der FieldCache-Methode facettieren (und das sollten Sie, wenn Sie eine große Anzahl von eindeutigen Feldern haben), sollten Sie diesen Wert mindestens auf die Anzahl der eindeutigen Werte in allen Feldern setzen, die Sie für die Facettierung verwenden (unter Verwendung der FieldCache-Methode).
  • QueryCache – geordnete Dokument-IDs. Dies ist für das Zwischenspeichern der Ergebnisse normaler Abfragen. Er kann viel weniger RAM benötigen als der FilterCache, da er nur die zurückgegebenen Dokumente zwischenspeichert, während der FilterCache die Ergebnisse für den gesamten Index zwischenspeichern muss. Die optimale Größe dieses Caches hängt von einer Vielzahl von Faktoren ab. Grundsätzlich sollten Sie darauf achten, dass er groß genug ist, damit die meisten Ergebnisse Ihrer wirklich häufigen Abfragen zwischengespeichert werden.
  • DocumentCache – speichert gespeicherte Felder. Solr speichert Dokumente im Arbeitsspeicher, so dass für gespeicherte Felder keine Anfrage auf die Festplatte gestellt werden muss. Dies kann sehr wertvoll sein, da gespeicherte Felder meist für die Anzeige von Trefferlisten verwendet werden. Das Solr Wiki empfiehlt, die Größe dieses Caches auf mindestens <max_results> * <max_concurrent_queries> einzustellen, um sicherzustellen, dass Solr ein Dokument während einer Anfrage nicht erneut abrufen muss.

Eine der Cache-Einstellungen, die Sie beachten sollten, ist der autowarm-Wert. Die Einstellung autowarm teilt Solr mit, wie viele Einträge aus dem alten Cache in den neuen Cache übernommen werden sollen, wenn eine neue Ansicht des Index geöffnet wird (aufgrund einer Indexänderung). Der Dokumenten-Cache kann nicht automatisch erwärmt werden, aber für die anderen Caches sollten Sie einen Wert verwenden, der groß genug ist, um das Auffüllen der Caches zu beschleunigen, aber nicht so groß, dass es zu lange dauert, die Caches zu erwärmen. Die neue Ansicht steht den Benutzern erst zur Verfügung, wenn die Erwärmung abgeschlossen ist. Testen Sie also unbedingt, ob die Erwärmung in einem akzeptablen Zeitrahmen erfolgt. Sie sollten die Anzahl der automatischen Aufwärmvorgänge so wählen, dass ein angemessener Teil des Caches in den neuen Sucher übertragen wird, aber nicht so hoch ist, dass es zu lange dauert, einen neuen Sucher für die Verwendung aufzuwärmen.

Es ist auch eine gute Idee, die Cache-Statistiken auf der Solr-Verwaltungswebseite einzusehen. Wenn Sie eine sehr niedrige Trefferquote haben, schadet Ihr Cache möglicherweise mehr als er nützt. Wenn Sie eine sehr hohe Verdrängungsrate haben, ist Ihr Cache wahrscheinlich zu klein und kann ebenfalls mehr schaden als nützen. Wenn Sie genügend Auslagerungen haben, ist es durchaus möglich, dass zwischengespeicherte Ergebnisse weggeworfen werden, bevor sie verwendet werden, oder nachdem sie nur eine Handvoll Mal verwendet wurden. Informieren Sie sich im Solr-Wiki über SolrCaching und stellen Sie sicher, dass Sie die richtigen Einstellungen für eine optimale Leistung verwenden.

Dies ist nicht das erste Mal, dass wir wissen müssen, wie viele eindeutige Werte wir in einem Feld haben. Ein sehr nützliches Werkzeug, um einige dieser Informationen zu finden, ist der LukeRequestHandler, den Solr bereitstellt. Wenn Sie einfach solr/admin/luke oder solr/admin/luke?wt=xslt&tr=luke.xsl aufrufen, erhalten Sie eine Vielzahl von großartigen Statistiken über Ihre Daten. Scheuen Sie sich nicht, die Daten einzuschleusen, sich die Dinge mit dem LukeRequestHandler anzusehen, Ihre Einstellungen zu ändern und dann wieder von vorne zu beginnen. Bei großen Indizes können Sie einige Informationen opfern, indem Sie numTerms=0 hinzufügen, solr/admin/luke?numTerms=0. Dies kann einen Aufruf, der bei einem großen Index viele Minuten dauert, in Sekunden verwandeln – zum Preis von weniger detaillierten Termdaten.

Solr-Facettierung

Solr verfügt über eine ausgezeichnete und effiziente Facettierungsimplementierung, aber es lohnt sich, die Auswirkungen auf den Speicher zu berücksichtigen. Solr bietet zwei Hauptmodi für die Facettierung: FacetQueries und FacetFields.

  • FacetQueries werden behandelt, indem die Ergebnisse einer Abfrage als Filter zwischengespeichert werden. Dieser FacetQuery-Satz von Dokumenten wird mit den Ergebnismengen abgeglichen, um zu zählen, für wie viele Dokumente eine Abfragebedingung zutrifft (die Facette zählt). Wenn nur wenige Ergebnisse im Filter vorhanden sind, wird der Filter als Hash-Satz von Dokument-IDs beibehalten. Gibt es mehr Ergebnisse als in der ‚hashDocSet‘-Einstellung angegeben, wird stattdessen eine Bitmenge verwendet.
  • FacettenFelder ermöglichen die Zählung von Facetten auf der Grundlage unterschiedlicher Werte in einem Feld. Es gibt zwei Methoden für FacetFields, eine, die bei wenigen unterschiedlichen Werten in einem Feld gut funktioniert, und die andere für den Fall, dass ein Feld viele unterschiedliche Werte enthält (in der Regel Tausende und mehr – Sie sollten testen, was für Sie am besten funktioniert).Die erste Methode, facet.method=enum, funktioniert, indem sie eine FacetQuery für jeden einzelnen Wert im Feld ausgibt. Wie bereits erwähnt, ist dies eine ausgezeichnete Methode, wenn die Anzahl der unterschiedlichen Werte in einem Feld gering ist. Sie benötigt jedoch zu viel Speicherplatz und funktioniert nicht mehr, wenn die Anzahl der unterschiedlichen Werte zu groß wird. Wenn Sie diese Methode verwenden, sollten Sie darauf achten, dass Ihr FilterCache groß genug ist, um mindestens einen Filter für jeden eindeutigen Wert zu enthalten, den Sie facettieren möchten. Die zweite Methode verwendet den Lucene FieldCache (zukünftige Versionen von Solr werden eine andere nicht invertierte Struktur verwenden – das UnInvertedField). Diese Methode ist zwar langsamer und speicherintensiver für Felder mit einer geringen Anzahl eindeutiger Werte, aber wenn Sie viele eindeutige Werte haben, ist dies der richtige Weg. Diese Methode verwendet den FieldCache, um die Werte für das angegebene Feld für jedes Dokument nachzuschlagen. Jedes Mal, wenn ein Dokument mit einem bestimmten Wert gefunden wird, wird die Anzahl der Werte erhöht.

Abfragen

Sie sollten sich vor Augen halten, welche Arten von Abfragen im Allgemeinen langsamer sind, und deren Verwendung sorgfältig abwägen. Denken Sie daran, dass dies nur Allgemeinplätze sind, aber sie sind wichtig, um sie bei der Gestaltung Ihrer Einrichtung zu berücksichtigen. Also ganz allgemein: Die Familie der Multi-Term-Abfragen ist offensichtlich langsamer als Term-Abfragen. Insbesondere FuzzyQuery kann aufgrund der Editierdistanz, die es für die Bewertung und den Abgleich berechnet, sehr langsam sein. Es ist offensichtlich, aber auch hilfreich zu bedenken, dass die schnellsten Abfragen diejenigen sind, die mit den wenigsten Dokumenten übereinstimmen und einer einfachen Begriffsabfrage so nahe wie möglich kommen. Eine BooleanQuery fügt ein wenig eigenen Overhead hinzu und kombiniert auch die Kosten ihrer Abfrageklauseln. SpanQueries sind teurer als Standardabfragen, da sie Positionen berücksichtigen, sowohl für den Abgleich als auch für die Wertung. Dasselbe gilt für die Phrase-Abfragen, aber sie sind tendenziell schneller als Span-Abfragen. Schließlich sind AND-Abfragen in der Regel um einiges schneller als OR-Abfragen, da Skip-Listen verwendet werden können. Überlegen Sie, welche Arten von Abfragen Sie von Ihren Benutzern zulassen und welche Sie am ehesten sehen werden. Wenn die Standardimplikationen von Lucene/Solr für Ihre Bedürfnisse nicht ausreichen (z.B. wenn Sie hauptsächlich komplexe Wildcard-Abfragen verarbeiten müssen), gibt es andere Möglichkeiten. Sie können zum Beispiel einen separaten Permuterm-Index für eine effizientere Wildcard-Unterstützung erstellen.

ConstantScore-Abfragen sind Abfragen, die einfach einen konstanten Score für jedes Dokument zurückgeben, anstatt einen Score, der aus einer Relevanzformel abgeleitet wird. Bei großen Indizes können sie auch wesentlich schneller sein als ihre Äquivalente mit nicht konstantem Score, allerdings mit dem Nachteil, dass sie nicht zur Relevanz beitragen (wenn Sie denken, dass sie sich wie ein Filter anhören, haben Sie recht – tatsächlich verwenden sie einen Filter unter der Haube). Lucene 2.4 bietet sowohl eine ConstantScoreRangeQuery als auch eine ConstantScoreQuery, die einen Filter als Argument akzeptiert. Wenn Sie einen QueryFilter verwenden, können Sie effektiv jede Abfrage in eine ConstantScoreQuery verwandeln. Solr bietet sogar noch mehr ConstantScore-Abfragen, einschließlich ConstantScorePrefixQuery und ConstantScoreWildcardQuery. Solr 1.3 und höher verwendet nun standardmäßig die gesamte ConstantScore-Familie für die eingebauten Abfrageparser.

Bei einem großen Index können ConstantScore-Abfragen ein guter Ersatz für die Familie der Multi-Term-Abfragen sein: WildcardQuery, FuzzyQuery, RangeQuery, usw. Standardmäßige Abfragen mit mehreren Begriffen funktionieren, indem alle übereinstimmenden Begriffe im Index aufgezählt werden und dann eine BooleanQuery mit jedem dieser Begriffe als Klausel erstellt wird. Wenn viele eindeutige Termübereinstimmungen aufgezählt werden, kann die Abfrage ziemlich langsam sein. Bei einer ConstantScoreQuery wird nicht jeder Begriff in einer Abfrage mit mehreren Begriffen bewertet (was möglicherweise nicht sehr hilfreich ist), sondern allen Übereinstimmungen wird eine konstante Punktzahl zugewiesen, und es wird keine BooleanQuery erstellt. Dadurch werden Maxclause-Ausnahmefehler vermieden, die bei Abfragen, die zu BooleanQueries erweitert werden, häufig auftreten, und sie können bei großen Indizes erheblich schneller sein. In der nächsten Version von Lucene werden alle Multi-Term-Abfragen (Wildcard, Fuzzy, Range, Prefrix) eine Option zur Verwendung einer konstanten Bewertung anstelle einer BooleanQuery-Erweiterung bieten.

Lucene 2.4 wird eine neue Bereichsabfrage namens TrieRangeQuery einführen. TrieRangeQuery ermöglicht extrem effiziente numerische Bereichsabfragen in großem Umfang und Sie sollten in der nächsten Version ein Auge darauf haben. Es ist ein großer Schritt vorwärts in der Unterstützung von Lucene für numerische Bereichsabfragen. Solr 1.4 wird wahrscheinlich auch Unterstützung für TrieRangeQuery enthalten.

Maximierung des Durchsatzes

Wenn Sie beginnen, Lucene und Solr auf einem Server mit vielen Kernen oder Prozessoren zu verwenden, können Sie auf bestimmte bekannte Engpässe stoßen. Ich werde einige der häufigsten Probleme besprechen, die Sie berücksichtigen sollten, wenn Sie versuchen, das Beste aus Lucene/Solr auf höherwertiger Hardware herauszuholen.

Wenn Sie ein System mit Lucene entwerfen, möchten Sie in der Regel einen einzigen IndexSearcher/IndexReader über mehrere Threads hinweg gemeinsam nutzen. IndexReader und IndexSearcher können im Grunde austauschbar verwendet werden, da ein IndexSearcher im Grunde eine dünne Hülle um einen IndexReader ist. Aufgrund eines Fehlers in der Sun JRE ist das Bild unter Windows komplizierter, und Sie können auf einem System mit mehreren Kernen/Prozessoren eine bessere Leistung erzielen, wenn Sie mehrere IndexSearcher/IndexReader-Instanzen verwenden. Dies kann jedoch sehr viel ressourcenintensiver sein, insbesondere wenn Sie nach Feldern sortieren oder etwas anderes tun, das einen FieldCache oder andere zwischengespeicherte Ressourcen verwendet, die auf einen IndexReader verweisen. Bei einem großen Index sollten so wenig IndexReader wie möglich gleichzeitig aktiv sein, damit mehr Ressourcen für die Nutzung zur Verfügung stehen. Die Ausnahme von diesem Ratschlag ist, wenn Sie einen neuen IndexSearcher aufwärmen wollen, bevor er in Betrieb genommen wird, damit die erste Suche, die ein Benutzer auf einem neuen Searcher sieht, genauso schnell ist wie jede andere Suche. In diesem Fall sollte der alte Searcher weiterhin Anfragen bearbeiten, bis der neue Searcher einsatzbereit ist. Solr kümmert sich um diese Art der Verwaltung effektiv hinter den Kulissen.

In Ihrem Bestreben, so wenig IndexReader wie möglich zu verwenden, werden Sie auf einem Multi-Core/Prozessor-Rechner wahrscheinlich auf einige bekannte Engpässe stoßen. Wenn Sie darauf achten, diese Engpässe zu vermeiden, werden Sie einen dramatischen Anstieg des Durchsatzes auf Ihrem Server feststellen.

Ein Engpass, den Sie vermeiden sollten, um die Multicore-Leistung in Lucene zu maximieren, ist, dass Sie Ihre IndexReader im Nur-Lese-Modus öffnen. Dadurch wird ein Synchronisationsengpass beseitigt, der hauptsächlich mit Löschprüfungen zusammenhängt, und es wird sichergestellt, dass Sie einen besseren gleichzeitigen Durchsatz mit mehreren Kernen/Threads erhalten. Wenn Sie es gewohnt sind, mit IndexSearcher zu arbeiten, bedeutet dies, dass Sie stattdessen einen IndexReader erstellen und dann einen IndexSearcher mit diesem erstellen. Solr 1.3+ verwendet intern schreibgeschützte IndexReader, um sicherzustellen, dass Sie von Anfang an die maximale Leistung erhalten.

Ein weiterer Synchronisationsengpass in Lucene/Solr kann durch die Verwendung eines Nicht-Windows-Betriebssystems vermieden werden. Wenn Sie mit Lucene auf einem Nicht-Windows-Betriebssystem arbeiten, können Sie ein NIOFSDirectory anstelle eines FSDirectory verwenden, um die Multithreading-Leistung zu steigern. Wie bereits erwähnt, verhindert ein Fehler in Suns Windows JVM, dass diese Optimierung für Windows-Benutzer zur Verfügung steht, aber dies könnte in einem zukünftigen JRE-Update korrigiert werden. Solr 1.3 nutzt diese Funktion noch nicht, aber Solr 1.4+ wird Ihr Betriebssystem automatisch erkennen und die richtige Implementierung für maximale Leistung verwenden.

Wenn Sie Solr 1.4 in die Hände bekommen, können Sie versuchen, die alternative FastLRUCache Cache-Implmentierung anstelle von solr.LRUCache zu verwenden. Der Standard-LRUCache verwendet synchronisierte ‚gets‘ auf die zugrunde liegende Map, was bei einer ausreichenden Anzahl von Kernen/Prozessoren/Threads zu einem Synchronisationsengpass führen kann. Der FastLRUCache bietet unsynchronisierte ‚gets‘ auf die zugrundeliegende Map, allerdings auf Kosten einer gelegentlichen Bereinigungsoperation. FastLRUCache soll besser für Caches mit hoher Trefferquote geeignet sein (Puts sind teurer, während Gets billiger sind), daher sollten Sie für einen Cache mit niedriger Trefferquote immer noch solr.LRUCache verwenden.

JVM-Einstellungen

Die ordnungsgemäße Konfiguration Ihrer JVM kann ein kompliziertes Thema sein und wird am besten in Artikeln behandelt, die sich mit dieser Aufgabe befassen. Außerdem sind moderne JVMs ziemlich gut darin, Standardeinstellungen auf der Grundlage der erkannten Hardware zu wählen. In den folgenden Abschnitten finden Sie jedoch ein paar schnelle Tipps.

Auswählen von Xms Xmx

Eine Strategie besteht darin, einen sehr niedrigen Minimal- und einen hohen Maximalwert für den Arbeitsspeicher festzulegen. Lassen Sie Ihre Lucene/Solr-Anwendung laufen und überwachen Sie die Speichernutzung der JVM. Setzen Sie die minimale Einstellung auf das, was Sie als allgemeine Nutzung sehen. Setzen Sie die maximale Einstellung auf das, was Sie sich leisten können, und lassen Sie dabei genügend RAM für das Betriebssystem, andere Anwendungen und vor allem den Dateisystem-Cache übrig. Wie viel RAM Sie übrig lassen sollten, hängt von einer Vielzahl von Faktoren ab, darunter Ihr Betriebssystem, welche anderen Programme laufen, wie groß Ihr Index ist, usw. Das Betriebssystem wird Ihren überschüssigen RAM-Speicher für den Cache-Zugriff auf das Dateisystem verwenden und ein großer Index benötigt für eine optimale Leistung viel RAM für diesen Cache. Alles in allem sollten Sie bei einem großen Index sicherstellen, dass Sie mindestens ein paar Gigabyte RAM zur Verfügung haben, die über das hinausgehen, was Sie der JVM zur Verfügung stellen.

Verwenden Sie den Server HotSpot VM

Stellen Sie sicher, dass Sie die -server HotSpot VM verwenden. Dies ist die beste Option für eine lang laufende Serveranwendung, die den Durchsatz maximieren soll. Um zu überprüfen, ob Sie die Client- oder Server-HotSpot-VM verwenden, geben Sie Java -version in die Befehlszeile ein und suchen Sie nach ‚client‘ oder ’server‘. Wenn Sie eine der vielen Java JRE’s auf Ihrem System verwenden, achten Sie darauf, dass Sie die richtige Version auswählen. Oft wird die JRE-Distribution nicht mit der Server-HotSpot-VM ausgeliefert, die JDK-Distribution jedoch in der Regel schon.

Einstellungen prüfen

Eine gute Möglichkeit, um zu sehen, welche JVM-Einstellungen Ihr Server verwendet, sowie andere nützliche Informationen, ist die Verwendung des admin RequestHandlers, solr/admin/system. Dieser RequestHandler gibt eine Fülle von Serverstatistiken und -einstellungen aus.

Der Rest Ihres Servers

Stellen Sie sicher, dass Ihre Solr- und Lucene-Indizes von allen Indizierungsanwendungen (Windows Indizierungsdienst, Desktop-Suchanwendungen usw.) ausgeschlossen sind. Es ist unwahrscheinlich, dass eine Indizierungsanwendung Solr/Lucene-Indexdateien als etwas erkennt, das sie versteht und zu analysieren versucht, aber es ist am besten, sie einfach auszuschließen. Sie möchten sicher sein, dass externe Anwendungen Ihre Indexdateien nicht überprüfen, wenn sie sich ändern, insbesondere wenn Sie einen großen Index erstellen. Achten Sie auch darauf, Ihre Indizes von allen Backup-Anwendungen auszuschließen. Backups werden wahrscheinlich inkonsistent sein, wenn sie nicht in Zusammenarbeit mit Solr/Lucene durchgeführt werden, und sie können sich negativ auf die Leistung auswirken. Lucene In Action 2 hat ein kostenloses Kapitel über die Durchführung von Hot Backups mit Lucene veröffentlicht. Sie können ein Backup eines Solr-Index erstellen, indem Sie einfach die Solr-Replikation einrichten.

Denken Sie an die anderen Programme, die auf dem Server laufen müssen, und stellen Sie sicher, dass sie über genügend RAM verfügen, das über das hinausgeht, was der Java JVM zugewiesen wurde. Vergewissern Sie sich auch, dass das Betriebssystem über genügend RAM verfügt, um zu funktionieren, und dass genügend RAM für den Dateisystem-Cache des Betriebssystems verfügbar ist. Es sollte genügend RAM zur Verfügung stehen, um die wichtigsten Lucene-Indexdateien im Arbeitsspeicher zwischenzuspeichern – bei einem sehr großen Index sollten mindestens einige Gigabyte verfügbar sein. Wie viel genau Sie benötigen, hängt von vielen Faktoren ab. Schauen Sie sich also die physische Größe Ihres Index an und gehen Sie davon aus, dass Sie so viel davon im RAM zwischengespeichert haben möchten, wie Sie vernünftigerweise bekommen können.

Hohes Abfragevolumen

Viele Anwendungen kommen an einen Punkt, an dem ein einzelner Rechner eine bestimmte Indexgröße noch problemlos bewältigen kann, aber mit einer bestimmten Abfragelast nicht mehr mithalten kann. Der richtige Weg, mit dieser Situation umzugehen, besteht darin, den Index auf andere Server zu replizieren und dann die Last auf die Server zu verteilen, von denen jeder eine ‚Kopie‘ des Indexes enthält. Die Kopien können dann im Laufe der Zeit aktualisiert werden, wenn sich die ‚Master‘-Version des Indexes ändert.

Replizieren mit Lucene

Segmente

Es finden zwar Zusammenführungen statt, aber jedes Mal, wenn Sie einen neuen Stapel Dokumente zu Lucene/Solr hinzufügen, wird ein neues Indexsegment erstellt. Wenn Sie den Index auf einen anderen Server kopieren (replizieren), müssen Sie oft nur das neue, kleinere Segment kopieren.

Die Lucene-Replikation ist meist eine Do-it-yourself-Angelegenheit. Die beste Methode besteht darin, sich die Semantik der Lucene-Indexdateien zunutze zu machen. Lucene-Indizes setzen sich aus 1-n einzelnen Segmenten zusammen. Es wird ein einmaliges Schreibschema verwendet, so dass sich die Dateien jedes Segments bei Indexaktualisierungen nicht ändern. Stattdessen werden neue Dateien erstellt, und dann wird dem Index atomar mitgeteilt, dass er auf die alten Dateien, die sich nicht geändert haben, und auf alle neu erstellten Dateien verweisen soll. Diese Vorgehensweise eignet sich gut für die Indexreplikation, da es recht einfach ist, Indexänderungen mit einem Programm wie rsync effizient zu replizieren – Sie können einfach die neuen Dateien kopieren. Wenn Sie beispielsweise ein paar Dokumente zu einem Index hinzufügen, der bereits Millionen von Dokumenten enthält, wird ein neues Segment geschrieben, das die wenigen neuen Dokumente enthält, und oft muss nur dieses Segment auf die anderen Rechner repliziert werden. Die Zusammenführung von Segmenten wirkt sich zwar darauf aus, welche Segmente kopiert werden müssen, aber in vielen Fällen gibt es große unveränderte Segmente, die ein effizientes Kopieren von kleinen Indexdeltas ermöglichen.

Eine klassische Konfiguration wäre also ein Master, auf dem Sie Dokumente hinzufügen und aktualisieren, und dann n Slave-Server, auf die Sie den Master-Index replizieren (eigentlich nur die geänderten Dateien im Index).

Wenn der Zeit- und Bandbreitenbedarf für die Replikation weniger ins Gewicht fällt und ein hoher Abfragedurchsatz wichtiger ist, kann es ratsam sein, den Vorteil der Übertragung geänderter Segmente aufzugeben und nur vollständig optimierte Indizes zu replizieren. Das kostet zwar etwas mehr Ressourcen, aber der Master übernimmt die Kosten für die Optimierung (so dass die Benutzer nicht die übliche Verlangsamung des Rechners bemerken, die eine Optimierung mit sich bringt), und die Slaves erhalten immer einen vollständig optimierten Index, gegen den sie Abfragen stellen können, was eine maximale Abfrageleistung ermöglicht. Im Allgemeinen ist die Bandbreite für die Replikation jetzt kein großes Problem mehr, aber bedenken Sie, dass die Optimierung eines großen Index sehr zeitaufwändig sein kann, so dass diese Strategie nicht für jede Situation geeignet ist.

Lucene Solr Replikation

Dieses Diagramm zeigt einen einzelnen Master mit zwei Slaves. Der Master empfängt alle Aktualisierungen des Index und repliziert diese Aktualisierungen zu wichtigen Zeitpunkten an die Slaves. Die besten Zeitpunkte sind oft nach einer Optimierung oder einem Stapel von Indexaktualisierungen.

Replizieren mit Solr

Das beste Beispiel für die Replikation von Lucene-Indizes ist Solr, das sowohl über eine Unix/RSync/Skript-Lösung verfügt, die sich auf Hardlinks stützt, um effiziente Snapshots des Indexes zu erstellen, als auch über eine neue Java-Lösung, die die Vorteile der pluggable IndexDeletionPolicy von Lucene nutzt, um Snapshots des Indexes zu erhalten. Die Skript-Replikation ist ziemlich robust und funktioniert schon seit einiger Zeit gut. Die neue Java-Replikation wird erst in Solr 1.4 verfügbar sein. Sie befindet sich zwar noch in der frühen Härtungsphase (schließlich ist sie noch neu), ist aber sicherlich eine Funktion, auf die viele Benutzer gespannt sind.

Im Solr-Modell gibt es einen Master-Server, der alle Aktualisierungen durchführt, und 1-n Slave-Server, die alle Abfragen durchführen. Der Master erstellt gelegentlich „Schnappschüsse“ des Indexes und friert damit buchstäblich eine Ansicht des Indexes in der Zeit ein. Die Slaves fragen dann den Master ab, ob es einen neuen Snapshot zum Herunterladen gibt. Ist dies der Fall, werden alle geänderten Dateien vom Master auf den Slave übertragen und Solr öffnet eine neue Ansicht auf den aktualisierten Index (mit Cache-Autowarming und allem anderen, was normalerweise bei einer Aktualisierung der Indexansicht auf einem einzelnen Rechner geschieht). Sie sollten Ihr Setup sorgfältig konfigurieren, damit die Replikation ausreichend Zeit hat, um abgeschlossen zu werden, bevor eine neue Replikation ausgelöst wird. In der Praxis sollten Sie, abhängig von Ihrer Hardware und Ihrem Index, nicht weniger als eine Minute einplanen. Bei einem sehr großen Index oder einer Umgebung mit geringer Bandbreite kann die für die Replikation benötigte Zeit länger sein.

Mit diesem Modell lässt sich Solr problemlos horizontal skalieren. Fügen Sie einfach je nach Bedarf weitere Slaves hinzu, um die jeweilige Last zu bewältigen, und richten Sie dann einen Load Balancer ein, um eine einzelne virtuelle IP-Adresse zuzuweisen, die bei eingehenden Anfragen in die IP-Adresse jedes der Slaves aufgelöst wird.

Eine vollständige Anleitung zur Replikation mit Solr finden Sie im Solr Wiki: Unix-Skript-Replikation, reine Java-Replikation.

Wenn Sie sich für die skriptbasierte Replikation entscheiden, sollten Sie sich darüber im Klaren sein, dass die Java JVM einige der Skripte starten wird. Wenn die JVM jedoch einen neuen Prozess startet, verwendet sie die bevorzugte Methode des Betriebssystems zur Erstellung eines neuen Prozesses. Auf Unix-Systemen ist diese Methode in der Regel der fork-Aufruf. Der Fork-Aufruf versucht in der Regel, so viel Speicher zuzuweisen, wie der aktuelle Prozess verbraucht – dieser Speicher wird nicht verwendet, da als nächstes ein exec-Aufruf kommt, um das Skript zu starten. Das Betriebssystem kann jedoch davon ausgehen, dass Sie den angeforderten Arbeitsspeicher verwenden werden. Wenn Ihre JVM also 5 Gigabyte Arbeitsspeicher verwendet, wird sie weitere 5 Gigabyte anfordern, um ein einfaches, kleines Skript zu starten. Auch hier wird der Arbeitsspeicher nicht benötigt oder verwendet, aber Sie können eine „Out of Memory“-Ausnahme erhalten, wenn Sie nicht über den erforderlichen Arbeitsspeicher verfügen und Ihr Betriebssystem dieses Problem nicht standardmäßig behandelt. Dies ist kein neues Problem in der Welt des Forks und eine der Abhilfen ist die so genannte Speicherüberbindung in Kombination mit Copy-on-Write. In diesem Modus können RAM-Zuweisungsanforderungen gewährt werden, auch wenn sie nicht erfüllt werden können. Die Probleme mit dem fehlenden Speicher treten dann erst später auf, wenn Sie versuchen, zu viel RAM zu verwenden. Das ist der copy-on-write Teil. Der Speicher des geforkten Prozesses wird mit dem übergeordneten Prozess geteilt, bis dieser versucht, ihn zu ändern, wenn er kopiert wird. Wenn Sie damit Probleme haben, können Sie überprüfen, ob Ihr Betriebssystem so eingestellt ist, dass es den Speicher überbelegt. Ein Beispiel: Linux ist oft so eingestellt, dass es in bestimmten Situationen eine Überbelegung des Speichers zulässt, aber nicht bei sehr großen Anfragen (es wird wahrscheinlich keine Überbelegung von 5 Gigabyte zulassen). Es wird eine einfache Heuristik verwendet, um festzustellen, ob der Overcommit gewährt werden sollte. Möglicherweise müssen Sie Ihre Betriebssystemeinstellungen so ändern, dass immer ein Overcommit gewährt wird, wenn Sie OOM-Probleme feststellen, wenn Solr versucht, ein Skript zu starten.

Große Indexgrößen

Manche Indizes werden so groß, dass ein einziger Rechner sie nicht mehr angemessen aufnehmen kann. Die allgemeine Lösung besteht darin, den Index aufzuteilen, so dass sich Teile davon auf mehreren Servern befinden. Eine einzige Suche kann dann auf jedem Server durchgeführt werden, und die Ergebnisse können (wahrscheinlich parallel) abgerufen und dann zu einer einzigen Ergebnismenge für den Benutzer zusammengefasst werden. Lucene verfügt über einige Klassen, die Ihnen den Einstieg in die verteilte Suche erleichtern, und Solr bietet eine einfache, vollwertige Lösung, die auf Milliarden von Dokumenten skaliert werden kann. Lucene Solr Verteilung

In einer verteilten Konfiguration erhält ein Server-‚Shard‘ eine Abfrageanfrage und durchsucht dann sich selbst sowie die anderen Shards in der Konfiguration und gibt die kombinierten Ergebnisse von jedem Shard zurück.

Verteiltes Lucene

Die verteilte Unterstützung von Lucene ist nicht sehr umfangreich, aber es sind genügend Tools verfügbar. Lucene bietet eine RemoteSearchable-Implementierung, die eine verteilte Suche entweder mit einem MultiSearcher oder eher mit einem ParallelMultiSearcher ermöglicht. Anstatt eine Handvoll lokaler Searchables mit einem MultiSeacher zu durchsuchen, können Sie den MultiSearcher verwenden, um eine Reihe von RemoteSearchables zu durchsuchen, die jeweils auf einen anderen Server verweisen. Genau wie bei einer lokalen MultiSearcher-Suche wird jede untergeordnete Searchable durchsucht und die Ergebnisse werden kombiniert. Diese Methode der Skalierung wurde für viele verteilte Systeme verwendet, ist aber keine ideale Lösung und leidet unter übermäßigem Chattering zwischen den Servern, was die Skalierbarkeit im großen Maßstab beeinträchtigt. Für viele ist es jedoch eine einfache und angemessene Lösung. Wie ich bereits sagte, war die verteilte Suche kein Schwerpunkt des Lucene-Projekts, so dass Sie wahrscheinlich auf viele Situationen stoßen werden, in denen Sie etwas Code schreiben müssen. Tatsächlich ist RemoteSearchable nur ein Teil der cleveren Infrastruktur, die Sie wahrscheinlich für eine wirklich funktionierende Lösung entwickeln müssen. Wie so oft ist es am besten, sich an Solr zu orientieren, wenn es um die Verbreitung von Lucene geht. Denken Sie daran, dass es auch andere Ansätze gibt und verwendet werden.

Verteiltes Solr

Solr bietet eine extrem einfache, extrem skalierbare, verteilte Lösung, die sofort einsatzbereit ist. Wie ich bereits in der Einleitung erwähnt habe, ist Lucene die Kerntechnologie von IR und Solr ist ein darauf aufbauender Suchserver (mit einigen seiner eigenen Killertechnologien – siehe insbesondere Facettierung). Solr bietet eine täuschend einfache verteilte Unterstützung, die auf Lucene aufbaut.

Der Aufbau einer verteilten Solr-Serverfarm ist so einfach wie die Installation von Solr auf jedem Rechner. Solr bezeichnet jeden Server in einer verteilten Einrichtung als ‚Shard‘ und Ihre Serverfarm besteht aus 1-n Shards.

Es liegt an Ihnen, alle Ihre Dokumente auf jedem „Shard“ Ihrer Serverfarm zu indizieren. Es gibt keine standardmäßige Unterstützung für die verteilte Indizierung, aber Ihre Methode kann so einfach sein wie eine Round-Robin-Technik: Indizieren Sie jedes Dokument auf dem nächsten Server in diesem Kreis. Ein einfaches Hashing-System würde ebenfalls funktionieren. Das Solr Wiki schlägt uniqueId.hashCode() % numServers als geeignete Hashing-Funktion vor.

Beachten Sie, dass Solr keine universellen Term-/Doc-Häufigkeiten berechnet. Bei einem großen Umfang spielt es wahrscheinlich keine Rolle, dass tf/idf auf Shard-Ebene berechnet wird. Wenn Ihre Sammlung jedoch sehr ungleichmäßig auf die Server verteilt ist, könnten Sie Probleme mit den Relevanzergebnissen bekommen. Es ist wahrscheinlich am besten, die Dokumente nach dem Zufallsprinzip auf Ihre Shards zu verteilen.

Sobald Sie Ihre Dokumente in jedem Shard indiziert haben, ist die Suche über mehrere Shards hinweg ganz einfach:

http://localhost:8983/solr/select?shards=localhost:8983/solr,localhost:7…

Sie fügen einfach einen Shards-Parameter hinzu, der jede Shards-URL durch Kommata getrennt enthält. Dadurch wird der select RequestHandler veranlasst, jede der aufgelisteten URLs einzeln zu durchsuchen und die Ergebnisse dann so zu kombinieren, als hätten Sie eine Suche über einen großen Index durchgeführt. Sie sollten die Anfragen auf die einzelnen Server verteilen. Im Allgemeinen ist es jedoch am besten, wenn Sie die URL nicht zur Angabe Ihrer Shards verwenden. Wenn Sie viele Shards eingerichtet haben oder einfach keine Lust haben, sich mit einer Reihe von URLs in einer Solr GET-Anfrage zu befassen, ist es viel einfacher, den Parameter shards für Ihren SearchHandler in der Datei solrcofig.xml festzulegen. Auf diese Weise können Sie ihn einmal festlegen und ihn für eine Weile vergessen.

Jeder RequestHandler, der SearchHandler erweitert, kann SearchComponents verwenden und eine verteilte Suche durchführen. Allerdings funktionieren nur SearchComponents, die ‚distributed aware‘ sind, mit verteilten Suchen. Die aktuellen Komponenten, die eine verteilte Suche unterstützen, sind:

  • Die Komponente Abfrage, die Dokumente zurückgibt, die einer Abfrage entsprechen
  • Die Facet-Komponente für facet.query- und facet.field-Anfragen, bei denen die Facetten nach Anzahl sortiert werden (die Standardeinstellung). <Solr 1.4> Die nächste Version von Solr wird auch die Sortierung nach Namen unterstützen.
  • Die Komponente Hervorheben
  • die Komponente Debuggen

Die besten Ergebnisse erzielen Sie, wenn Sie die eingehenden Anfragen auf die einzelnen Shards verteilen. Jede Anfrage, die bei einem Shard eingeht, wird von diesem Shard an sich selbst und an die anderen Shards verteilt und die Ergebnisse werden dann zusammengeführt. Sie möchten sicherstellen, dass diese Aufgabe gleichmäßig auf Ihre Shards verteilt wird. Achten Sie jedoch auf die Deadlock-Warnung im Solr-Wiki, wenn Sie dies tun. Sie müssen sicherstellen, dass die Anzahl der Threads, die http-Anfragen in Ihrem Container bedienen, größer ist als die Anzahl der Anfragen, die Sie von der Shard selbst und allen anderen Shards in Ihrer Konfiguration erhalten können, sonst kann es zu einem Deadlock kommen.

Alle Details zur Einrichtung einer verteilten Suche mit Solr finden Sie im Solr Wiki.

Große Indexgröße und hohes Abfragevolumen

Wenn Ihr Index zu groß für einen einzelnen Rechner ist und Sie ein Abfragevolumen haben, mit dem einzelne Shards nicht mithalten können, ist es an der Zeit, jeden Shard in Ihrer verteilten Sucheinrichtung zu replizieren. Die hier vorgestellten Ideen können auch mit einem reinen Lucene-System verwendet werden, aber ich werde mich auf Solr konzentrieren, da es bereits für diese Art der Nutzung ausgelegt ist.

Die Idee ist, verteilte Suche mit Replikation zu kombinieren. Sehen Sie sich die Abbildung Verteilt und Repliziert an. Für jeden Shard gibt es einen ‚Master‘-Server und 1-n ‚Slaves‘, die vom Master repliziert werden. Auf diese Weise kann der Master-Server Aktualisierungen und Optimierungen vornehmen, ohne die Leistung der Abfrageverarbeitung zu beeinträchtigen. Abfrageanfragen sollten auf alle Slave-Server verteilt werden. Dadurch erhalten Sie sowohl eine höhere Kapazität für die Abfrageverarbeitung als auch ein Failover-Backup, falls ein Server ausfällt.

Lucene Solr Verteilung Replikation

Bei der Verteilung und Replikation weiß keiner der Master-Shards von den anderen. Sie erstellen einen Index für jeden Master, der Index wird auf jeden Slave repliziert und dann werden die Suchvorgänge auf die Slaves verteilt, wobei ein Slave von jedem Master/Slave-Shard verwendet wird.

Für eine hohe Verfügbarkeit können Sie einen Load Balancer verwenden, um eine virtuelle IP für die Slaves eines jeden Shards einzurichten. Wenn Sie mit dem Lastausgleich noch nicht vertraut sind, ist HAProxy ein guter Open-Source-Software-Lastausgleicher. Wenn ein Slave-Server ausfällt, erkennt ein guter Load Balancer den Ausfall mithilfe einer Technik (in der Regel ein Heartbeat-System) und leitet alle Anfragen an die verbleibenden Live-Slaves weiter, die mit dem ausgefallenen Slave zusammen gearbeitet haben. Es sollte dann eine einzige virtuelle IP eingerichtet werden, so dass Anfragen auf eine einzige IP zugreifen können und die Last auf jede der virtuellen IPs für die Such-Slaves verteilt wird.

Mit dieser Konfiguration haben Sie ein vollständig ausbalanciertes, fehlertolerantes System auf der Suchseite (Solr unterstützt noch keine fehlertolerante Indizierung). Eingehende Suchanfragen werden an einen der funktionierenden Slaves weitergeleitet. Der Slave verteilt dann die Suchanfrage an einen Slave für jeden der Shards in Ihrer Konfiguration. Der Slave stellt eine Anfrage an jede der virtuellen IPs für jeden Shard, und der Load Balancer wählt einen der verfügbaren Slaves aus. Schließlich werden die Ergebnisse in einer einzigen Ergebnismenge zusammengefasst und zurückgegeben. Wenn einer der Slaves ausfällt, wird er aus der Rotation genommen und die verbleibenden Slaves werden verwendet. Wenn ein Shard-Master ausfällt, können die Suchanfragen weiterhin von den Slaves bedient werden, bis Sie das Problem behoben haben und den Master wieder in Betrieb nehmen.

Fazit

Wenn Sie mit Lucene eine skalierbare Lösung für die meisten Anwendungen entwickeln, beginnen Sie, eine selbstgebaute Suchmaschine zu bauen. Das ist in der Regel nicht klug. Lucene ist eher ein Toolkit, während Solr eher eine End-to-End-Suchlösung sein will. Warum also über die Skalierung von Lucene und Solr sprechen? Möglicherweise müssen Sie Lucene skalieren, wenn Sie alten Code vererben oder spezielle Anforderungen haben, die den Einsatz von Solr verhindern. Im Allgemeinen ist es jedoch ein ziemlicher Aufwand, Lucene über mehrere Rechner hinweg richtig zu skalieren. Solr hat einen großen Teil dieser Arbeit übernommen, ebenso wie viele andere Aufgaben auf höherer Ebene, und es ist ratsam, diese Vorteile zu nutzen. Zu verstehen, wie Lucene funktioniert und skaliert, ist jedoch auch ein wichtiger Teil des Verständnisses der inneren Funktionsweise und der Skalierbarkeit von Solr. Denken Sie daran, dass Lucene die Tools für den Aufbau einer hoch skalierbaren Suchlösung bereitstellt, während das Lucene-Unterprojekt Solr Lucene zum Aufbau einer solchen Lösung verwendet.

Jetzt verstehen Sie hoffentlich, warum ich mit der Maximierung der Leistung eines einzelnen Rechners begonnen habe. Es ist zwar ein wenig offensichtlich, aber selbst wenn Sie mit Anforderungen beginnen, die über einen einzelnen Server hinausgehen, ist es sehr wichtig zu wissen, wie Sie die Leistung auf einem einzelnen Rechner maximieren können. Sowohl bei der Replikation als auch bei der Verteilung werden die Suchvorgänge auf jedem einzelnen Server einzeln durchgeführt (und im Fall der Verteilung kombiniert). Die meisten fruchtbaren Bemühungen zur Maximierung der Leistung für verteilte und replizierte Suchvorgänge sind daher die gleichen wie die zur Maximierung der Leistung auf einem einzelnen Rechner.

Ich hoffe, ich konnte Ihnen zeigen, dass Lucene und Solr sich beide als hoch skalierbare Suchlösungen erweisen. Es gibt wahrscheinlich noch viel zu erforschen und zu testen, was Sie für Ihre speziellen Anforderungen tun müssen, wenn es um eine groß angelegte Installation geht, aber ich hoffe, Sie haben jetzt ein wenig mehr Orientierung für Ihre Reise. Ich denke, Sie werden erstaunt sein, wie leistungsfähig Lucene/Solr schon mit den Standardeinstellungen ist. Wenn Sie es jedoch richtig optimieren und konfigurieren, kann Lucene/Solr bei extrem großen Sammlungen wirklich fliegen. Mit der richtigen Konfiguration ist eine Skalierung von Millionen bis Milliarden von Dokumenten mit Antwortzeiten unter einer Sekunde selbst bei hoher Last und hohen Zuverlässigkeitsanforderungen durchaus möglich.

Links

You Might Also Like

Vom Suchunternehmen zum praktischen KI-Pionier: Unsere Vision für 2025 und darüber hinaus

CEO Mike Sinoway gibt Einblicke in die Zukunft der KI und stellt...

Read More

Wenn KI schief geht: Fehlschläge in der realen Welt und wie man sie vermeidet

Lassen Sie nicht zu, dass Ihr KI-Chatbot einen 50.000 Dollar teuren Tahoe...

Read More

Lucidworks Kernpakete: Branchenoptimierte KI-Such- und Personalisierungslösungen

Entdecken Sie unsere umfassenden Core Packages, die Analytics Studio, Commerce Studio und...

Read More

Quick Links