Zum Hauptinhalt springen

Speicherverbrauch

Für groß angelegte Produktionsszenarien muss die Speichernutzung von Garnet optimiert werden, um den verfügbaren Speicher auf einem Rechner optimal zu nutzen. Hier besprechen wir die Speicherkomponenten und wie man sie abstimmt. Konfigurationsparameter sind hier aufgelistet.

Garnet ist als zwei Speicherinstanzen von Tsavorite konzipiert: der Hauptspeicher und der Objektspeicher. Jede ist unabhängig für den Speicher konfiguriert. Wenn Sie nur reine Zeichenfolgen (z. B. GET, SET und deren Varianten), HYPERLOGLOG und BITMAP-Befehle verwenden, sollten Sie den Objektspeicher mit dem Parameter DisableObjects (--no-obj) deaktivieren. Dies vermeidet Speicherverschwendung für den Objektspeicher.

Hauptspeicher

Der vom Hauptspeicher genutzte Speicher besteht aus der Summe von drei Komponenten

  • Indexgröße
  • Loggröße
  • Überlauf-Buckets

Indexgröße

Die Indexgröße wird mit dem Parameter IndexSize (-i oder --index) konfiguriert. Er gibt die Gesamtgröße in Bytes an, die der Index im Hauptspeicher belegt. Der Index ist als Hash-Buckets organisiert, wobei jeder Bucket 64 Bytes lang ist, d. h. die Größe einer Cache-Zeile. Der Bucket enthält 7 Einträge und einen Zeiger auf einen Überlauf-Bucket, der weiter unten beschrieben wird.

Die Faustregel für die Dimensionierung des Index lautet: Wenn Sie erwarten, dass der Cache-Speicher K Schlüssel enthält, setzen Sie die Größe auf K * 16 Bytes. Die Begründung dafür ist

  • Wir möchten, dass die Buckets im Durchschnitt halb voll sind, also etwa 4 Schlüssel pro Bucket
  • Daher wünschen wir uns für K Schlüssel K / 4 Buckets
  • Jeder Bucket beansprucht 64 Bytes
  • Also ist die Gesamtgröße 64 * (K / 4) = K * 16 Bytes

Loggröße

Der oben beschriebene Index enthält weder Schlüssel noch Werte. Stattdessen werden sowohl Schlüssel als auch Werte in einer separaten Struktur namens Hybrid-Log gespeichert. Der vom Log belegte Speicher wird mit MemorySize (-m oder --memory) konfiguriert.

Der Speicher ist als zirkulärer Puffer von Seiten organisiert, wobei jede Seite die Größe hat, die mit PageSize (-p oder --page) konfiguriert wird. Die Seitengröße bestimmt die maximale Schlüssel- oder Wertgröße, die Sie speichern können, da ein Datensatz vollständig in eine Seite passen muss.

Ein Datensatz im Garnet-Hauptspeicher besteht aus

  • Ein 8-Byte-Header namens RecordInfo, der Metadaten und die logische Adresse des vorherigen Eintrags in einer Datensatzkette enthält.
  • Der Schlüssel, der aus einem 8-Byte-Header gefolgt von den Schlüssel-Bytes (SpanByte) besteht.
  • Der Wert, der aus einem 8-Byte-Header gefolgt von den Wert-Bytes (SpanByte) besteht.

Überlauf-Buckets

Jeder Hash-Bucket hat 7 Einträge (Slots), die die Wurzel einer Kette von im Log gespeicherten Datensätzen enthalten. Wenn der Hash-Bucket für einen bestimmten Schlüssel voll ist, kommt es zu einem Überlauf in zusätzliche Buckets, die als Überlauf-Buckets bezeichnet werden und dynamisch zugewiesen werden. Obwohl diese nicht kontrolliert oder begrenzt werden können, sind sie typischerweise sehr klein und können ignoriert werden. Falls Ihr Index zu klein dimensioniert war, können sie mehr Speicher beanspruchen. Um dem entgegenzuwirken, können Sie den Index dynamisch vergrößern, wie weiter unten beschrieben.

Automatische Indexvergrößerung

Sie können Garnet so konfigurieren, dass der Index automatisch vergrößert wird (jedes Mal verdoppelt), wenn er sich füllt. Dies geschieht durch die Konfiguration von IndexResizeFrequencySecs (--index-resize-freq), um anzugeben, wie häufig die Größenänderungsprüfung ausgelöst werden soll. Das Indexwachstum wird ausgelöst, wenn die Anzahl der Überlauf-Buckets einen bestimmten Prozentsatz der Gesamtzahl der Haupt-Hash-Buckets überschreitet. Dieser Schwellenwert wird mit IndexResizeThreshold (--index-resize-threshold) angegeben.

Wir unterstützen auch IndexMaxSize (--index-max-size), die die maximale Größe angibt, bis zu der der Index wächst. Eine Verkleinerung der Indexgröße wird derzeit nicht unterstützt.

Objektspeicher

Der Index und die Überlauf-Buckets werden ähnlich wie im Hauptspeicher verwaltet, und die entsprechenden Parameter sind

  • ObjectStoreIndexSize (--obj-index)
  • ObjectStoreIndexMaxSize (--obj-index-max-size)

Der Log-Speicher wird jedoch anders gehandhabt, wie unten beschrieben.

Loggröße (Objektspeicher)

Beim Objektspeicher enthält der Hybrid-Log Referenzen auf Schlüssel und Werte (die Objekte) und nicht die tatsächlichen Schlüssel und Werte selbst. Der vom Objektspeicher-Log belegte Speicher wird mit ObjectStoreLogMemorySize (--obj-log-memory) konfiguriert. Dieser Parameter steuert jedoch nur die Anzahl der Datensätze im Objektspeicher, wobei jeder Datensatz aus

  • Ein 8-Byte-Header namens RecordInfo, der Metadaten und die logische Adresse des vorherigen Eintrags in einer Datensatzkette enthält.
  • Eine 8-Byte-Referenz auf das Schlüsselobjekt, das ein Byte-Array auf dem Heap ist (byte[]).
  • Eine 8-Byte-Referenz auf das Wertobjekt, das eine IGarnetObject-Instanz ist, die verschiedene Objekttypen wie SortedSet, Hash, Set usw. sein kann.

Mit anderen Worten, wenn Sie ObjectStoreLogMemorySize einstellen, steuern Sie nur die Anzahl der Datensätze im Speicher und nicht den gesamten Speicherverbrauch der Objekte. Insbesondere, da jeder Datensatz 24 Bytes lang ist, bedeutet die Einstellung von ObjectStoreLogMemorySize auf S lediglich, dass Sie maximal S / 24 Datensätze im Hauptspeicher halten können.

Dies bedeutet natürlich, dass wir den gesamten Speicher über einen anderen Mechanismus verfolgen müssen. Dazu bietet Garnet eine Konfiguration namens ObjectStoreHeapMemorySize (--obj-heap-memory) an, die den vom Heap-Byte-Arrays der Schlüssel und den IGarnetObject-Instanzen belegte Heap-Speicher in Bytes darstellt. Sie können diesen Parameter in Kombination mit --obj-log-memory verwenden, um den vom Objektspeicher insgesamt belegten Speicher zu steuern.

Zusammenfassend lässt sich sagen, dass der vom Objektspeicher insgesamt belegte Platz die Summe aus Folgendem ist:

  • Größe des Objektspeicher-Index (und Überlauf-Buckets), wie zuvor
  • ObjectStoreLogMemorySize (--obj-log-memory), die die maximale Anzahl von Datensätzen im Speicher steuert.
  • ObjectStoreHeapMemorySize (--obj-heap-memory), die die vom Speicher belegte Gesamtgröße der Objekte steuert.

Lese-Cache

Der Lese-Cache hilft, Datensätze von der Festplatte in den Speicher in einem separaten Lese-Cache-Bereich zu laden, ohne das Haupt-Log zu vergrößern. Dies hilft, zusätzliche I/Os zu vermeiden, wenn Datensätze gelesen werden, die sich bereits auf der Festplatte befinden. Weitere Details zu den Interna des Lese-Caches finden Sie hier.

Der Lese-Cache kann unabhängig für den Hauptspeicher oder den Objektspeicher oder beides gleichzeitig aktiviert werden. Verwenden Sie die Option --readcache, um ihn für den Hauptspeicher zu aktivieren, und die Option --obj-readcache für den Objektspeicher.

Die folgenden Konfigurationsoptionen können verwendet werden, um die Speichernutzung des Lese-Caches zu steuern

  • --readcache-page steuert die Größe jeder Seite für den Lese-Cache des Hauptspeichers.
  • --readcache-memory steuert die Gesamtgröße des Lese-Caches des Hauptspeichers.
  • --obj-readcache-page gibt die Größe jeder Seite für den Lese-Cache des Objektspeichers an.
  • --obj-readcache-log-memory steuert die Gesamtgröße des Lese-Cache-Log-Speichers des Objektspeichers, was die Möglichkeit ist, die maximale Anzahl von Datensätzen im Speicher zu steuern.
  • --obj-readcache-heap-memory steuert die von den Objekten im Lese-Cache belegte Gesamt-Heap-Größe.