Limita flush dello sfondo di Linux (pagine sporche)


26

Lo svuotamento in background su Linux si verifica quando sono in sospeso troppi dati scritti (regolabile tramite / proc / sys / vm / dirty_background_ratio) o viene raggiunto un timeout per le scritture in sospeso (/ proc / sys / vm / dirty_expire_centisecs). A meno che non venga raggiunto un altro limite (/ proc / sys / vm / dirty_ratio), è possibile che vengano memorizzati nella cache più dati scritti. Ulteriori scritture bloccheranno.

In teoria, questo dovrebbe creare un processo in background che scrive pagine sporche senza disturbare altri processi. In pratica, disturba qualsiasi processo che esegue la lettura non memorizzata o la scrittura sincrona. Male. Questo perché il flush in background scrive effettivamente al 100% della velocità del dispositivo e qualsiasi altra richiesta del dispositivo in questo momento verrà ritardata (poiché tutte le code e le cache di scrittura sulla strada sono riempite).

Esiste un modo per limitare la quantità di richieste al secondo eseguite dal processo di lavaggio o stabilire una priorità efficace per gli altri I / O dei dispositivi?


Forse questa sarebbe una buona domanda da inviare alla mailing list del kernel linux vger.kernel.org/vger-lists.html#linux-kernel

Quale scheduler IO stai usando?
3dinfluence

Ho provato vari (cfq, scadenza), ma immagino che funzionino in modo affidabile solo quando non è inclusa alcuna cache di scrittura supportata da batteria. Come un array di dischi, ho 1 Gb di dati alla velocità del bus PCIe (RAM) e poi colpisce il reality-wall. I / O zero per alcuni secondi per tutti i LUN. La limitazione delle vampate di calore (almeno quelle di sfondo) a una stima approssimativa della velocità effettiva del dispositivo risolverebbe questo problema di congestione.
Korkman,

1
Di recente sono venuto a conoscenza del fatto che / sys / block / sdX / queue / nr_requests è un parametro sintonizzabile. Abbassandolo al minimo (= 4 nel mio caso) migliora molto la latenza del carico simultaneo: Sysbench fsync scritture casuali al secondo passano da 4 (!) A 80-90 mentre scrivono alla velocità del bus con dd. Le prestazioni non caricate sembrano inalterate. Gli scheduler sono tutti uguali, noop o scadenza sembrano ottimali. Questo può essere vero per la maggior parte delle configurazioni BBWC.
Korkman,

Risposte:


20

Dopo molti benchmarking con sysbench, arrivo a questa conclusione:

Sopravvivere (per quanto riguarda le prestazioni) una situazione in cui

  • un cattivo processo di copia inonda le pagine sporche
  • e la cache di scrittura hardware è presente (possibilmente anche senza quella)
  • e letture o scritture sincrone al secondo (IOPS) sono fondamentali

basta scaricare tutti gli ascensori, le code e le cache delle pagine sporche. Il posto corretto per le pagine sporche è nella RAM di quella cache di scrittura hardware.

Regola dirty_ratio (o nuovi dirty_bytes) il più basso possibile, ma tieni d'occhio il throughput sequenziale. Nel mio caso particolare, 15 MB erano ottimali ( echo 15000000 > dirty_bytes).

Questo è più un trucco che una soluzione perché gigabyte di RAM vengono ora utilizzati solo per la cache di lettura anziché per la cache sporca. Affinché la cache sporca funzioni bene in questa situazione, il flusher in background del kernel Linux dovrebbe fare una media alla velocità con cui il dispositivo sottostante accetta le richieste e regolare di conseguenza il flushing in background. Non facile.


Specifiche e parametri di riferimento per il confronto:

Testato durante ddl'installazione di zeri su disco, sysbench ha mostrato un enorme successo , aumentando 10 thread scritti da fsync a 16 kB da 33 a 700 IOPS (limite di inattività: 1500 IOPS) e thread singolo da 8 a 400 IOPS.

Senza carico, gli IOPS non sono stati influenzati (~ 1500) e la velocità effettiva è stata leggermente ridotta (da 251 MB / sa 216 MB / s).

dd chiamata:

dd if=/dev/zero of=dumpfile bs=1024 count=20485672

per sysbench, test_file.0 è stato preparato per essere privo di analisi con:

dd if=/dev/zero of=test_file.0 bs=1024 count=10485672

chiamata sysbench per 10 thread:

sysbench --test=fileio --file-num=1 --num-threads=10 --file-total-size=10G --file-fsync-all=on --file-test-mode=rndwr --max-time=30 --file-block-size=16384 --max-requests=0 run

chiamata sysbench per un thread:

sysbench --test=fileio --file-num=1 --num-threads=1 --file-total-size=10G --file-fsync-all=on --file-test-mode=rndwr --max-time=30 --file-block-size=16384 --max-requests=0 run

I blocchi più piccoli hanno mostrato numeri ancora più drastici.

--file-block-size = 4096 con 1 GB dirty_bytes:

sysbench 0.4.12:  multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 1

Extra file open flags: 0
1 files, 10Gb each
10Gb total file size
Block size 4Kb
Number of random requests for random IO: 0
Read/Write ratio for combined random IO test: 1.50
Calling fsync() after each write operation.
Using synchronous I/O mode
Doing random write test
Threads started!
Time limit exceeded, exiting...
Done.

Operations performed:  0 Read, 30 Write, 30 Other = 60 Total
Read 0b  Written 120Kb  Total transferred 120Kb  (3.939Kb/sec)
      0.98 Requests/sec executed

Test execution summary:
      total time:                          30.4642s
      total number of events:              30
      total time taken by event execution: 30.4639
      per-request statistics:
           min:                                 94.36ms
           avg:                               1015.46ms
           max:                               1591.95ms
           approx.  95 percentile:            1591.30ms

Threads fairness:
      events (avg/stddev):           30.0000/0.00
      execution time (avg/stddev):   30.4639/0.00

--file-block-size = 4096 con 15 MB dirty_bytes:

sysbench 0.4.12:  multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 1

Extra file open flags: 0
1 files, 10Gb each
10Gb total file size
Block size 4Kb
Number of random requests for random IO: 0
Read/Write ratio for combined random IO test: 1.50
Calling fsync() after each write operation.
Using synchronous I/O mode
Doing random write test
Threads started!
Time limit exceeded, exiting...
Done.

Operations performed:  0 Read, 13524 Write, 13524 Other = 27048 Total
Read 0b  Written 52.828Mb  Total transferred 52.828Mb  (1.7608Mb/sec)
    450.75 Requests/sec executed

Test execution summary:
      total time:                          30.0032s
      total number of events:              13524
      total time taken by event execution: 29.9921
      per-request statistics:
           min:                                  0.10ms
           avg:                                  2.22ms
           max:                                145.75ms
           approx.  95 percentile:              12.35ms

Threads fairness:
      events (avg/stddev):           13524.0000/0.00
      execution time (avg/stddev):   29.9921/0.00

--file-block-size = 4096 con 15 MB dirty_bytes su sistema inattivo:

sysbench 0.4.12: benchmark di valutazione del sistema multi-thread

Running the test with following options:
Number of threads: 1

Extra file open flags: 0
1 files, 10Gb each
10Gb total file size
Block size 4Kb
Number of random requests for random IO: 0
Read/Write ratio for combined random IO test: 1.50
Calling fsync() after each write operation.
Using synchronous I/O mode
Doing random write test
Threads started!
Time limit exceeded, exiting...
Done.

Operations performed:  0 Read, 43801 Write, 43801 Other = 87602 Total
Read 0b  Written 171.1Mb  Total transferred 171.1Mb  (5.7032Mb/sec)
 1460.02 Requests/sec executed

Test execution summary:
      total time:                          30.0004s
      total number of events:              43801
      total time taken by event execution: 29.9662
      per-request statistics:
           min:                                  0.10ms
           avg:                                  0.68ms
           max:                                275.50ms
           approx.  95 percentile:               3.28ms

Threads fairness:
      events (avg/stddev):           43801.0000/0.00
      execution time (avg/stddev):   29.9662/0.00

Test-sistema:

  • Adaptec 5405Z (ovvero 512 MB di cache di scrittura con protezione)
  • Intel Xeon L5520
  • 6 GiB RAM a 1066 MHz
  • Scheda madre Supermicro X8DTN (chipset 5520)
  • 12 dischi Seagate Barracuda da 1 TB
    • 10 nel software Linux RAID 10
  • Kernel 2.6.32
  • File system xfs
  • Debian instabile

In sintesi, ora sono sicuro che questa configurazione funzionerà bene in situazioni di inattività, a carico elevato e persino a pieno carico per il traffico del database che altrimenti sarebbero affamate dal traffico sequenziale. Il throughput sequenziale è superiore a quello che due link gigabit possono fornire comunque, quindi nessun problema a ridurlo un po '.


Qual è la tua metodologia per arrivare alla parte '15MB for dirty_buffers is ottimale'?
Marcin,

1
Prova ed errore. Ad esempio, cambia la metà della prossima volta, ecc., Fino a quando non ho finito con soli 15 MB e OK IOPS. L'attuale kernel 3.2 può comportarsi in modo molto diverso, a proposito.
Korkman,

2
Volevo solo dire grazie per avermi messo sulla strada giusta. Si sono verificati problemi simili con un nodo XenServer. Si è rivelato essere cache PHP-FPM / APC che causa pagine sporche. La regolazione del modello di memoria cache APC ha risolto il problema per noi. DiskIO è passato dal 20% di utilizzo a 0.
jeffatrackaid

Logicamente dirty_bytesdovrebbe essere appena abbastanza alto da non bloccare le CPU mentre i processi stanno scrivendo se il processo sta scrivendo in media con il throughput del dispositivo. Se il codice dell'applicazione sta eseguendo cicli di calcolo enorme seguiti dalla scrittura di enormi quantità di dati, se sarà molto difficile da ottimizzare perché le medie a breve termine differiscono notevolmente dalle medie a lungo termine. La soluzione corretta sarebbe quella di regolare le dirty_bytesimpostazioni specifiche del processo , ma Linux non supporta tale cosa per quanto ne so.
Mikko Rantalainen,

3

Anche se l'ottimizzazione dei parametri del kernel ha risolto il problema, in realtà è possibile che i problemi di prestazioni siano stati il ​​risultato di un bug sul controller Adaptec 5405Z che è stato corretto in un aggiornamento del firmware del 1 ° febbraio 2012. Le note di rilascio affermano "Risolto un problema a causa del quale il firmware poteva bloccarsi durante uno stress I / O elevato". Forse diffondere l'I / O come hai fatto è bastato per evitare che questo errore si inneschi, ma è solo una supposizione.

Ecco le note di rilascio: http://download.adaptec.com/pdfs/readme/relnotes_arc_fw-b18937_asm-18837.pdf

Anche se questo non fosse il caso della tua situazione particolare, ho pensato che ciò potesse essere di beneficio agli utenti che si imbatteranno in questo post in futuro. Abbiamo visto alcuni messaggi come i seguenti nel nostro output di dmesg che alla fine ci hanno portato all'aggiornamento del firmware:

aacraid: Host adapter abort request (0,0,0,0)
[above was repeated many times]
AAC: Host adapter BLINK LED 0x62
AAC0: adapter kernel panic'd 62.
sd 0:0:0:0: timing out command, waited 360s
sd 0:0:0:0: Unhandled error code
sd 0:0:0:0: SCSI error: return code = 0x06000000
Result: hostbyte=DID_OK driverbyte=DRIVER_TIMEOUT,SUGGEST_OK
sd 0:0:0:0: timing out command, waited 360s
sd 0:0:0:0: Unhandled error code
sd 0:0:0:0: SCSI error: return code = 0x06000028
Result: hostbyte=DID_OK driverbyte=DRIVER_TIMEOUT,SUGGEST_OK
sd 0:0:0:0: timing out command, waited 360s
sd 0:0:0:0: Unhandled error code
sd 0:0:0:0: SCSI error: return code = 0x06000028

Ecco i numeri di modello dei controller RAID Adaptec che sono elencati nelle note di rilascio del firmware con la correzione di blocco I / O elevata: 2045, 2405, 2405Q, 2805, 5085, 5405, 5405Z, 5445, 5445Z, 5805, 5805Q, 5805Z, 5805ZQ, 51245, 51645, 52445.


1
Wow, grazie per il tuo contributo. Anche se questo non è stato il mio caso, mi dai ancora un altro motivo per evitare del tutto HW RAID e passare alle sole configurazioni HBA. HW RAID ha ancora il vantaggio di BBWC, ma con cose come bcache che si spostano nel kernel, anche questo svanisce. Il lato negativo di HW RAID è esattamente il tipo di bug del firmware che descrivi. Avevo un altro sistema con installazione DRBD e un elevato carico di I / O che causava il ripristino del firmware, quindi non è raro imbattersi (potrebbe essere stato esattamente quel bug).
Korkman,

1

Un kernel che include "WBT":

Miglioramenti a livello di blocco , LWN.net

Con la limitazione del writeback, [lo strato di blocco] tenta di ottenere le massime prestazioni senza eccessiva latenza I / O utilizzando una strategia presa in prestito dallo scheduler di rete CoDel. CoDel tiene traccia della latenza minima osservata dei pacchetti di rete e, se supera un valore di soglia, inizia a eliminare i pacchetti. L'eliminazione di scritture è disapprovata nel sottosistema I / O, ma una strategia simile è seguita in quanto il kernel monitora la latenza minima di letture e scritture e, se supera un valore di soglia, inizia a ridurre la quantità di riscrittura in background è stato fatto. Questo comportamento è stato aggiunto in 4.10; Axboe ha detto che sono stati visti risultati abbastanza buoni.

WBT non richiede il passaggio al nuovo livello di blocco blk-mq. Detto questo, non funziona con gli scheduler I / O CFQ o BFQ. È possibile utilizzare WBT con gli scheduler deadline / mq-deadline / noop / none. Credo che funzioni anche con il nuovo scheduler I / O "kyber".

Oltre a ridimensionare la dimensione della coda per controllare la latenza, il codice WBT limita il numero di richieste di riscrittura in background in proporzione al limite di coda calcolato.

La configurazione di runtime è in /sys/class/block/*/queue/wbt_lat_usec.

Le opzioni di configurazione della build da cercare sono

/boot/config-4.20.8-200.fc29.x86_64:CONFIG_BLK_WBT=y
/boot/config-4.20.8-200.fc29.x86_64:# CONFIG_BLK_WBT_SQ is not set
/boot/config-4.20.8-200.fc29.x86_64:CONFIG_BLK_WBT_MQ=y

L'affermazione del problema è stata confermata al 100% dall'autore di WBT - ben fatto :-).

Blocco [PATCHSET]: limitazione del writeback bufferizzato

Dall'alba dei tempi, il nostro writeback bufferizzato in background ha fatto schifo. Quando eseguiamo un writeback con buffer in background, dovrebbe avere un impatto limitato sull'attività in primo piano. Questa è la definizione di attività in background ... Ma per quanto ricordo, gli scrittori con buffer pesante non si sono comportati così. Ad esempio, se faccio qualcosa del genere:

$ dd if=/dev/zero of=foo bs=1M count=10k

sul mio laptop, quindi prova a avviare Chrome, in pratica non si avvia prima che venga eseguito il writeback bufferizzato. Oppure, per carichi di lavoro orientati al server, in cui l'installazione di un RPM di grandi dimensioni (o simile) influisce negativamente sulle letture del database o sulla sincronizzazione delle scritture. Quando ciò accade, ho persone che mi urlano contro.

I risultati di alcuni test recenti sono disponibili qui:

https://www.facebook.com/axboe/posts/10154074651342933

Vedi le pubblicazioni precedenti per una descrizione più ampia del patchset.


Sono felice di vedere che il problema è stato riconosciuto e risolto all'interno del kernel ora. Tieni presente che blk-mq è abbastanza nuovo e forse non ancora maturo .
Korkman,

@korkman sospiro, immagino che mangerò la citazione per evitare la falsa implicazione. Sono d'accordo che questo è qualcosa aggiunto negli ultimi due anni, potrebbero esserci ancora regressioni delle prestazioni o peggio. AFAIR il manutentore rifiuta la correzione della corruzione dei dati nel senso che è un colpo di fortuna. Se si utilizzano le versioni del kernel in cui è stato sviluppato blk-mq, è discutibile quanto l'utilizzo del livello di blocco "legacy" eviterà i bug. Il bug di sospensione che ho corretto era un bug originato in blk-mq, quindi è stato refactored o qualcosa del genere e ha interessato entrambi. github.com/torvalds/linux/commit/1dc3039bc87a
sourcejedi

0

Qual è la tua media per Dirty in / proc / meminfo? Questo non dovrebbe normalmente superare il tuo / proc / sys / vm / dirty_ratio. Su un file server dedicato ho impostato dirty_ratio su un'altissima percentuale di memoria (90), poiché non la supererò mai. La tua dirty_ration è troppo bassa, quando la colpisci, tutto schizza via, alzalo.


Il problema non è che i processi vengono bloccati quando si colpisce dirty_ratio. Sto bene con quello. Ma il processo "in background" che scrive i dati sporchi sui dischi riempie le code senza pietà e uccide le prestazioni IOPS. Penso che sia chiamato fame di IO. In effetti, l'impostazione di dirty_ratio_bytes su un valore estremamente basso (come 1 MB) aiuta molto, perché il flushing avverrà quasi immediatamente e le code verranno mantenute vuote. Lo svantaggio è probabilmente una velocità inferiore per il sequenziale, ma va bene.
Korkman,

Hai spento tutti gli ascensori? Cos'altro hai modificato da un sistema alla vaniglia?
Luca,

1
Vedi la mia risposta. La fine della storia era rimuovere la cache sporca e lasciare quella parte al controller HW. Gli ascensori sono in qualche modo irrilevanti con la cache di scrittura HW in atto. Il controller ha i suoi algoritmi di ascensore, quindi avere qualsiasi ascensore nel software aggiunge solo un sovraccarico.
Korkman,

Elevevator nel software è un compromesso: sacrificare la latenza per migliorare la larghezza di banda. Ad esempio, immagina 100 K di operazioni di scrittura nella coda del software inviata in ordine casuale; se l'ascensore del software può ordinare quelle operazioni usando un enorme buffer, potrebbe finire per inviare solo 5K richieste molto più grandi al dispositivo. Tuttavia, di conseguenza, la latenza deve essere aumentata di 100.000 operazioni perché è possibile che le prime operazioni 2K e le ultime operazioni 1K siano effettivamente vicine l'una all'altra sul dispositivo. Senza ulteriore latenza, sarà impossibile unirli.
Mikko Rantalainen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.