
Questa primavera abbiamo già discusso alcuni argomenti introduttivi, ad esempio, e . Nella seconda di esse abbiamo persino promesso di continuare a esplorare le prestazioni delle diverse topologie multi-disco in ZFS. Questo è un file system di nuova generazione che sta attualmente venendo implementato ovunque: da fino a .
Bene, oggi è il giorno perfetto per conoscere ZFS, cari lettori curiosi. Sappiate solo che, secondo la modesta stima dello sviluppatore di OpenZFS, Matt Arends, ‘è davvero complicato’.
Ma prima di arrivare ai numeri - e ci saranno, promesso - su tutte le varianti della configurazione a otto dischi di ZFS, dobbiamo parlare di come come ZFS memorizza i dati su disco.
Zpool, vdev e device

Questo diagramma del pool completo include tre vdev ausiliari, uno per ciascuna classe, e quattro per RAIDz2

Di solito non ci sono motivi per creare un pool di vdev di tipi e dimensioni non corrispondenti - ma se lo desiderate, nulla vi impedisce di farlo.
Per veramente comprendere il file system ZFS, è necessario dare un'occhiata attenta alla sua struttura effettiva. In primo luogo, ZFS combina i tradizionali livelli di gestione dei volumi e del file system. In secondo luogo, utilizza un meccanismo di transazione di copia all'atto della scrittura. Queste caratteristiche significano che il sistema è strutturalmente molto diverso dai normali file system e dagli array RAID. Il primo set di elementi costitutivi fondamentali per la comprensione: è il pool di archiviazione (zpool), il dispositivo virtuale (vdev) e il dispositivo reale (device).
zpool
Il pool di archiviazione zpool è la struttura superiore di ZFS. Ogni pool contiene uno o più dispositivi virtuali. A sua volta, ognuno di essi contiene uno o più dispositivi reali (device). I pool virtuali sono blocchi autonomi. Un singolo computer fisico può contenere due o più pool separati, ma ognuno è completamente indipendente dagli altri. I pool non possono condividere dispositivi virtuali.
La ridondanza di ZFS si trova a livello dei dispositivi virtuali, non a livello dei pool. A livello dei pool non c'è assolutamente alcuna ridondanza - se un disco vdev o un vdev speciale va perso, si perde anche l'intero pool.
Le moderne pool di archiviazione possono sopportare la perdita della cache o del diario del dispositivo virtuale, sebbene possano perdere una piccola quantità di dati sporchi se perdi il diario vdev durante un'interruzione di corrente o un guasto di sistema.
C'è un'eccessiva convinzione che le 'strisce di dati' (striping) in ZFS vengano scritte su tutto il pool. Questo è errato. Zpool non è affatto un divertente RAID0, è piuttosto un divertente con un meccanismo di distribuzione variabile complesso.
Per la maggior parte, le scritture vengono distribuite tra i dispositivi virtuali disponibili in base allo spazio libero disponibile, in modo che, teoricamente, tutti vengano riempiti contemporaneamente. Nelle versioni più recenti di ZFS, viene considerato l'uso corrente (utilizzo) del vdev: se un dispositivo virtuale è significativamente più carico di un altro (ad esempio, a causa di un carico di lettura), verrà temporaneamente saltato per la scrittura, nonostante ci sia spazio libero disponibile.
Il meccanismo di determinazione dell'utilizzo, integrato nei moderni metodi di distribuzione della scrittura di ZFS, può ridurre la latenza e aumentare la larghezza di banda in periodi di carico di lavoro insolitamente elevato, ma questo non è un lasciapassare per una mescolanza involontaria di HDD lenti e SSD veloci in un unico pool. Un pool impari funzionerà comunque alla velocità del dispositivo più lento, come se fosse composto esclusivamente da tali dispositivi.
vdev
Ogni pool di archiviazione è composto da uno o più dispositivi virtuali (virtual device, vdev). A sua volta, ogni vdev include uno o più dispositivi fisici. La maggior parte dei dispositivi virtuali viene utilizzata per la semplice archiviazione dei dati, ma ci sono diverse classi ausiliarie di vdev, tra cui CACHE, LOG e SPECIAL. Ognuno di questi tipi di vdev può avere una delle cinque topologie: dispositivo singolo (single-device), RAIDz1, RAIDz2, RAIDz3 o mirror.
RAIDz1, RAIDz2 e RAIDz3 sono speciali varietà di ciò che i veterani chiamerebbero RAID a doppia (diagonale) parità. 1, 2 e 3 si riferiscono a quanti blocchi di parità sono riservati per ciascuna stripe di dati. Invece di dischi separati per garantire la parità, i dispositivi virtuali RAIDz distribuiscono uniformemente questa parità tra i dischi. Un array RAIDz può perdere tanti dischi quanti sono i suoi blocchi di parità; se ne perde un altro, smetterà di funzionare e porterà con sé il pool di archiviazione.
Nei dispositivi virtuali a specchio (mirror vdev), ogni blocco è memorizzato su ciascun dispositivo nel vdev. Sebbene i doppi specchi (two-wide) siano i più comuni, nello specchio può esserci qualsiasi numero arbitrario di dispositivi; nelle installazioni più grandi, per migliorare le prestazioni di lettura e la tolleranza ai guasti, spesso si utilizzano i tripli. Uno specchio vdev può sopravvivere a qualsiasi guasto, purché almeno un dispositivo nel vdev continui a funzionare.
I vdev singoli sono intrinsecamente pericolosi. Un tale dispositivo virtuale non sopravviverà a nessun guasto; e se viene utilizzato come storage o vdev speciale, il suo guasto porterà alla distruzione dell'intero pool. Fai attenzione qui, molto, molto attenzione.
I dispositivi virtuali CACHE, LOG e SPECIAL possono essere creati secondo qualsiasi delle topologie sopra menzionate — ma ricorda che la perdita di un dispositivo virtuale SPECIAL significa la perdita del pool, quindi si raccomanda vivamente una topologia ridondante.
dispositivo
Probabilmente è il termine più semplice da capire in ZFS — è letteralmente un dispositivo a blocchi di accesso casuale. Ricorda che i dispositivi virtuali sono composti da dispositivi separati, e il pool è fatto di dispositivi virtuali.
I dischi — magnetici o a stato solido — sono i dispositivi a blocchi più comuni utilizzati come mattoni per il vdev. Tuttavia, qualsiasi dispositivo con un descrittore in /dev è adatto — quindi interi array RAID hardware possono essere utilizzati come dispositivi separati.
Un semplice file raw è uno dei più importanti dispositivi a blocchi alternativi da cui può essere costruito un vdev. I pool di test da sono un modo molto utile per verificare i comandi del pool e vedere quanta memoria è disponibile nel pool o nel dispositivo virtuale di questa topologia.

Puoi creare un pool di test da file sparsi in pochi secondi, ma non dimenticare di rimuovere successivamente l'intero pool e i suoi componenti.
Supponiamo che tu voglia configurare un server con otto dischi e stia pianificando di utilizzare dischi da 10 TB (~9300 GiB) - ma non sei sicuro su quale topologia si adatti meglio alle tue esigenze. Nell'esempio sopra, costruiamo in pochi secondi un pool di test da file sparsi e ora sappiamo che un vdev RAIDz2 composto da otto dischi da 10 TB offre 50 TiB di capacità utile.
Un'altra classe speciale di dispositivi è SPARE (riserva). I dispositivi hot-swappable, a differenza dei normali dispositivi, appartengono a tutto il pool, non a un singolo dispositivo virtuale. Se un vdev nel pool fallisce e un dispositivo di riserva è connesso al pool e disponibile, si unirà automaticamente al vdev danneggiato.
Dopo che il dispositivo di riserva si è collegato al vdev danneggiato, inizia a ricevere copie o ricostruzioni dei dati che dovrebbero essere sul dispositivo mancante. Nel RAID tradizionale, questo è chiamato recupero (rebuilding), mentre in ZFS è 'ripristino della ridondanza' (resilvering).
È importante notare che i dispositivi di riserva non sostituiscono permanentemente i dispositivi guasti. Sono solo una sostituzione temporanea per ridurre il tempo durante il quale si verifica la degradazione del vdev. Dopo che l'amministratore sostituisce il dispositivo guasto nel vdev, la ridondanza viene ripristinata su questo dispositivo permanente, e il SPARE si disconnette dal vdev e torna a funzionare come riserva per l'intero pool.
Set di dati, blocchi e settori
Il prossimo set di mattoni da capire nel nostro viaggio attraverso ZFS riguarda non tanto l'hardware, quanto il modo in cui i dati stessi sono organizzati e memorizzati. Qui saltando alcuni livelli — come il metaslab — per non sovraccaricare i dettagli, mantenendo la comprensione della struttura generale.
Set di dati (dataset)

Quando creiamo per la prima volta un set di dati, mostra tutto lo spazio disponibile nel pool. Poi imposteremo una quota e cambieremo il punto di montaggio. Magia!

Zvol è per lo più semplicemente un set di dati privo del proprio strato di file system, che qui sostituiamo con un file system ext4 del tutto normale.
Il dataset ZFS è sostanzialmente simile a un file system montato standard. Proprio come un normale file system, a prima vista sembra "solo un'altra cartella". Ma, come con i normali file system montati, ogni dataset ZFS ha un insieme proprio di proprietà fondamentali.
Prima di tutto, un dataset può avere una quota assegnata. Se si imposta zfs set quota=100G poolname/datasetname, non sarà possibile scrivere nella cartella montata /poolname/datasetname più di 100 GiB.
Hai notato la presenza – e l'assenza – di slash all'inizio di ogni riga? Ogni dataset ha il proprio posto sia nell'gerarchia ZFS che nell'gerarchia di montaggio del sistema. Nell'gerarchia ZFS non c'è uno slash iniziale: si inizia con il nome del pool e poi il percorso da un dataset all'altro. Ad esempio, pool/parent/child per un dataset con il nome child sotto il dataset genitore parent in un pool con un nome creativo pool.
Per impostazione predefinita, il punto di montaggio del dataset sarà equivalente al suo nome nell'gerarchia ZFS, con uno slash all'inizio: il pool con nome pool viene montato come /pool, il dataset parent viene montato in /pool/parent, e il dataset figlio child viene montato in /pool/parent/child. Tuttavia, il punto di montaggio del dataset del sistema può essere modificato.
Se specifichiamo zfs set mountpoint=/lol pool/parent/child, il dataset pool/parent/child sarà montato nel sistema come /lol.
In aggiunta ai dataset, dobbiamo menzionare i volumi (zvols). Un volume è sostanzialmente simile a un dataset, eccetto che in esso non c'è effettivamente un file system – è semplicemente un dispositivo a blocchi. Puoi, ad esempio, creare zvol con il nome mypool/myzvol, quindi formattarlo con il file system ext4, e poi montare questo file system – ora hai un file system ext4, ma con il supporto di tutte le funzionalità di sicurezza ZFS! Può sembrare strano su un singolo computer, ma ha molto più senso come backend quando si esporta un dispositivo iSCSI.
Blocchi

Un file è rappresentato da uno o più blocchi. Ogni blocco è memorizzato su un singolo dispositivo virtuale. La dimensione del blocco è generalmente uguale al parametro recordsize, ma può essere ridotta a 2^ashift, se contiene metadati o un file di piccole dimensioni.

Non stiamo davvero scherzando riguardo al danno enorme alle prestazioni se si imposta un ashift troppo piccolo. in effetti non stiamo scherzando riguardo ai danni enormi alle prestazioni se imposti un ashift troppo piccolo.
Nel pool ZFS, tutti i dati, comprese le metainformazioni, sono memorizzati in blocchi. La dimensione massima del blocco per ciascun dataset è definita nella proprietà recordsize (dimensione del record). La dimensione del record può variare, ma questo non influenzerà la dimensione o la posizione di qualsiasi blocco che è già stato scritto nel dataset — vale solo per i nuovi blocchi man mano che vengono scritti.
Se non diversamente specificato, la dimensione del record attuale per impostazione predefinita è di 128 KiB. È una sorta di compromesso non banale, dove le prestazioni non saranno ideali, ma nemmeno disastrose nella maggior parte dei casi. Recordsize può essere impostato su qualsiasi valore da 4K a 1M (con ulteriori configurazioni recordsize può essere impostato ancora più in alto, ma di solito non è una buona idea).
Ogni blocco fa riferimento solo ai dati di un singolo file — non è possibile inserire due file diversi in un unico blocco. Ogni file è composto da uno o più blocchi, a seconda della dimensione. Se la dimensione del file è inferiore alla dimensione del record, verrà memorizzato in un blocco di dimensioni inferiori — per esempio, un blocco con un file di 2 KiB occuperà solo un settore di 4 KiB sul disco.
Se il file è sufficientemente grande e richiede più blocchi, tutte le registrazioni con quel file avranno una dimensione recordsize — inclusa l'ultima registrazione, la cui parte principale potrebbe risultare .
I volumi zvol non hanno la proprietà recordsize — invece hanno una proprietà equivalente volblocksize.
Settori
L'ultimo e più basilare mattoncino è il settore. Questa è l'unità fisica più piccola che può essere scritta o letta dal dispositivo sottostante. Per decenni, la maggior parte dei dischi ha utilizzato settori da 512 byte. Ultimamente, la maggior parte dei dischi è configurata per settori da 4 KiB, e in alcuni — specialmente SSD — settori da 8 KiB o anche di più.
Nel sistema ZFS esiste una proprietà che consente di impostare manualmente la dimensione del settore. Questa proprietà ashift. È un po' confuso, poiché ashift è una potenza di due. Ad esempio, ashift=9 significa una dimensione del settore di 2^9, ovvero 512 byte.
ZFS richiede al sistema operativo informazioni dettagliate su ogni dispositivo di archiviazione a blocchi quando viene aggiunto a un nuovo vdev, e teoricamente imposta automaticamente ashift correttamente sulla base di queste informazioni. Sfortunatamente, molti dischi mentono sulle loro dimensioni di settore, per mantenere la compatibilità con Windows XP (che non è in grado di comprendere dischi con dimensioni di settore diverse).
Questo significa che l'amministratore ZFS è fortemente incoraggiato a conoscere la reale dimensione del settore delle proprie unità e a impostarla manualmente ashift. Se ashift è impostato troppo piccolo, il numero di operazioni di lettura/scrittura aumenta astronomicamente. Scrivere "settori" da 512 byte in un vero settore da 4 KiB implica la necessità di scrivere prima il "settore" poi leggere il settore da 4 KiB, modificarlo con il secondo "settore" da 512 byte, riscriverlo nel nuovo settore da 4 KiB e così via per ogni scrittura.
Nel mondo reale, questa penalizzazione colpisce gravemente i dischi a stato solido Samsung EVO, per i quali deve essere applicato ashift=13, ma questi SSD mentono sulla loro dimensione di settore, e quindi per impostazione predefinita viene fissato ashift=9. Se un amministratore di sistema esperto non modifica questa impostazione, questo SSD funziona più lentamente come un normale HDD magnetico.
A confronto, per dimensioni troppo grandi ashift non c’è praticamente alcuna penalizzazione. Non c'è una reale riduzione delle prestazioni, e l'aumento dello spazio inutilizzato è infinitesimale (o nullo con la compressione attivata). Pertanto, raccomandiamo vivamente anche a quei dischi che utilizzano effettivamente settori da 512 byte di impostare ashift=12 o addirittura ashift=13, per guardare al futuro con fiducia.
La proprietà ashift viene impostato per ogni dispositivo virtuale vdev, e non per il pool, come molti erroneamente pensano - e non può essere cambiato dopo l'impostazione. Se accidentalmente si sbaglia ashift quando si aggiunge un nuovo vdev al pool, si è contaminato irreparabilmente questo pool con un dispositivo a bassa prestazione e, in genere, non c'è altra soluzione se non quella di distruggere il pool e ricominciare da capo. Anche rimuovere il vdev non salverà da un'impostazione errata. ashift!
Il meccanismo di copia su scrittura

Se un normale file system deve riscrivere i dati, cambia ogni blocco lì dove si trova.

Il file system con copia alla scrittura salva una nuova versione del blocco e poi sblocca la vecchia versione.

In termini astratti, ignorando la reale posizione fisica dei blocchi, la nostra «cometa dei dati» si semplifica in un «verme dei dati» che si muove da sinistra a destra sulla mappa dello spazio disponibile.

Ora possiamo avere una buona comprensione di come funzionano gli snapshot di copia alla scrittura: ogni blocco può appartenere a più snapshot e resterà fino a quando non verranno distrutti tutti gli snapshot associati.
Il meccanismo di copia alla scrittura (Copy on Write, CoW) è la base fondamentale di ciò che rende ZFS un sistema così straordinario. Il concetto di base è semplice: se chiedi a un file system tradizionale di modificare un file, farà esattamente ciò che hai richiesto. Se chiedi a un file system con copia alla scrittura di fare lo stesso, dirà «va bene» — ma ti mentirà.
Invece, il file system con copia alla scrittura salva una nuova versione del blocco modificato e poi aggiorna i metadati del file per interrompere il legame con il vecchio blocco e collegare a esso il nuovo blocco che hai appena scritto.
La disconnessione del vecchio blocco e il collegamento del nuovo avviene in un'unica operazione, quindi non può essere interrotta: se spegni l'alimentazione dopo che ciò è avvenuto, hai una nuova versione del file, e se spegni l'alimentazione prima, hai la vecchia versione. In entrambi i casi, non ci saranno conflitti nel file system.
La copia alla scrittura in ZFS avviene non solo a livello di file system, ma anche a livello di gestione dei dischi. Questo significa che ZFS non è soggetto a un buco nella scrittura () — un fenomeno in cui il numero di strisce è stato scritto solo parzialmente prima di un guasto di sistema, con danni all'array dopo il riavvio. Qui la striscia è scritta in modo atomico, vdev è sempre coerente, e .
ZIL: diario delle intenzioni ZFS

Il sistema ZFS gestisce le scritture sincrone in modo particolare: le salva temporaneamente, ma immediatamente, nello ZIL, prima di scriverle successivamente in modo permanente insieme alle scritture asincrone.

Di solito, i dati scritti nello ZIL non vengono mai letti di nuovo. Ma ciò è possibile dopo un guasto di sistema.

SLOG, o dispositivo LOG secondario, è semplicemente un vdev speciale – e, preferibilmente, molto veloce – dove ZIL può essere memorizzato separatamente dallo storage principale

Dopo un guasto, tutti i dati sporchi nel ZIL vengono ripristinati – in questo caso il ZIL si trova su SLOG, quindi vengono ripristinati proprio da lì
Ci sono due categorie principali di operazioni di scrittura – sincroniche (sync) e asincroniche (async). Per la maggior parte dei carichi di lavoro, la stragrande maggioranza delle operazioni di scrittura è asincronica – il file system consente di aggregarle e restituirle in pacchetti, riducendo la frammentazione e aumentando notevolmente la larghezza di banda.
Le scritture sincroniche sono un'altra cosa. Quando un'applicazione richiede una scrittura sincronica, sta dicendo al file system: «Devi confermarla nella memoria non volatile questo preciso momento, e fino ad allora non posso fare nient'altro». Pertanto, le scritture sincroniche devono essere immediatamente confermate su disco – e se ciò aumenta la frammentazione o riduce la larghezza di banda, così sia.
ZFS gestisce le scritture sincroniche in modo diverso rispetto ai normali file system – invece di riversarle immediatamente nello storage normale, ZFS le conferma in un'area di storage speciale chiamata journal delle intenzioni ZFS – ZFS Intent Log, o ZIL. La chiave è che queste scritture anche rimangono in memoria, aggregate insieme alle normali richieste di scrittura asincroniche, per essere successivamente scaricate nello storage come normali TXG (gruppi di transazione, Transaction Groups).
In condizioni di funzionamento normale, il ZIL viene scritto e mai più letto. Quando, dopo qualche istante, le scritture dal ZIL vengono confermate nello storage principale nei normali TXG dalla memoria, si scollegano dal ZIL. L'unico momento in cui qualcosa viene letto dal ZIL è durante l'importazione del pool.
Se si verifica un guasto di ZFS – guasto del sistema operativo o interruzione di corrente – quando ci sono dati nel ZIL, questi dati verranno letti durante il successivo import del pool (ad esempio, al riavvio del sistema di emergenza). Tutto ciò che si trova nel ZIL sarà letto, aggregato in gruppi TXG, confermato nello storage principale e poi scollegato dal ZIL durante il processo di importazione.
Una delle classi ausiliarie vdev si chiama LOG o SLOG, dispositivo secondario LOG. Ha un compito: fornire un pool con un dispositivo vdev separato e, preferibilmente, molto più veloce, con un'alta resilienza alla scrittura per memorizzare ZIL, invece di memorizzare ZIL nel principale archivio vdev. Lo stesso ZIL si comporta allo stesso modo indipendentemente dalla posizione di archiviazione, ma se un vdev con LOG ha una prestazione di scrittura molto alta, le scritture sincrone avranno luogo più rapidamente.
L'aggiunta di un vdev con LOG nel pool in alcun modo non può migliorare le prestazioni della scrittura asincrona - anche se forzi tutte le scritture in ZIL tramite zfs set sync=always, saranno comunque legate all'archiviazione principale in TXG allo stesso modo e alla stessa velocità di prima della registrazione. L'unico miglioramento diretto delle prestazioni è il ritardo nella scrittura sincrona (poiché una maggiore velocità del log accelera l'esecuzione delle operazioni sync).
Tuttavia, in un ambiente che richiede già un gran numero di scritture sincrone, il vdev LOG può indirettamente accelerare la scrittura asincrona e la lettura non memorizzata nella cache. Scaricare le scritture ZIL in un vdev LOG separato significa meno concorrenza per gli IOPS nello storage primario, il che migliora in qualche modo le prestazioni di tutte le operazioni di lettura e scrittura.
Snapshot
Il meccanismo di copia-on-write è anche una base necessaria per gli istantanei atomici di ZFS e la replica asincrona incrementale. In un file system attivo esiste un albero di puntatori che segna tutte le scritture con i dati correnti: quando si crea uno snapshot, si fa semplicemente una copia di questo albero di puntatori.
Quando in un file system attivo viene sovrascritta una scrittura, ZFS prima scrive una nuova versione del blocco in uno spazio inutilizzato. Poi scollega la vecchia versione del blocco dal file system corrente. Ma se uno snapshot fa riferimento al vecchio blocco, rimane comunque immutato. Il vecchio blocco non sarà effettivamente recuperato come spazio libero finché tutti gli snapshot che fanno riferimento a quel blocco non saranno eliminati!
Replica

La mia libreria Steam nel 2015 occupava 158 GiB e conteneva 126.927 file. È abbastanza vicino alla situazione ottimale per rsync: la replica di ZFS in rete era "solo" il 750% più veloce.

Nella stessa rete, la replica di un file immagine di una macchina virtuale Windows 7 di 40 gigabyte è una storia completamente diversa. La replica ZFS avviene 289 volte più velocemente di rsync - o "solo" 161 volte più veloce se sei abbastanza esperto da chiamare rsync con l'opzione --inplace.

Quando l'immagine della macchina virtuale viene scalata, i problemi di rsync si scalano insieme ad essa. Una dimensione di 1,9 TiB non è così grande per un'immagine virtuale moderna - ma è abbastanza grande affinché la replica ZFS sia risultata 1148 volte più veloce di rsync, anche con l'argomento rsync --inplace.
Una volta che capisci come funzionano gli snapshot, sarà facile afferrare il concetto di replica. Poiché uno snapshot è semplicemente un albero di puntatori a registrazioni, ne consegue che se facciamo zfs send di uno snapshot, stiamo inviando sia questo albero che tutte le registrazioni ad esso correlate. Quando trasmettiamo questo zfs send in zfs receive all'oggetto di destinazione, scrive sia il reale contenuto del blocco sia l'albero di puntatori che punta ai blocchi, nel set di dati di destinazione.
Le cose diventano ancora più interessanti con il secondo zfs send. Ora abbiamo due sistemi, ciascuno dei quali contiene poolname/datasetname@1, e tu scatti un nuovo snapshot poolname/datasetname@2. Pertanto, nel pool sorgente hai datasetname@1 e datasetname@2, mentre nel pool di destinazione hai solo il primo snapshot. datasetname@1.
Poiché tra sorgente e destinazione abbiamo uno snapshot comune datasetname@1, possiamo fare incrementale zfs send su di esso. Quando diciamo al sistema zfs send -i poolname/datasetname@1 poolname/datasetname@2, confronta i due alberi di puntatori. Qualsiasi puntatore che esista solo in @2, evidentemente, punta a nuovi blocchi - quindi avremo bisogno del contenuto di questi blocchi.
Nel sistema remoto, l'elaborazione incrementale send è altrettanto semplice. Prima registriamo tutte le nuove registrazioni incluse nel flusso send, e poi aggiungiamo i puntatori a questi blocchi. Voilà, abbiamo @2 nel nuovo sistema!
La replica incrementale asincrona ZFS è un enorme miglioramento rispetto ai metodi precedenti non basati su snapshot, come rsync. In entrambi i casi vengono trasferiti solo i dati modificati - ma rsync deve prima lettura leggere i dati da entrambe le parti per verificare il checksum e confrontarlo. Al contrario, la replica ZFS non legge nulla tranne gli alberi di puntatori - e qualsiasi blocco che non è presente nello snapshot comune.
Compressione integrata
Il meccanismo di copia durante la scrittura semplifica anche il sistema di compressione integrata. Nelle tradizionali sistemi di file, la compressione è problematica: sia la vecchia versione che la nuova versione dei dati modificati si trovano nello stesso spazio.
Se consideriamo un frammento di dati nel mezzo di un file, che inizia la sua vita come un megabyte di zeri da 0x00000000 e così via, è molto facile comprimerlo in un solo settore su disco. Ma cosa succede se sostituiamo questo megabyte di zeri con un megabyte di dati non comprimibili, come JPEG o rumore pseudo-casuale? Improvvisamente, a questo megabyte di dati servirebbero non uno, ma 256 settori da 4 KiB, mentre in questa parte del disco è riservato solo un settore.
ZFS non ha questo problema, poiché le registrazioni modificate vengono sempre scritte in spazio inutilizzato: il blocco originale occupa solo un settore da 4 KiB, mentre la nuova registrazione occuperà 256, ma questo non è un problema: il frammento modificato più recentemente "dal mezzo" del file sarebbe scritto nello spazio inutilizzato indipendentemente dal fatto che la sua dimensione sia cambiata o meno, quindi per ZFS è una situazione del tutto normale.
La compressione integrata di ZFS è disabilitata per impostazione predefinita e il sistema offre algoritmi plug-in: attualmente ci sono LZ4, gzip (1-9), LZJB e ZLE.
- LZ4 — è un algoritmo a flusso che offre una compressione e decompressione estremamente veloci e vantaggi prestazionali per la maggior parte dei casi d'uso, anche su CPU piuttosto lente.
- GZIP — è un algoritmo rispettabile, conosciuto e amato da tutti gli utenti dei sistemi Unix. Può essere implementato con livelli di compressione da 1 a 9, con un aumento del grado di compressione e utilizzo della CPU man mano che ci si avvicina al livello 9. L'algoritmo è ben adattato per tutti gli usi testuali (o altri casi estremamente comprimibili), ma altrimenti spesso può causare problemi con la CPU: usalo con cautela, soprattutto a livelli più elevati.
- LZJB — è l'algoritmo originale in ZFS. È obsoleto e non dovrebbe più essere utilizzato, LZ4 lo supera in tutti i parametri.
- ZLE — codifica di livello zero, Zero Level Encoding. Essa non interferisce con i dati normali, ma comprime grandi sequenze di zeri. Utile per set di dati completamente non comprimibili (ad esempio, JPEG, MP4 o altri formati già compressi), poiché ignora i dati non comprimibili, ma comprime lo spazio non utilizzato nei record finali.
Consigliamo la compressione LZ4 praticamente per tutte le applicazioni; la penalizzazione delle prestazioni quando si incontrano dati non comprimibili è molto bassa, e incremento le prestazioni per i dati tipici sono significative. La copia di un'immagine di macchina virtuale per una nuova installazione del sistema operativo Windows (OS appena installata, senza dati all'interno) con compression=lz4 è stata eseguita con il 27% di velocità in più rispetto a compression=none, in .
ARC — cache di sostituzione adattiva
ZFS è l'unico file system moderno di cui siamo a conoscenza che utilizza un proprio meccanismo di caching per la lettura, invece di fare affidamento sulla cache delle pagine del sistema operativo per memorizzare copie dei blocchi letti di recente nella memoria.
Sebbene la cache proprietaria non sia priva di problemi — ZFS non può rispondere a nuove richieste di allocazione di memoria con la stessa velocità del kernel, quindi una nuova chiamata malloc() per l'allocazione di memoria può fallire se ha bisogno di memoria RAM attualmente occupata da ARC. Ma ci sono ottime ragioni per utilizzare una cache proprietaria, almeno per ora.
Tutti i sistemi operativi moderni noti, inclusi MacOS, Windows, Linux e BSD, utilizzano l'algoritmo LRU (Least Recently Used) per implementare la cache delle pagine. Questo è un algoritmo primitivo, che promuove il blocco cache "in cima alla coda" dopo ogni lettura e espelle i blocchi "in fondo alla coda" secondo necessità, per aggiungere nuovi cache miss (blocchi che dovevano essere letti dal disco, e non dalla cache) in alto.
In genere, l'algoritmo funziona correttamente, ma nei sistemi con ampi set di dati, l'LRU porta facilmente al thrashing — espellendo blocchi spesso necessari per liberare spazio per blocchi che non saranno mai più letti dalla cache.
— un algoritmo molto meno naïf, che può essere considerato come una cache «ponderata». Dopo ogni lettura di un blocco memorizzato nella cache, esso diventa un po' più «pesante» e diventa più difficile da espellere — e anche dopo l'espulsione, il blocco è monitorato per un determinato periodo di tempo. Un blocco che è stato espulso ma deve essere letto nuovamente nella cache diventerà anch'esso «più pesante».
Il risultato finale di tutto ciò è una cache con un rapporto di hit (hit ratio) molto più elevato — ovvero il rapporto tra i hit nella cache (lettura eseguita dalla cache) e i miss (lettura dal disco). Questa è una statistica estremamente importante — i hit nella cache non solo vengono gestiti considerevolmente più velocemente, ma anche i miss della cache possono essere gestiti più rapidamente, perché più hit nella cache significano meno richieste parallele al disco e meno latenza per i miss rimanenti, che devono essere gestiti dal disco.
Conclusione
Dopo aver studiato la semantica fondamentale di ZFS — come funziona la scrittura, nonché le relazioni tra i pool di storage, i dispositivi virtuali, i blocchi, i settori e i file — siamo pronti a discutere delle reali prestazioni con numeri concreti.
Nella parte successiva, esamineremo le prestazioni effettive dei pool con vdev speculari e RAIDz, uno in confronto all'altro, e anche rispetto alle tradizionali topologie RAID del kernel Linux che abbiamo esplorato. .
Inizialmente volevamo esaminare solo le basi — le stesse topologie di ZFS — ma dopo tanto saremo pronti a parlare di configurazioni avanzate e ottimizzazione di ZFS, incluso l'uso di tipi ausiliari di vdev, come L2ARC, SLOG e Special Allocation.
Fonte: habr.com
