Verwendung von DocValues in Solr 4.2
In der neuesten Version von Apache Solr(4.2) wurde die Unterstützung für DocValue-Feldtypen hinzugefügt. DocValues sind in Lucene schon seit einiger Zeit in Arbeit(~5 Jahre), auch unter dem Namen „column stride fields“, aber seit kurzem sind sie stabil genug, um in Solr integriert zu werden. Für diejenigen unter Ihnen, die mit dem Lucene-Jargon nicht vertraut sind: DocValues sind spaltenorientierte Felder. Mit anderen Worten, die Werte von DocValue-Feldern werden dicht in Spalten gepackt und nicht spärlich gespeichert wie bei gespeicherten Feldern.
Zur Veranschaulichung mit etwas JSON:
zeilenorientiert (gespeicherte Felder)
{ 'doc1': {'A':1, 'B':2, 'C':3}, 'doc2': {'A':2, 'B':3, 'C':4}, 'doc3': {'A':4, 'B':3, 'C':2} }
spaltenorientiert (docValues)
{ 'A': {'doc1':1, 'doc2':2, 'doc3':4}, 'B': {'doc1':2, 'doc2':3, 'doc3':3}, 'C': {'doc1':3, 'doc2':4, 'doc3':2} }
Wenn Solr/Lucene eine Reihe von Dokument-IDs aus einer Abfrage zurückgibt, verwendet es dann die zeilenorientierte (auch als gespeicherte Felder bezeichnete) Ansicht der Dokumente, um die tatsächlichen Feldwerte abzurufen. Dies erfordert nur eine sehr geringe Anzahl von Suchvorgängen, da alle Felddaten dicht beieinander in der Felddatendatei gespeichert sind.
Für das Facettieren/Sortieren/Gruppieren muss Lucene jedoch über jedes Dokument iterieren, um die Feldwerte zu sammeln. Traditionell wird dies durch die Invertierung des Termindexes erreicht. Das funktioniert eigentlich sehr gut, da die Feldwerte bereits gruppiert sind (aufgrund der Natur des Index), aber es ist relativ langsam beim Laden und wird im Speicher gehalten. DocValues zielt darauf ab, diese beiden Probleme zu lindern und gleichzeitig die Leistung vergleichbar zu halten.
Datensatz und Schema
Um die Eigenschaften von DocValues zu demonstrieren, habe ich einen Index mit 300.000 Artikeln der New York Times erstellt, der mit gespeicherten, indizierten und docValue-Feldern gemischt ist. Der Datensatz ist im Wesentlichen ein invertierter Index aus (docId, wordId, count). Wir erstellen ein Solr-Dokument pro Eintrag und fügen für das Beispiel ein paar weitere Felder hinzu.
Wir werden die folgenden Felder verwenden, um unseren Index zu erstellen:
- id: Automatisch generiert
- docId: int
- wordId: int
- Anzahl: int
- threadId: String (der Name des Java-Threads, der dieses Dokument gesendet hat)
- word: String (die dereferenzierte wordId)
Für jedes dieser Felder (außer „id“) werden wir drei Solr-Felder erstellen: ein gespeichertes, ein indiziertes und ein DocValue-Feld. Wir werden auch ein Feld „text“ einfügen, das ein Kopierfeld von word ist, das als text_en indiziert wird (Sie wissen schon, für die Suche). Hier ist ein Teil der resultierenden Solr schema.xml:
[gist id=“5265560″]
Es gibt 300.000 docId
-Werte und 102.660 wordId
-Werte, mit insgesamt 69.679.427 Kombinationen im Datensatz. threadId
wurde hinzugefügt, um ein Feld mit sehr geringer Kardinalität (4) anzuzeigen, und word
ist einfach wordId
, das gegen ein mit dem Datensatz geliefertes Wörterbuch dereferiert wird (damit wir etwas Text zum Suchen haben).
Hier sehen Sie ein Beispielsegment aus dem resultierenden Index:
1445843 Mar 26 10:43 _89.fdt 655 Mar 26 10:43 _89.fdx 1239 Mar 26 10:43 _89.fnm 62012 Mar 26 10:41 _89.nvd 46 Mar 26 10:41 _89.nvm 377 Mar 26 10:43 _89.si 589009 Mar 26 10:41 _89_Lucene41_0.doc 1301657 Mar 26 10:41 _89_Lucene41_0.tim 34516 Mar 26 10:41 _89_Lucene41_0.tip 442588 Mar 26 10:41 _89_Lucene42_0.dvd 122 Mar 26 10:41 _89_Lucene42_0.dvm
Die Dateien „dvd“ und „dvm“ sind die DocValues, „tim“ und „tip“ sind der Begriffsindex und das Wörterbuch, und „fdx“ und „fdt“ sind die gespeicherten Felder. Was der Rest dieser Dateien ist, können Sie in der Lucene-Dokumentation nachlesen. Ohne weiter darauf einzugehen, können wir feststellen, dass die DocValues viel kompakter sind als die gespeicherten Felder und der Begriffsindex, wenn wir uns nur die Dateigrößen ansehen (erinnern Sie sich daran, dass wir jedes unserer Felder als stored, indexed und docValues separat speichern). Da die Werte für ein einzelnes Feld zusammenhängend gespeichert werden, können sehr effiziente Packalgorithmen verwendet werden.
Das Ganze endet bei knapp 4 GB mit 69.639.425 Dokumenten (mein Indizierungsskript hat am Ende einen Stapel verloren). Hier ist ein Gist meines Indizierungscodes.
Verfahren
Meine Umgebung:
- Mac OS X 10.7.5
- 2,8 GHz Intel Core i7
- 16 GB 1333 MHz DDR3
- Apache Solr 4.2
- java -Xmx4g -Xms4g -jar start.jar
Vor jedem Test würde ich den Befehl purge
ausführen, um den Festplatten-Cache zu leeren. Dann würde ich die Abfrage einmal ausführen, um das anfängliche Timing zu erhalten, und dann den Durchschnitt der nächsten 10 Anfragen messen. Ich teste die Facettenleistung nur mit der folgenden Abfrage:
http://localhost:8983/solr/nytimes/select?q=*:*&facet=true&facet.field=$field
Ich habe auch alle in solrconfig.xml konfigurierten Caches deaktiviert.
Ergebnisse
field first avg mem+ gc- mem-gc ------------------------------------------------------ threadId_dv 2434 1798 30.5 19.7 10.8 threadId_idx 3421 1175 40.2 24.3 15.9 wordId_dv 6594 2882 157 14.9 142 wordId_idx 8311 2019 279 14.1 265 docId_dv 5502 2356 78.1 22.5 55.6 docId_idx 4915 1458 282 16.3 266 word_dv 8521 3266 209 60.0 149 word_idx 17322 1209 690 490 200
- first: Zeit in Millisekunden für die erste Anfrage
- avg: durchschnittliche Zeit in Millisekunden für die folgenden 10 Anfragen
- mem+: Erhöhung des aktiven Speichers während der ersten Anfrage
- gc-: Speicher, der nach der ersten Anfrage durch manuelle Garbage Collection wiederhergestellt wird
- mem-gc: mem+ minus gc-
Bei der anfänglichen Ladezeit sind die Ergebnisse unterschiedlich. DocValues schneiden in einigen Fällen besser ab, Termindex in anderen. Die Leistungsunterschiede hängen hier weitgehend von der Anzahl der eindeutigen Werte für das Feld und dem Feldtyp ab. Der größte Unterschied bei der Ladezeit ist das Feld „word_idx“, das doppelt so lange zum Laden aus dem invertierten Index braucht und dreimal so viel Speicherplatz benötigt. Bei wiederholten Zugriffen auf dasselbe Feld schneidet der invertierte Index aufgrund der internen Lucene-Zwischenspeicherung besser ab (auch der Grund für den höheren Speicherverbrauch). In allen Fällen verbrauchen DocValues beim Laden und nach der Garbage Collection weniger Speicherplatz.
Diskussion
DocValues haben viele potenzielle Einsatzmöglichkeiten. Wie wir in unserem kleinen Experiment gesehen haben, benötigen sie weniger Speicher als indizierte Felder und sind in der Regel schneller zu laden. Wenn Sie in einer Umgebung mit wenig Speicherplatz arbeiten oder ein Feld nicht indizieren müssen, sind DocValues perfekt für Facettenbildung/Gruppierung/Filterung/Sortierung geeignet. Sie haben auch das Potenzial, die Anzahl der Felder, die Sie facettieren/gruppieren/filtern/sortieren können, zu erhöhen, ohne den Speicherbedarf zu erhöhen. Alles in allem sind DocValues ein hervorragendes Werkzeug, das Sie in Ihren Werkzeuggürtel für das Schema-Design aufnehmen können.
Blick nach vorn
Wenn man an spaltenorientierte Datenbanken denkt, kommt einem natürlich die analytische Verarbeitung in den Sinn. Wenn jemand eine analytische Verarbeitungs-Engine vor Solr Cloud stellen würde, könnten Sie eine leistungsstarke, skalierbare OLAP-Datenbank erstellen. Der geringe Speicherbedarf von DocValues und die Skalierbarkeit von Solr Cloud sind zwei notwendige Zutaten – alles, was noch fehlt, ist etwas wie die StatsComponent, aber flexibler und steckbar, um Aggregationen über den gesamten Index zu erstellen. Nur ein kleiner Denkanstoß.