- Fast Dedup in OpenZFS 2.3.0 ist deutlich besser als die bisherige Deduplizierung, bleibt aber eine Funktion, bei der normale Nutzer Kosten und Voraussetzungen genau abwägen sollten, statt sie wie eine Standardeinstellung zu aktivieren
- Deduplizierung schreibt bereits gespeicherte Blöcke nicht erneut, sondern erhöht nur Referenzen; dadurch fallen auf allen Schreib- und Freigabepfaden Kosten für Abfragen und Aktualisierungen der dedup table an
- Der bisherige Ansatz verursachte hohe Speicher- und IO-Last durch Read-Modify-Write-Amplification der ZAP-basierten Tabelle, eine während Transaktionen nicht freigebbare Live-Entry-Liste und nutzlose Unique Entries
- Fast Dedup reduziert Live Entries von 424 auf 216 Byte und gibt Betreibern mit dedup log, inkrementellem Flush,
zpool ddtprune,dedup_table_quota, DDT-Prefetch und kstats mehr Kontrolle über die Kosten - Bei allgemeinen Workloads gibt es oft nur wenige tatsächlich doppelte Blöcke; da BRT/block cloning aus OpenZFS 2.2 ähnliche Einsparungen mit geringeren Kosten bietet, ist Deduplizierung nur dann vorsichtig sinnvoll, wenn massenhaft doppelte Daten vorliegen und keine Zero-Copy-Alternative verfügbar ist
Grundlegende Funktionsweise von OpenZFS dedup
- Deduplizierung (dedup) ist eine Funktion, bei der OpenZFS vor dem Schreiben von Daten auf die Festplatte prüft, ob dieselben Daten bereits vorhanden sind; falls ja, wird der neue Schreibvorgang übersprungen und der vorhandenen Kopie eine Referenz hinzugefügt
- Der schwierige Teil besteht darin, laufend Informationen zu speichern und abzufragen, um schnell herauszufinden, „ob sie bereits auf der Platte liegen“ und „wo sie liegen“
- Die Struktur, die diese Informationen enthält, ist die dedup table
- Konzeptionell ist sie eine Hash-Tabelle mit der Datenprüfsumme als Schlüssel und der Plattenposition sowie dem Referenzzähler (refcount) als Wert
- Sie ist keine Nutzerdatenstruktur, sondern strukturelle Pool-Daten, die als Teil der Pool-Metadaten gespeichert werden
Kosten auf Schreib- und Freigabepfaden
- Wenn dedup deaktiviert ist, weist OpenZFS Speicherplatz über den Metaslab Allocator zu, setzt die zurückgegebene DVA in den Block Pointer ein und schreibt die Daten
- Wenn dedup aktiviert ist, wird zuerst die Prüfsumme in der dedup table nachgeschlagen
- Gibt es keinen Eintrag, wird neuer Speicherplatz zugewiesen, die Daten werden geschrieben und ein neuer dedup entry mit refcount 1 angelegt
- Gibt es einen Eintrag, wird die vorhandene DVA in den Block Pointer kopiert, die Schreib-IO als abgeschlossen behandelt und der refcount erhöht
- Bei Blöcken, die per dedup zugewiesen wurden, wird im Block Pointer das Flag
Dgesetzt- Beim Freigeben wird bei gesetztem
D-Flag erneut in der dedup table nachgeschlagen und der refcount verringert - Fällt der refcount auf 0, wird der dedup entry gelöscht und der tatsächliche Speicherplatz freigegeben
- Beim Freigeben wird bei gesetztem
- Da jedes Schreiben und jede Freigabe Abfragen und Aktualisierungen der dedup table durchläuft, muss die tatsächliche Einsparung an Speicherplatz und IO größer sein als der Verwaltungs-Overhead der Tabelle, damit dedup sinnvoll ist
Warum die bisherige Deduplizierung schlecht war
-
Amplification der ZAP-basierten dedup table
- Die bisherige dedup table verwendet ZAP, OpenZFS’ standardmäßiges On-Disk-Hash-Tabellenobjekt
- ZAP ist eine allgemeine Struktur, die auch für Verzeichnisse, Attributlisten und interne Verwaltung genutzt wird, passt aber schlecht zur Speicherung von dedup entries
- Ein typischer dedup entry besteht aus einem 40-Byte-Schlüssel und einem nach Kompression etwa 64 Byte großen Wert; in einen 32K-ZAP-Block passen etwa 188 gewöhnliche Entries
- Da OpenZFS keine Teilblock-Schreibvorgänge und kein In-Place-Overwrite ausführt, muss selbst beim Aktualisieren eines einzelnen Entry der gesamte ZAP-Block gelesen, geändert und anschließend als neuer Block erneut geschrieben werden
- Prüfsummenschlüssel müssen stark kollisionsresistent sein, daher ist es unwahrscheinlich, dass zwei beliebige Entries nahe beieinander im selben ZAP-Block liegen; ebenso unwahrscheinlich ist, dass mehrere Aktualisierungen innerhalb einer Transaktion im selben Block zusammenfallen
- Wenn genügend RAM vorhanden ist und ARC die dedup table dauerhaft vorhält, sinken die Lesekosten; daraus entstand jedoch die alte Empfehlung, dass dedup sehr viel Speicher benötigt
- Die vdev class
dedupkann den Speicherbedarf etwas reduzieren, indem ein ausreichend großes und schnelles dediziertes dedup vdev hinzugefügt wird; bei Größenordnungen, in denen dedup sinnvoll ist, muss es jedoch groß genug für die gesamte Tabelle und schnell genug sein
-
Speicherverbrauch der Live-Entry-Liste
- OpenZFS hält während einer Transaktion erstellte oder geänderte dedup entries in einer live entry list im Speicher
- Diese Struktur soll verhindern, dass bei mehrfach gleichzeitig geschriebenen identischen Daten jeder Schreib-Thread entscheidet, die Daten seien noch nicht in der dedup table vorhanden, und sie jeweils neu schreibt
- Ein Lookup prüft zuerst die live entry list
- Ist der entsprechende Entry vorhanden, wird der refcount erhöht
- Ist er nicht vorhanden, wird ein Live Entry im Zustand „in progress“ erstellt, der tatsächliche Entry aus ZAP gelesen und anschließend auf „ready“ gesetzt
- Andere gleichzeitig zugreifende Schreib-Threads warten, bis der Entry ready ist
- Beim Ende der Transaktion wird die live entry list durchlaufen und ihr Inhalt in den dedup ZAP übernommen
- Ein bisheriger Live Entry war 424 Byte groß; dieser Speicher gehört nicht zu ARC, sondern zum Kernel Slab Memory und kann bei Speicherdruck des Systems nicht zurückgewonnen werden
- Die live entry list wird bei jeder Transaktion geleert, aber wenn in einer Transaktion viele unterschiedliche Daten geschrieben werden, kann ihr Peak groß werden
-
Unique Entries blähen die Tabelle auf
- Dedup verfolgt alle auf der Platte gespeicherten Blöcke, tatsächlicher Nutzen entsteht aber nur, wenn der refcount größer als 1 ist
- Ein unique entry mit refcount 1 ist im Wesentlichen ein Kostenposten, der darauf wartet, dass irgendwann dieselben Daten erneut geschrieben werden
- Dedup wird nach Verschlüsselung und Kompression auf Blockebene ausgeführt
- Selbst identische Ausgangsdaten werden nur dann als derselbe Block behandelt, wenn auch Kompressionsmethode, Verschlüsselungsschlüssel und Ausrichtung innerhalb der Datei übereinstimmen
- Bei allgemeinen Workloads gibt es oft nur wenige „wirklich identische“ Blöcke, sodass die Kosten von dedup leicht den Nutzen übersteigen
Verbesserungen durch Fast Dedup
-
Verkleinerung von Live Entries
- Fast Dedup reduziert zunächst den Memory Footprint der live entry list
- Große numerische Flag-Typen in
ddt_entry_twurden durch Bitfields ersetzt, und Synchronisationsfelder wurden vereinfacht - Ein 40-Byte-Zustand, der nur beim ersten Schreiben eines deduplizierten Datenblocks oder bei einem notwendigen Repair Write verwendet wird, wurde in ein separates IO-State-Objekt ausgelagert
- Der bisherige Wert eines dedup entry war 256 Byte groß und enthielt vier Physical Entries
- Jeder Physical Entry enthält drei 128-Bit-DVAs, einen refcount und eine Birth Transaction ID
- Der vierte Entry ist ein Überbleibsel der früheren Funktion
dedupditto; modernes OpenZFS unterstützt ihn nur noch lesend und schreibt ihn nicht neu - Wenn sich
copies=ändert und mehr DVAs benötigt werden, behält Fast Dedup die vorhandene Variante nicht als separaten Entry bei, sondern weist nur die zusätzlich benötigten Kopien zu und fügt sie dem vorhandenen dedup entry hinzu - Der Entry-Wert der neuen Fast-Dedup-Tabelle schrumpft von 256 Byte auf 72 Byte
- Ein Entry in der Live-Liste sinkt von bisher 424 Byte auf 216 Byte
-
Einführung des dedup log
- Der bisherige Ansatz schrieb die live entry list am Ende einer Transaktion direkt in den dedup ZAP zurück; auch wenn die 187 Einträge um einen Entry herum meist irrelevant waren, fielen blockweise Aktualisierungskosten an
- Fast Dedup ergänzt auf Basis der Beobachtung, dass kürzlich erstellte oder deduplizierte Blöcke mit höherer Wahrscheinlichkeit erneut dedupliziert oder freigegeben werden, ein dedup log
- Am Ende einer Transaktion werden Änderungen an Live Entries nicht direkt in ZAP geschrieben, sondern im Log aufgezeichnet
- Das On-Disk-Log ist für Crash Safety erforderlich
- Das In-Memory-Log wird für schnelle Lookups gehalten
- Die Lookup-Reihenfolge lautet damit: live entry list, In-Memory-Log, dedup ZAP
- Das On-Disk-Log wird beim Pool-Import verwendet, um das In-Memory-Log wiederherzustellen
-
Inkrementeller Log-Flush
- Wenn das Log wie im frühen Ansatz erst bei zu großer Größe auf einmal nach ZAP geflusht wird, können schon wenige Tausend Entries lange Pausen verursachen
- Fast Dedup nutzt incremental flushing, bei dem in jeder Transaktion ein Teil des Logs nach ZAP übernommen wird
- Die Flush-Menge wird im Vergleich zur tatsächlich für IO verbrauchten Zeit angepasst
- In geschäftigen Phasen wird weniger geschrieben, in ruhigen Phasen mehr
- Wenn das In-Memory-Log groß wird und Speicherdruck erzeugt, kann der Flush beschleunigt werden
- Um das On-Disk-Log append-only zu halten und es trotzdem ohne vollständigen Stopp leeren zu können, werden zwei Logs verwendet
- Eines nimmt als active log neue Änderungen auf
- Das andere wird als flushing log nach ZAP übernommen
- Wenn das flushing log leer ist, wird das On-Disk-Log genullt und die Rollen der beiden Logs werden getauscht
- Bei Pool-Scans wie scrub oder resilver gibt es im dedup log kein stabiles Positionskonzept; daher wird auf Scan-Anforderung das Log-Flushing beschleunigt, alles in den dedup ZAP übernommen und anschließend nach dem bisherigen Verfahren gescannt
Verwaltung von Unique Entries und Betriebsfunktionen
zpool ddtpruneentfernt einige unique entries aus der dedup table eines Pools- Dies kann nach Alter oder Prozentanteil angegeben werden
- Das Alterskriterium passt besonders gut zu Workloads, bei denen kürzlich genutzte Daten mit höherer Wahrscheinlichkeit erneut dedupliziert werden
- Wenn ein Block, dessen dedup entry durch Pruning entfernt wurde, später kopiert wird, wird er nicht gegen den vorhandenen Block dedupliziert, sondern als neuer Block zugewiesen
- Wenn ein alter Unique Block jedoch plötzlich mehrfach kopiert wird, können mehrere Referenzen auf einen neuen Block entstehen
- Die Pool-Property
dedup_table_quotabegrenzt die maximale Größe der dedup table- Wenn die Erstellung eines neuen Entry das Limit überschreiten würde, wird kein Entry angelegt und der Vorgang als normaler non-dedup write behandelt
- Das lässt sich zusammen mit einem dedizierten dedup device verwenden, damit bei vollem Gerät nichts auf das main device überläuft
zpool prefetch -t ddtlädt die dedup table vorab in ARC- Das kann direkt nach einem Pool-Import der Performance helfen
- Auch bei Fast Dedup ist es wirksam, weil für Entry-Lookups, die nicht im Log liegen, und beim Flush weiterhin ZAP-Zugriffe nötig sind
- Neue kstats und Tuneables wurden ebenfalls hinzugefügt
- Linux:
/proc/spl/kstat/zfs/<pool>/ddt_stats_<checksum> - FreeBSD:
kstat.zfs.<pool>.misc.ddt_stats_<checksum> - Linux-Tuneable:
/sys/modules/zfs/parameters/zfs_dedup_log_* - FreeBSD-Tuneable:
vfs.zfs.dedup.log_*
- Linux:
- Bestehende dedup-aware Tools wie
zpool status -D,zdb -Dundzdb -Swurden ebenfalls aktualisiert, damit sie die neue Struktur verstehen
Kompatibilität mit bestehenden dedup tables
- Der Großteil von Fast Dedup erfordert Änderungen am On-Disk-Format und lässt sich daher nicht unverändert auf bestehende dedup tables anwenden
- Auch bei bestehenden Tabellen können Funktionen arbeiten, die keine Änderung des On-Disk-Formats benötigen
dedup_table_quotazpool prefetch -t ddtddt_stats_*-Lookup und Hit Count- ZAP shrink
- Das dedup log auch für traditionelle Tabellen funktionsfähig zu machen, bleibt eine vergleichsweise straightforward Aufgabe
- Allerdings erhält man dann nicht die Vorteile kleinerer Live-/Log-Entries
- Bei
zpool ddtprunelässt sich für bestehende Tabellen vor allem der Modus „percentage of uniques“ leicht ergänzen- Der Altersmodus benötigt Daten aus dem neuen Entry-Format und ist daher mit dem alten Format nicht möglich
- Derzeit gibt es keine Funktion, um eine bestehende Tabelle in das neue Format zu konvertieren
- In einfachen Fällen, in denen
copies=nie geändert wurde, wäre es möglich, einen neuen ZAP anzulegen und die bestehenden Entries zu konvertieren und zu kopieren - Eine Online-Konvertierung ist komplex, weil sie parallele Lookups oder parallele Schreibvorgänge in altem und neuem ZAP erfordert
- Eine Offline-Konvertierung ist einfacher, würde aber erfordern, den Pool offline zu nehmen
- Wenn durch Änderungen an
copies=mehrere Varianten einen refcount besitzen, kann eine vollständige Konvertierung unmöglich sein
- In einfachen Fällen, in denen
- Das Senden eines deduplicated Dataset an einen anderen Pool, der neue dedup unterstützt, funktioniert
„Warum nicht aktivieren, obwohl es besser geworden ist?“
- Fast Dedup senkt den Overhead gegenüber der bisherigen Variante und kann dadurch in mehr Grenzfällen nützlich sein
- Trotzdem bleibt dedup ein Abwägungsproblem zwischen IO-Durchsatz, Speicherverbrauch und Größe der dedup table
- Bei allgemeinen Workloads können deduplizierbare Blöcke extrem selten sein
- Das simulierte DDT-Ergebnis von
zdb -Sfür einen Beispiel-Laptop-Pool zeigte praktisch keinen dedup-Nutzen- Die meisten der 11,7 Mio. Entries waren Unique Entries mit refcount 1
- Die Zahl tatsächlich deduplizierbarer Entries lag im Bereich von Rundungsfehlern bezogen auf die Gesamtmenge
- Es wurde
dedup = 1.00angezeigt
- In solchen Fällen bringt das Aktivieren von dedup fast nichts und erzeugt nur zusätzlichen IO- und Speicherdruck
Wann BRT/block cloning besser ist
- Seit OpenZFS 2.2 gibt es BRT, also block cloning beziehungsweise reflinks
- Die dedup table ist eine Struktur, mit der ohne Kontext herausgefunden werden soll, „ob diese Daten bereits auf der Platte liegen“
- Moderne Systeme können dem Storage Stack in manchen Fällen mitteilen, dass ein Kopiervorgang tatsächlich eine Kopie ist
copy_file_range()in Linux- und FreeBSD-Dateisystemencopyfile()unter macOSFSCTL_SRV_COPYCHUNKunter Windows- Ähnliche Funktionen gibt es auch in NFS, CIFS, OS-Block-Device-Treibern, SCSI EXTENDED COPY, NVMe Copy usw.
- Wenn Client-Programme und Zwischenschichten Copy-Offload-Signale weitergeben, kann OpenZFS in BRT nur den refcount erhöhen
- BRT verursacht keine Kosten, wenn ein Block nicht geklont wird; bei geklonten Blöcken ist ein Entry 16 Byte groß
- Im Beispiel-Pool zeigte BRT
used 292M; saved 309M; ratio 2.05x - Die Raw Saving war etwas geringer und ungefähr auf dem Niveau der dedup-Simulation, aber ohne die hohen Kosten, alle nicht geklonten Blöcke zu verfolgen
Praktische Entscheidungskriterien
- Fast Dedup verbessert alle drei klassischen Achsen der traditionellen Deduplizierung: IO throughput, memory usage und dedup table size
- Auch die katastrophalen Kosten bei Fehleinschätzungen sind geringer geworden, und Betreiber haben Werkzeuge bekommen, um die Tabelle zu begrenzen und aufzuräumen
- Damit es sich lohnt, müssen die Voraussetzungen dennoch klar erfüllt sein
- Die Datenmenge muss sehr groß sein
- Dieselben Daten müssen häufig kopiert werden
- Andere Zero-Copy-Optionen von OpenZFS wie block cloning oder snapshot clone dürfen nicht nutzbar sein
- Für Workloads, bei denen Clients ein klares Signal „bitte kopieren“ geben können, kann block cloning mit geringeren Kosten großen Nutzen bringen
1 Kommentare
Hacker-News-Kommentare
Offline-Deduplizierung oder eine verzögerte Deduplizierung, bei der man den Pool nicht komplett herunterfahren muss, die aber auch nicht sofort ausgeführt wird, wäre wünschenswert
Es wirkt in den meisten Fällen wie der falsche Ansatz, dass beim Aktivieren der Deduplizierung bei jedem Schreib- und Freigabevorgang ein Lookup und ein Schreibzugriff auf die Deduplizierungstabelle nötig sind. Wenn man Daten schreibt, möchte man in erster Linie, dass es so schnell wie möglich fertig ist, selbst wenn dabei mehr Plattenplatz verbraucht wird; deshalb speichert man die Datei, an der man gerade arbeitet, auch nicht in einem 7zip-Archiv. Es wäre gut, wenn ZFS später, wenn das System wenig ausgelastet ist, doppelte Daten finden und mit etwas wie BRT Speicherplatz zurückgewinnen könnte; das ließe sich möglicherweise auch als Teil eines normalen Scrubs erledigen
Die physische Position geht gehasht in den Merkle-Hash-Baum ein, daher müsste man beim Verschieben der physischen Position alle inneren Knoten bis zu dem Knoten, den man ändern will, neu schreiben, was zu teuer ist. Ein besseres Design wäre wohl gewesen, alle Knoten mit Block-Pointern in einen Teil aufzuteilen, der nur logische Block-Pointer enthält und in den Baum gehasht wird, und einen anderen Teil, der nur die physische Position als eine Art Cache für diesen logischen Pointer enthält, aber nicht in den Merkle-Baum eingeht. Dann müsste man für ein BP-Rewrite nur die Blöcke neu schreiben, die nicht Teil des Merkle-Baums sind. Mit der aktuellen Struktur ist es schwer, die gewünschte Funktion in ZFS zu bekommen, aber vielleicht wäre ein Workaround möglich, bei dem man bei einem Hash-Mismatch beim Lesen über den Hash des Pointers den Block in der Deduplizierungstabelle sucht und den deduplizierten Block neu zuweist. Der Preis dafür wäre ungefähr ein nutzloser Lesezugriff, also nicht allzu schlimm, aber wenn BP-Rewrite unmöglich ist, entstehen meist solche Flicklösungen
Es verbraucht viel RAM und I/O, aber man kann den „groveler“ terminieren und begrenzen. Allerdings hatte ich zu Zeiten von Windows 2012 R2 wegen eines Bugs einmal datenfressende Beschädigungen
Zum Beispiel mit jdupes oder duperemove. Ich habe sowohl an ZFS als auch an duperemove PRs geschickt, damit die nötigen Systemaufrufe unterstützt werden. Bei ZFS dauerte das Review lange, und mir ist klar geworden, dass ich den Abschluss völlig vergessen hatte, also muss ich das wieder aufgreifen
Das kann in vielen Situationen die Write-I/O deutlich reduzieren. In deduplizierenden Storage-Arrays gibt es auch Erweiterungen, bei denen beim Kopieren einer Datei zwischen zwei VMs die eigentlichen Daten gar nicht kopiert werden und stattdessen nur der Referenzzähler des ursprünglichen Blocks erhöht wird. Aus Sicht des Betriebssystems wirkt das wie absurd hohe Schreibgeschwindigkeiten im TB/s-Bereich, was ziemlich cool ist
Die Aussage, „das Grundproblem traditioneller Deduplizierung ist, dass dieser Overhead so groß ist, dass sie sich außerhalb seltener und spezieller Workloads kaum lohnt“, klingt ziemlich seltsam
Ich habe mit Arrays von Pure und Dell/EMC gearbeitet, und bei VMWare-Workloads ergab sich durch Deduplizierung/Komprimierung meist mindestens eine 3:1-Ersparnis. Das Speichern nur einer einzigen Kopie eines Basis-VM-Images funktioniert sehr gut. Auch auf einem Syslog-Server haben Deduplizierung/Komprimierung gut gegriffen, und ich habe dort schon 6:1-Einsparungen gesehen. Wie gut Deduplizierung funktioniert, hängt stark von der Größe der gehashten Blöcke ab, und kleiner ist besser. Je kleiner die Blöcke werden, desto schneller steigt die Wahrscheinlichkeit passender Blöcke; nach meiner Erfahrung ist die bevorzugte Blockgröße 4 KB
Außerdem scheint dort Komprimierung und Deduplizierung miteinander vermischt zu werden, obwohl das unterschiedliche Funktionen sind. In ZFS kann man die Komprimierung auf dem Pool aktivieren, und das lohnt sich fast immer, während Deduplizierung deaktiviert bleiben kann
Wenn man allerdings VMs auf einem ZFS-Dateisystem hostet, nutzt man wahrscheinlich ohnehin bessere Strategien wie Block- oder Dateisystem-Klone. Wer das nicht tut, verzichtet in diesem Umfeld auf eines der zentralen Unterscheidungsmerkmale von ZFS. Bei allgemeinen Fileservern oder privaten Desktops/Laptops gibt es normalerweise nur sehr wenige doppelte Blöcke, sodass sich der Overhead nicht lohnt. Bei Backups kann es je nach Implementierung und danach, ob vor der Dateisystemschicht verschlüsselt wird, funktionieren oder auch nicht. Komprimierung ist ein ganz anderes Thema, und die aktuelle Best Practice für ZFS ist, sie für fast alle Workloads standardmäßig zu aktivieren. Heutzutage sind die CPU-Kosten so gering, dass sie kaum der Rede wert sind, und unabhängig von der Platzersparnis kann die I/O-Reduktion beträchtlich sein. Bei einem typischen Log-Repository sind nach meiner Erfahrung sogar deutlich bessere Einsparungen als 6:1 möglich
Wenn man annimmt, dass 1 TB Plattenplatz derzeit etwa 15 Dollar kostet und 5 GB Server-RAM etwa 25 Dollar, dann braucht man schon eine Deduplizierungsrate von 3:1, nur um den Break-even zu erreichen. Wenn die Daten gut passen, könnte man vielleicht mit 1 GB pro TB auskommen, aber mit Pech reichen auch 5 GB nicht. Deshalb hieß es im Artikel, dass ZFS-Deduplizierung einen kleinen Sweet Spot hat, in dem die Daten exakt passen müssen, und dass es die meisten deshalb nicht interessiert. Andere Dateisysteme bevorzugen meist Offline-Deduplizierung mit besserer Wirtschaftlichkeit
Wenn man sagt, dass Deduplizierung/Komprimierung bei Syslog gut funktioniert, dann sind Deduplizierung und Komprimierung im Detail betrachtet nicht dasselbe. In der Enterprise-Storage-Welt werden beide oft zusammen genannt, aber bei Logs profitiert man wahrscheinlich eher von Komprimierung als von Deduplizierung, und ZFS hatte Komprimierung ohnehin schon immer
Stattdessen klont man das Basis-Image; dann belegt es bis zu Änderungen kaum zusätzlichen Platz. Das ist eine Folge der Copy-on-Write-Eigenschaften von ZFS. ZFS-Deduplizierung ist eine Funktion, die versucht, bereits vorhandene Kopien von Daten zu finden, die in ein Volume geschrieben werden. Für manche Einsatzzwecke wie ein Container-Image-Repository kann das durchaus sinnvoll sein, aber wenn man ohnehin schon weiß, dass ein Datensatz von Anfang an ein Klon eines anderen ist, ist das sehr ineffizient
Früher habe ich ZFS-Deduplizierung sehr breit eingesetzt und große Vorteile daraus gezogen. Der konkrete Anwendungsfall war Storage für VMWare-Cluster, und es gab Hunderte von Linux- und Windows-VMs mit weitgehend gleichem Inhalt. Das war noch vor der Docker-Ära.
Ich freue mich sehr auf schnelle Deduplizierung. Seit Jahren wollte ich ZFS-Deduplizierung für ArchiveBox-Daten verwenden, und dank schneller Deduplizierung könnte es endlich möglich werden, Millionen von URLs in einer Sammlung zu archivieren und das Dateisystem die gesamte Kompression übernehmen zu lassen.
In Archivdaten tauchen Dinge wie
jquery.min.js,bootstrap.min.cssund Logo-Bilder in Tausenden von Snapshots immer wieder auf. Andere Tools komprimieren innerhalb eines einzelnen Crawls und erzeugen wacz- oder warc.gz-Dateien, aber soweit ich weiß hat bisher kein Tool versucht, über die gesamte Datenbank aller jemals aufgenommenen Snapshots hinweg zu komprimieren. Ich frage mich auch, ob schon jemand einen probabilistischen Deduplizierungs-Ansatz mit etwas wie einem Bloom-Filter ausprobiert hat, damit man nicht die gesamte Deduplizierungs-Hashtabelle unverändert speichern muss. Man könnte etwa jeweils rund 100 Block-Hashes in Buckets gruppieren und eine stark komprimierte Darstellung im Bloom-Filter speichern. Beim Schreiben würde man den Hash des zu schreibenden Blocks im Bloom-Filter nachschlagen, und wenn ein potenzieller Deduplizierungs-Treffer erkannt wird, die 100 Blöcke dieses Buckets direkt durchsuchen, um den identischen Hash zu finden. Theoretisch könnte man auch Bloom-Filter-Schichten mit unterschiedlicher Auflösung haben und bei hohem Speicherdruck Filter mit hoher Auflösung dynamisch auf die Platte auslagern. Wenn die Genauigkeit des Bloom-Filters ein einstellbarer Parameter wäre, könnte man Präferenzen zwischen CPU-Zeit/Overhead und eingesparten Bytes wählen.dm-vdo verhält sich genauso. Stattdessen wären vielleicht solid compression über längere Bereiche, das Entpacken von WARC-Dateien in eine strukturähnliche Verzeichnisform oder ein FUSE-System auf Basis von Content-Defined Chunking besser. Vielleicht macht Seafile so etwas.
Wenn man sich den WARC-Standard ansieht, gibt es dort bereits hashbasierte Deduplizierung mit Zeigern nach der ersten Speicherung. Das ist also geradezu ein Paradebeispiel dafür, wo Deduplizierung auf Dateisystemebene keine besonders gute Passung ist.
Ich frage mich, warum es so schwierig ist, das mit reduziertem RAM-Verbrauch richtig zum Laufen zu bringen. Kommerzielle Storage-Appliances haben das schon vor mindestens zehn Jahren geschafft, auch auf Systemen mit im Verhältnis zur angeschlossenen Plattenkapazität „wenig“ RAM.
Sollte es nicht reichen, die Fingerprints in einer Datenbank zu speichern und nachts alles durchzugehen und die Block-Pointer zu korrigieren?
Das ist seit Langem ein gewünschtes Feature, und wenn es möglich wäre, könnte man auch defragmentieren. Ich habe darüber nachgedacht, ob sich das mit einer Block-Pointer-Indirektion wie im virtuellen Speicher gegen einen kleinen Performance-Preis lösen ließe, aber ich bin kein ZFS-Entwickler und übersehe sicher etwas. http://eworldproblems.mbaynton.com/posts/2014/zfs-block-poin... / https://github.com/openzfs/zfs/issues/3582
Der große Nachteil ist, dass es kein Dateiübertragungsprotokoll mit RDMA gibt. Ich habe gehört, dass es auch einen experimentellen Branch gibt, um Hammer2 auf FreeBSD lauffähig zu machen. Aber FreeBSD unterstützt ebenfalls kein RDMA. In FreeBSD 15 hat Chelsio zwar Support für NVMe-oF-Targets und -Initiatoren gesponsert, aber das scheint nur TCP zu betreffen.
Verwende einfach
cp --reflink=auto.Damit erhält man Deduplizierung auf Dateiebene. Der Befehl macht eine Lightweight-Kopie, und wie bei ZFS-Klonen auf Dateiebene werden die Datenblöcke erst beim Ändern kopiert. Es ist keine Hardlink, sondern eine Kopie. Dasselbe sollte auch auf anderen Copy-on-Write-Transaktionsdateisystemen funktionieren, die Reflinks unterstützen.
Ich wollte ZFS wirklich verwenden, aber alle Daten sollten natürlich verschlüsselt sein. Dabei wurde die Nutzung viel komplizierter als erwartet, und erstaunlich viele Leute verschlüsseln ihre Daten dann einfach gar nicht, wenn es schwierig wird.
Sogar bei Proxmox, das auf der Website mit „Enterprise“ wirbt, hätte ich erwartet, dass Verschlüsselung in der Standardinstallation unterstützt wird, aber wenn man sie zusammen mit Verschlüsselung verwenden will, verliert man wichtige Funktionen. Man sollte sich unbedingt auch den Issue-Tracker ansehen. Es gibt dort einige erstaunliche Dinge, die ich in einem Produktiv-Dateisystem nicht erwartet hätte.
Ich wünschte, es gäbe als Dateisystem ein komplett anderes API. Die gesamte API-Oberfläche von Dateisystemen in allen Betriebssystemen ist wegen der Abwärtskompatibilität ein völlig festgefahrenes Chaos.
Ich habe versucht, den Vortrag dazu zu finden, aber ohne Erfolg. Ich dachte, ich hätte ihn auf dem Developer Summit gesehen, aber offenbar doch nicht.
Vergiss Deduplizierung und nutze lieber ZFS-Kompression — das ist deutlich wirtschaftlicher.
Im Allgemeinen deaktiviere ich selbst bei rsync-Jobs die Kompression für große Videodateien oft, weil sie für Speicherplatz oder Übertragung wenig bis gar nichts bringt, aber RAM und CPU verbraucht. Deduplizierung ist gut für Betriebssystem-Images von Virtual Machines, weil ein Großteil der Speicherkosten aus dem wiederholten Basis-Image besteht.
Allgemeine Deduplizierung wirkt in der Theorie gut, in der Praxis funktioniert sie jedoch oft nicht wie erhofft. IPFS verwendet Chunks variabler Größe und Rolling Hashes, um Daten ähnlich wie
rsynczu deduplizieren, erzielt damit aber in der Praxis keinen Unterschied und erhöht nur unnötig die Komplexität.