Limite el vaciado de fondo de Linux (páginas sucias)

26

El vaciado de fondo en Linux ocurre cuando hay demasiados datos escritos pendientes (ajustables a través de / proc / sys / vm / dirty_background_ratio) o se alcanza un tiempo de espera para las escrituras pendientes (/ proc / sys / vm / dirty_expire_centisecs). A menos que se alcance otro límite (/ proc / sys / vm / dirty_ratio), se pueden almacenar en caché más datos escritos. Otras escrituras bloquearán.

En teoría, esto debería crear un proceso en segundo plano escribiendo páginas sucias sin perturbar otros procesos. En la práctica, perturba cualquier proceso de lectura sin caché o escritura sincrónica. Mal. Esto se debe a que el vaciado de fondo en realidad escribe al 100% de la velocidad del dispositivo y cualquier otra solicitud de dispositivo en este momento se retrasará (porque todas las colas y cachés de escritura en el camino están llenas).

¿Hay alguna forma de limitar la cantidad de solicitudes por segundo que realiza el proceso de lavado o de otra manera priorizar efectivamente las E / S de otros dispositivos?

korkman
fuente
Tal vez esta sea una buena pregunta para enviar a la lista de correo del kernel de Linux vger.kernel.org/vger-lists.html#linux-kernel
¿Qué planificador de IO estás usando?
3dinfluence
Intenté varios (cfq, fecha límite), pero supongo que solo funcionan de manera confiable cuando no se incluye caché de escritura respaldada por batería. Como una matriz de discos, tengo 1 GiB de datos a la velocidad del bus PCIe (RAM) y luego golpeo el muro de la realidad. Varios segundos cero E / S para todos los LUN. Limitar los sofocos (al menos los de fondo) a una estimación aproximada de la velocidad real del dispositivo resolvería ese problema de congestión.
korkman
1
Recientemente me di cuenta de que / sys / block / sdX / queue / nr_requests es un sintonizable importante. Reducirlo al mínimo (= 4 en mi caso) mejora mucho la latencia de carga concurrente: las escrituras aleatorias por segundo de Sysbench fsync saltaron de 4 (!) A 80-90 mientras escribía a la velocidad del bus con dd. El rendimiento sin carga parece no verse afectado. Los programadores son todos iguales, noop o la fecha límite parece óptima. Esto puede ser cierto para la mayoría de las configuraciones de BBWC.
korkman

Respuestas:

20

Después de mucho benchmarking con sysbench, llego a esta conclusión:

Para sobrevivir (en cuanto al rendimiento) una situación donde

  • un malvado proceso de copia inunda páginas sucias
  • y la caché de escritura de hardware está presente (posiblemente también sin eso)
  • y las lecturas o escrituras sincrónicas por segundo (IOPS) son críticas

simplemente volcar todos los ascensores, colas y cachés de páginas sucias. El lugar correcto para las páginas sucias es en la RAM de ese caché de escritura de hardware.

Ajuste dirty_ratio (o new dirty_bytes) lo más bajo posible, pero vigile el rendimiento secuencial. En mi caso particular, 15 MB fueron óptimos ( echo 15000000 > dirty_bytes).

Esto es más un truco que una solución porque los gigabytes de RAM ahora se usan solo para el almacenamiento en caché de lectura en lugar de la caché sucia. Para que el caché sucio funcione bien en esta situación, el enjuague de fondo del kernel de Linux necesitaría promediar a qué velocidad el dispositivo subyacente acepta solicitudes y ajustar el enjuague de fondo en consecuencia. No es fácil.


Especificaciones y puntos de referencia para la comparación:

Probado mientras ddusaba ceros en el disco, sysbench mostró un gran éxito , al aumentar 10 escrituras de fsync a 16 kB de 33 a 700 IOPS (límite de inactividad: 1500 IOPS) y un solo hilo de 8 a 400 IOPS.

Sin carga, los IOPS no se vieron afectados (~ 1500) y el rendimiento se redujo ligeramente (de 251 MB / sa 216 MB / s).

dd llamada:

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

para sysbench, el test_file.0 se preparó para no analizarse con:

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

sysbench llama para 10 hilos:

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

sysbench llama para un hilo:

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

Los tamaños de bloque más pequeños mostraron números aún más drásticos.

--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 en el sistema inactivo:

sysbench 0.4.12: punto de referencia de evaluación del sistema de subprocesos múltiples

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

Sistema de prueba:

  • Adaptec 5405Z (eso es 512 MB de caché de escritura con protección)
  • Intel Xeon L5520
  • 6 GiB RAM @ 1066 MHz
  • Placa base Supermicro X8DTN (chipset 5520)
  • 12 discos Seagate Barracuda de 1 TB
    • 10 en el software de Linux RAID 10
  • Kernel 2.6.32
  • Sistema de archivos xfs
  • Debian inestable

En resumen, ahora estoy seguro de que esta configuración funcionará bien en situaciones inactivas, de alta carga e incluso de carga completa para el tráfico de la base de datos que de lo contrario se habría visto afectado por el tráfico secuencial. El rendimiento secuencial es superior al que dos enlaces de gigabit pueden entregar de todos modos, por lo que no hay problema en reducirlo un poco.

korkman
fuente
¿Cuál es su metodología para llegar a la parte '15MB para dirty_buffers es óptima'?
Marcin
1
Prueba y error. Como, cambiar la mitad de la cantidad la próxima vez, etc., hasta que termine con solo 15 MB y OK IOPS. El kernel actual 3.2 puede comportarse de manera muy diferente, por cierto.
korkman
2
Solo quería decir gracias por ponerme en el camino correcto. Tuve algunos problemas similares con un nodo XenServer. Resultó ser caché PHP-FPM / APC que causa páginas sucias. El ajuste del modelo de memoria caché de APC nos resolvió el problema. DiskIO pasó de una utilización del 20% a 0.
jeffatrackaid
Lógicamente, dirty_bytesdebe ser apenas lo suficientemente alto como para no detener las CPU mientras los procesos están escribiendo si el proceso está escribiendo en promedio con el rendimiento del dispositivo. Si el código de su aplicación realiza ciclos de gran cálculo seguidos de una gran cantidad de datos, será muy difícil de optimizar porque los promedios de tiempo corto difieren mucho de los promedios de tiempo largo. La solución correcta sería ajustar la dirty_bytesconfiguración específica del proceso , pero Linux no es compatible hasta donde yo sé.
Mikko Rantalainen
3

Aunque ajustar los parámetros del kernel detuvo el problema, en realidad es posible que sus problemas de rendimiento sean el resultado de un error en el controlador Adaptec 5405Z que se corrigió en una actualización de firmware del 1 de febrero de 2012. Las notas de la versión dicen "Se solucionó un problema por el cual el firmware podía bloquearse durante un alto estrés de E / S". Quizás extender la E / S como lo hizo fue suficiente para evitar que se active este error, pero eso es solo una suposición.

Aquí están las notas de la versión: http://download.adaptec.com/pdfs/readme/relnotes_arc_fw-b18937_asm-18837.pdf

Incluso si este no fuera el caso para su situación particular, pensé que esto podría beneficiar a los usuarios que se encuentren con esta publicación en el futuro. Vimos algunos mensajes como el siguiente en nuestra salida de dmesg que finalmente nos llevaron a la actualización 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

Estos son los números de modelo de los controladores RAID Adaptec que se enumeran en las notas de la versión para el firmware que tiene la alta corrección de bloqueo de E / S: 2045, 2405, 2405Q, 2805, 5085, 5405, 5405Z, 5445, 5445Z, 5805, 5805Q, 5805Z, 5805ZQ, 51245, 51645, 52445.

sa289
fuente
1
Wow, gracias por tu aporte. Aunque este no fue el caso para mí, me das otra razón más para evitar HW RAID por completo y pasar a las configuraciones solo de HBA. HW RAID todavía tiene la ventaja de BBWC, pero con cosas como bcache moviéndose al núcleo, incluso eso desaparece. El lado positivo de HW RAID es exactamente el tipo de errores de firmware que describe. Tenía otro sistema con configuración DRBD y alta carga de E / S que causa restablecimientos de firmware, por lo que no es raro encontrarlo (podría haber sido exactamente ese error).
korkman
1

Un núcleo que incluye "WBT":

Mejoras en la capa de bloques , LWN.net

Con la limitación de reescritura, [la capa de bloques] intenta obtener el máximo rendimiento sin una latencia de E / S excesiva utilizando una estrategia prestada del planificador de red CoDel. CoDel rastrea la latencia mínima observada de los paquetes de red y, si eso excede un valor umbral, comienza a descartar paquetes. La eliminación de escrituras está mal vista en el subsistema de E / S, pero se sigue una estrategia similar en que el núcleo monitorea la latencia mínima de ambas lecturas y escrituras y, si eso excede un valor umbral, comienza a disminuir la cantidad de reescritura en segundo plano. Eso se está haciendo. Este comportamiento se agregó en 4.10; Axboe dijo que se han visto resultados bastante buenos.

WBT no requiere cambiar a la nueva capa de bloque blk-mq. Dicho esto, no funciona con los programadores de E / S CFQ o BFQ. Puede usar WBT con los programadores de fecha límite / mq-fecha límite / noop / none. Creo que también funciona con el nuevo planificador de E / S "kyber".

Además de escalar el tamaño de la cola para controlar la latencia, el código WBT limita el número de solicitudes de reescritura en segundo plano como una proporción del límite de cola calculado.

La configuración de tiempo de ejecución está en /sys/class/block/*/queue/wbt_lat_usec.

Las opciones de configuración de compilación a buscar son

/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

El autor de WBT confirmó su declaración del problema al 100%. Bien hecho :-).

Bloque [PATCHSET]: aceleración de reescritura en búfer

Desde los albores del tiempo, nuestra reescritura amortiguada de fondo ha apestado. Cuando hacemos reescritura en segundo plano, debería tener poco impacto en la actividad en primer plano. Esa es la definición de actividad en segundo plano ... Pero por lo que puedo recordar, los escritores con mucho buffer no se han comportado así. Por ejemplo, si hago algo como esto:

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

en mi computadora portátil, y luego intente iniciar Chrome, básicamente no comenzará antes de que finalice la reescritura en búfer. O, para cargas de trabajo orientadas al servidor, donde la instalación de un gran RPM (o similar) afecta negativamente las lecturas de la base de datos o las escrituras de sincronización. Cuando eso sucede, hago que la gente me grite.

Los resultados de algunas pruebas recientes se pueden encontrar aquí:

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

Ver publicaciones anteriores para una descripción más grande del conjunto de parches.

sourcejedi
fuente
Estoy feliz de ver que el problema se reconoce y se trata dentro del núcleo ahora. Tenga en cuenta que blk-mq es bastante nuevo y tal vez aún no sea tan maduro .
Korkman
@korkman suspiro, creo que destrozaré la cita para evitar la falsa implicación. Estoy de acuerdo en que esto es algo agregado en los últimos años, aún puede haber regresiones de rendimiento o algo peor. AFAIR el mantenedor descarta la corrección de corrupción de datos en el sentido de que es una casualidad. Si está utilizando las versiones de kernel donde se desarrolló blk-mq, es discutible cuánto usar la capa de bloque "heredado" evitará errores. El error de suspensión que arreglé fue un error que se originó en blk-mq, luego fue refactorizado o algo y afectó a ambos. github.com/torvalds/linux/commit/1dc3039bc87a
sourcejedi
0

¿Cuál es su promedio para Dirty en / proc / meminfo? Esto normalmente no debe exceder su / proc / sys / vm / dirty_ratio. En un servidor de archivos dedicado, tengo dirty_ratio configurado en un porcentaje muy alto de memoria (90), ya que nunca lo excederé. Su dirty_ration es demasiado baja, cuando la golpea, todo se cae, levántala.

Luke
fuente
El problema no es que los procesos se bloqueen al presionar dirty_ratio. Estoy de acuerdo con eso. Pero el proceso "en segundo plano" que escribe datos sucios en los discos llena las colas sin piedad y mata el rendimiento de IOPS. Se llama IO inanición, creo. De hecho, establecer sucio_ratio_bytes extremadamente bajo (como 1 MB) ayuda mucho, porque el enjuague ocurrirá casi de inmediato y las colas se mantendrán vacías. El inconveniente es posiblemente un rendimiento más bajo para secuencial, pero está bien.
korkman
¿Apagaste todos los ascensores? ¿Qué más modificaste de un sistema de vainilla?
Lucas
1
Mira mi auto respuesta. El final de la historia fue eliminar el almacenamiento en caché sucio y dejar esa parte al controlador HW. Los ascensores son irrelevantes con HW write-cache en su lugar. El controlador tiene sus propios algoritmos de elevador, por lo que tener cualquier elevador en el software solo agrega gastos generales.
korkman
Elevevator en software es una compensación: sacrifique la latencia para mejorar el ancho de banda. Por ejemplo, imagine 100K operaciones de escritura en la cola de software enviada en orden aleatorio; Si el elevador de software puede ordenar esas operaciones utilizando un enorme búfer, puede terminar enviando solo 5K solicitudes mucho más grandes al dispositivo. Sin embargo, como resultado, la latencia debe incrementarse en 100K operaciones porque puede ser que las primeras 2K operaciones y las últimas 1K operaciones estén realmente cerca unas de otras en el dispositivo. Sin latencia adicional, será imposible fusionarlos.
Mikko Rantalainen