16 August 2016 Engineering

Gerade genug Redis für Logstash

Von Aaron MildensteinGuy Boertje

Message-Queues werden gemeinsam mit Logstash-Installationen genutzt, um einen akuten Anstieg von Events abzufedern, was zu einer Verlangsamung von Elasticsearch oder anderen Downstream-Komponenten führen kann. Redis ist eine der Technologien, die aufgrund ihrer Geschwindigkeit, Einfachheit und geringen Ressourcen-Verbrauchs häufig als Message-Queue zum Einsatz kommt.

Das ist wahrscheinlich auch der Grund, warum es die Integration schon seit sehr, sehr langer Zeit gibt – genauer gesagt bereits seit Logstash Version 1.0.4, die vor mehr als fünf Jahren veröffentlicht wurde.

Mit all den neuen Verbesserung der Logstash-Pipeline und des Redis-Plugins selbst erreichst du vielleicht nicht die volle Leistung, die das Logstash-Plugin liefern könnte.

Logstash versucht, die Anzahl der Cores in deinem System zu erkennen und die entsprechend passende Anzahl an Pipeline-Workern zu ermitteln und einzustellen. Jedoch spielen Pipeline-Worker nur bei Filter- und Output-Plugins eine Rolle. Sie haben keine Auswirkungen auf Input-Plugins. Falls du im Redis-Plugin nicht die Option „Threads“ konfiguriert hast, konsumiert Redis nur mit einen Bruchteil dessen, wozu es tatsächlich in der Lage wäre.

Das Test-Setup umfasst die aktuellste stabile Logstash-Version (2.3.4) mit Redis (3.0.7), welche beide auf dem gleichen 2015 MacBook Pro, laufen. Die verwendete Logstash-Konfiguration ist

input {  
  redis {
    host => "127.0.0.1"
    data_type => "list"
    key => "redis_test"
    # batch_count => 1
    # threads => 1
  }
}
output { stdout { codec => dots } }

Rufe Logstash folgendermaßen auf: bin/logstash -f some_config_file.config | pv -Wart > dev/null

Dieses Test-Setup wurde ausschließlich für ein Testen des maximalen Datendurchsatzes entwickelt. Die tatsächliche Leistung wird je nach den verwendeten Plugins stark variieren.

Beim Testen einiger Optionen für die Einstellung threads – alle anderen Einstellungen verwenden die Standardwerte – erhalten wir Ergebnisse wie diese:

  • threads => 1 : 37.4K/sec
  • threads => 2 : 57K/sec
  • threads => 4 : 67K/sec
  • threads => 8 : 87.3K/sec

Machen wir die gleichen Tests erneut. Dieses Mal jedoch mit batch_count => 250 und dem Hinzufügen von -b 250 zu unsererem Logstash-Aufruf, um in vollem Umfang von dieser Erhöhung zu profitieren.

  • threads => 1 : 35.2K/sec
  • threads => 2 : 48.9K/sec
  • threads => 4 : 58.7K/sec
  • threads => 8 : 72.5K/sec

Wie du sehen kannst, resultiert die Erhöhung des standardmäßigen batch_count von 125 auf 250 in einer Leistungsminderung.

In der Vergangenheit war es für User normal, bei der Arbeit mit Redis einen größeren batch_count zu verwenden. Dies ist jedoch nicht länger die optimale Vorgehensweise und reduziert sogar die Performance. Dies liegt hauptsächlich an Veränderungen innerhalb der Architektur der Logstash-Pipeline. Sogar eine Erhöhung des batch_count auf 250 Ergebnisse resultiert bei einer Thread-Anzahl von threads => 8 in einem Abfall der Performance von 89,5 K/sec auf 72,5 K/sec. Der optimale Punkt für den batch_count liegt genau um 125, welches auch die standardmäßige pipeline-batch-size ist; und dafür gibt es einen Grund!

Tiefer einsteigen

Aktuelle Änderungen am Redis-Input umfassen das Aktivieren des Batch-Mode als Standard und die Nutzung eines Lua-script, um die Batch-Retrieval-Befehle auf dem Redis-Server auszuführen. Lua-Scripte fungieren auch als Transaktion, sodass wir einen Batch nehmen und die Queue in einem Schritt verkürzen können. Dies zwingt andere Redis-Verbindungen zum Warten, wobei du beachten solltest, dass dein Redis-Output-Plugin in der Upstream-Logstash-Config eine solche Verbindung ist.

Du solltest auch wissen, dass die Art und Weise, mit der Events durch die Upstream-Logstash-Instanz zu Redis hinzugefügt werden, die Performance der Downstream-Logstash-Instanz beeinflussen kann. Zum Beispiel haben wir während der Aufbereitung der Testergebnisse herausgefunden, dass ein Querying der Listengröße über LLEN redis_test in redis_cli häufig die Messwerte beeinflusst. Versuche die folgenden Faktoren im Zusammenhang mit deiner Upstream-Logstash-Instanz zu verstehen.

  • Die Rate, mit der Events generiert werden
  • Die Größe von Events – kleinste, größte und durchschnittliche
  • Die Größe der batch_events beim Redis-Output

Schaue dir einmal die Nutzung einer größeren Einheit in Bezug auf die batch_events (z. B. 500) auf dem Upstream-Redis-Output an. Dies wird die Events vor dem Aufruf von Redis zwischenspeichern, was den Redis-Input-Threads im Downstream mehr Zeit gibt, aktiv zu werden und Informationen aus Redis zu konsumieren. Wenn die Rate der Event-Generierung jedoch hoch ist, kann sich dies als nicht sonderlich hilfreich herausstellen – 50 K/sec in Batches von 500 bedeutet, dass ein Batch-Puffer innerhalb von 10 Millisekunden gefüllt ist.

Wir haben beobachtet, dass die Leistung des Lua-Scripts proportional mit größeren Batches abnimmt. Dies bedeutet, dass höhere Batch-Größen auf dem Redis-Input die Leistung beider Logstash-Instanzen auf beiden Seiten von Redis beeinflusst.

Generell ist das Ziel der Abstimmung der Redis-Input-Plugin-Config, der Pipeline-Batch-Größe und der Worker-Threads sicherzustellen, dass der Input mit der Filter/Output-Instanz mithalten kann. Du solltest wissen, wie hoch der durchschnittliche Datendurchsatz deiner Filter/Output-Instanz bei Nutzung der Standardeinstellungen ist. Falls dein Datendurchsatz zum Beispiel 35 K/sec beträgt, dann gibt es kaum Bedarf, die Anzahl der Redis-Input-Worker auf 8 zu erhöhen, wenn ein oder zwei Worker eine ausreichende Anzahl an Events abrufen können.

Weitere Variationsquellen sind die JSON-Kodierung (1), Dekodierung (2) und eine weitere Kodierung (3) – richtig gelesen, dreimal. 1 im Upstream-Redis-Output, 2 im Downstream-Redis-Input und 3 im Elasticsearch-Output. Events mit großen String-Werten benötigen einige Zeit für die Dekodierung. Stelle daher sicher, dass du in deinen Testszenarien Datensätze nutzt, die mit Blick auf Größe und Größenverteilung so genau wie möglich deinen Produktionsdaten entsprechen.

Skalierung des Datendurchsatzes mit Hilfe von mehreren Logstash-Instanzen und einem Redis-Server

Viele Nutzer versuchen, ihren Datendurchsatz durch die Inbetriebnahme mehrerer Logstash-Instanzen auf unterschiedlichen Hardware-Komponenten zu verbessern, wobei alle auf denselben Redis-Server und in die entsprechende Liste schreiben.

Dies funktioniert und kann auch gut funktionieren. Jedoch empfiehlt es sich, die gleichen Einstellungstipps bei jeder Instanz anzuwenden. Wahrscheinlich macht es nicht viel Sinn, mehrere Redis-Inputs in einer Konfiguration definiert zu haben, außer sie sind mit verschiedenen Redis-Servern oder einer anderen Liste verbunden. Tune einfach die Threads des einen Redis-Inputs. Offensichtlich wird das Setup mit drei Logstash-Instanzen, die alle auf denselben Redis-Server ausgerichtet sind, zu einem gewissen Grad an Konkurrenz im Rahmen der Client-Connection führen. Dies bedeutet, dass du wahrscheinlich erst eine Instanz einrichtest und dann alle drei optimieren solltest, sobald diese zusammen in Betrieb sind.

Fazit

In nicht allzu ferner Zukunft werden wir den Redis-Input mit einem JRuby-Wrapper um die beliebte und schnelle Jedis Java-Library verbessern. Dies wird außerdem für den zusätzlichen Vorteil einer Unterstützung des Redis-Cluster-Modes sorgen.

Um deine Redis-Input-Performance in der Zwischenzeit zu verbessern, kannst du versuchen, die Anzahl der genutzten Threads zu erhöhen. Und vergewissere dich, entsprechend zu messen und zu tunen, sofern du etwas anderes als den standardmäßigen batch_count oder mehrere Instanzen von Logstash nutzt, die mit einem einzigen Redis-Server arbeiten.

Falls du Fragen zu Redis oder Logstash hast, starte ein Diskussionsthema in unserem Forum. Ebenso kannst du uns auf IRC (#logstash) und GitHub finden.

Viel Spaß beim Logstashen!