...

«Dirty Ratio» y «Dirty Background Ratio» en Linux: ajuste preciso para un rendimiento óptimo de escritura

Muestro cómo linux sucio y el «dirty background ratio» para controlar la caché de página y, con ello, influir en el rendimiento de escritura, la latencia y la seguridad de los datos. De este modo, puedes establecer valores límite concretos que activen los flusher a tiempo, eviten los bloqueos y aumenten el rendimiento de escritura de tus cargas de trabajo.

Puntos centrales

Para empezar, resumiré brevemente las ideas principales antes de profundizar en el tema.

  • Páginas subidas de tono Almacena temporalmente los datos de Writes en la RAM y agrupa muchos pequeños accesos para obtener operaciones de E/S más eficientes.
  • dirty_background_ratio Inicia subprocesos de limpieza en segundo plano, lo que limita así la cantidad de residuos sin que se note.
  • cociente_sucio frena los procesos de escritura cuando se supera el límite estricto.
  • Relación Ambos valores determinan los picos de latencia, el rendimiento y el tamaño del búfer.
  • Variantes de bytes (dirty_bytes) permiten un control más preciso y absoluto en servidores de gran tamaño.

Entender las «páginas obscenas»

Cuando un proceso escribe datos, estos van a parar primero al Caché de página y se marcan como „dirty“ hasta que el núcleo pueda volcarlas al disco. Este almacenamiento en búfer acelera las aplicaciones, ya que la RAM responde más rápido que cualquier SSD o HDD, y las pequeñas operaciones de escritura se agrupan para formar grandes transferencias secuenciales. Siempre tengo presente cuánta „suciedad“ permito, ya que un exceso de almacenamiento en búfer puede alargar las colas de espera o suponer un mayor riesgo de que se pierdan datos en caso de fallos del sistema. Quien comprenda cómo funciona esto, tomará mejores decisiones en cuanto a la reescritura, la latencia y la presión de almacenamiento. Un breve artículo de fondo sobre el Caché de reescritura ayuda a clasificar esta mecánica de forma clara.

Seguridad de los datos, fsync y ventana de fallo

Los valores límite no solo influyen en el rendimiento, sino también en tu margen de riesgo. Lo calculo a grandes rasgos con una sencilla regla empírica: la cantidad máxima de datos no validados dividida por el rendimiento sostenido del dispositivo da, aproximadamente, el tiempo que tarda el búfer en vaciarse. Ejemplo: si permito 4 GB de datos «sucios» y el medio de destino alcanza los 500 MB/s, la reescritura completa tardará unos 8 segundos. Durante ese tiempo, en caso de corte de corriente o de fallo del kernel, podrían perderse las últimas escrituras.

Las aplicaciones pueden cerrar la ventana mediante fsync() o fdatasync() reducir, ya que estas llamadas obligan al sistema de archivos a escribir datos (y, según el modo de registro, también metadatos) en el soporte. Esto es más costoso, pero esencial para bases de datos o registros. Me aseguro de que mis límites de datos sucios se ajusten al comportamiento de sincronización: frecuentes fsync()-Las visitas se benefician de un menor cociente_sucio, para que el núcleo no aplique una limitación adicional cuando, de todos modos, la persistencia se realiza con regularidad. Por el contrario, en el caso de los registros en los que predominan las operaciones de «append» y que rara vez se vacían, puedo permitir búferes más grandes, siempre teniendo en cuenta el riesgo de pérdida aceptado.

También son importantes las barreras y el orden de escritura: los sistemas de archivos modernos utilizan comandos FUA/Flush para vaciar correctamente las cachés de los controladores. En soportes sin protección contra pérdidas de alimentación (PLP), los búferes de gran tamaño aumentan el riesgo; con PLP o protección de la caché de escritura, a menudo es aceptable utilizar búferes más grandes.

Proporción de fondo sucio: el umbral suave

Con dirty_background_ratio Indico a partir de qué porcentaje de la memoria disponible los hilos de vaciado comienzan a escribir en segundo plano. Este valor no bloquea ninguna aplicación, sino que inicia de forma silenciosa las tareas de limpieza para que el búfer no se desborde. Los valores bajos generan una escritura en segundo plano más frecuente, pero más uniforme, y suavizan los picos de latencia. Los valores más altos permiten un mayor tamaño del búfer, lo que aumenta el rendimiento en el caso de grandes escrituras secuenciales, aunque puede provocar picos de E/S considerables en caso de un vaciado repentino. Normalmente, los valores por defecto rondan el diez por ciento, pero yo ajusto este límite en función del soporte, la carga de trabajo y los requisitos de seguridad.

Dirty Ratio: la frenada brusca

El parámetro cociente_sucio marca el límite a partir del cual el núcleo limita los procesos de escritura hasta que se hayan reescrito suficientes páginas. Este límite estricto protege la memoria frente a una avalancha de datos no persistentes y, por lo tanto, afecta directamente a las aplicaciones en cuanto estas intentan seguir generando datos. En el caso de las bases de datos, suelo establecer un valor bastante bajo para que las consultas mantengan tiempos de respuesta constantes y no se produzcan fases de vaciado prolongadas. En cambio, para las tareas de copia de seguridad utilizo búferes más generosos, con el fin de transferir bloques grandes de forma eficiente. Los valores predeterminados habituales oscilan entre el veinte y el cuarenta por ciento, pero yo siempre adapto este rango a la carga concreta.

Interacción y relaciones típicas

Ambos valores límite funcionan como Tándem y solo surten efecto cuando se combinan. Siempre mantengo el valor de `dirty_background_ratio` por debajo del de `dirty_ratio`, para que el núcleo se inicie a tiempo en segundo plano y el freno duro se active en contadas ocasiones. Como regla general, suelo elegir entre una cuarta parte y la mitad del límite máximo; por ejemplo, 5-10 frente a 20. De este modo, el «writeback» se inicia con la suficiente antelación sin reducir innecesariamente el rendimiento. Quien no respete esta relación se encontrará, o bien con una limitación demasiado prematura, o bien con un trabajo en segundo plano demasiado tardío, con picos de latencia perceptibles.

Control por dispositivo y detalles de la capa de bloques

Además de los límites globales, merece la pena fijarse en el nivel de los dispositivos. Linux distribuye la carga sucia a través de los denominados Dispositivos compatibles (bdi). En /sys/class/block//bdi/ Me parecen parámetros como max_ratio, que determinan qué parte del «Dirty-Budget» global permitido puede consumir cada dispositivo. En sistemas que cuentan con unidades de disco lentas y rápidas en paralelo, limito las unidades lentas para que no se conviertan en un cuello de botella.

También es relevante la limitación de la capa de bloques a través de /sys/block//queue/wbt_lat_usec (Writeback Throttling). Con ello establezco una latencia objetivo; el kernel limita la carga de escritura cuando se supera ese tiempo objetivo. En el caso de los discos duros SATA, suelo establecer valores conservadores para proteger la interactividad. En unidades NVMe muy rápidas, desactivo o aumento la latencia objetivo para que el controlador pueda aprovechar al máximo su paralelismo. Elijo el programador de E/S (mq-deadline, BFQ, none) según convenga: BFQ resulta útil en sistemas interactivos con carga mixta, mientras que ninguno o que mq-deadline suele funcionar mejor en trabajos de puro rendimiento con NVMe.

La interacción es fundamental: si dirty_background_ratio Aunque el valor sea bajo, si el dispositivo se limita de forma agresiva mediante WBT, siguen produciéndose atascos visibles. Por eso calibro ambos niveles a la vez: límites globales de «dirty» para el tamaño del búfer y la capa de bloques para la protección contra la latencia.

Ratio frente a bytes: valores por defecto y variantes

En sistemas con mucho RAM Los porcentajes se convierten rápidamente en grandes magnitudes absolutas. En ese caso, prefiero establecer límites máximos absolutos con dirty_bytes y dirty_background_bytes para limitar claramente el tamaño del búfer a unos 2-8 GB. Esto desacopla el control de los niveles de memoria, que fluctúan mucho, y mantiene predecible la cantidad de datos no persistentes. La elección sigue siendo dinámica: para servidores pequeños con poca RAM, los porcentajes suelen ser más que suficientes. Quien disponga de una gran capacidad, a menudo tendrá más facilidad para planificar utilizando valores en bytes.

Parámetros Significado Valores predeterminados típicos ¿Cuándo hay que cambiarlo? Nota
vm.dirty_background_ratio Inicio del Limpieza de fondo en porcentaje ≈ 10% En caso de fluctuaciones en la latencia o de SSD/NVMe muy rápidos Más bajo = latencia más estable; más alto = mayor capacidad de búfer
vm.dirty_ratio Duro Límite de estrangulamiento en porcentaje ≈ 20–40% En las bases de datos es menor, en las copias de seguridad es mayor Demasiado alto → Posibles bloqueos al realizar un flush
vm.dirty_background_bytes Inicio del «flush» en segundo plano en Bytes Desactivado si se utiliza Ratio Gran cantidad de RAM, objetivos de búfer fijos Anula los parámetros de Ratio
vm.bytes sucios Límite estricto de modulación en Bytes Desactivado si se utiliza Ratio Gran capacidad de RAM, límite máximo programable Anula los parámetros de Ratio

Escenarios de carga de trabajo y recomendaciones

Cargas de escritura secuenciales como Copias de seguridad Se benefician de grandes búferes y de una escritura en segundo plano moderada, ya que el núcleo puede escribir en el soporte a grandes rasgos. En este caso, suelo fijar dirty_ratio entre el 30 y el 40 por ciento, y dirty_background_ratio entre el 10 y el 20 por ciento. Las bases de datos y las pequeñas aplicaciones de E/S aleatoria se benefician de una latencia predecible, por lo que elijo un 10-15 % en modo «duro» y un 3-5 % en modo «suave». Para servidores mixtos de web y aplicaciones, un 15-20 % de «hard» y un 5-10 % de «soft» resultan ser un buen término medio. Estos rangos sirven como punto de partida; a partir de ahí, lo que cuenta es la realidad medida de tu sistema.

Aspectos del sistema de archivos y opciones de montaje

La ruta de reescritura termina en el sistema de archivos, cuya estrategia determina las latencias y la seguridad. Ext4 con datos=ordenados (Por defecto) escribe los datos de usuario antes de las confirmaciones del diario; data=respuesta reduce la latencia, pero pone en riesgo los datos antiguos en caso de fallos del sistema. El parámetro comprometer= (segundos) determina la frecuencia con la que se guarda el registro. Los intervalos más cortos reducen la pérdida de datos, pero consumen más E/S. XFS utiliza un diseño de registro muy maduro; los archivos grandes logbsize y una alineación adecuada facilitan las tareas de alto rendimiento. Btrfs agrupa las escrituras mediante «copy-on-write», lo que estabiliza las latencias, pero puede provocar fragmentación en el caso de pequeñas escrituras aleatorias y SSD con poca capacidad. Opciones como nodatacow pueden ayudar a desfragmentar determinadas rutas o de forma selectiva cuando se producen picos de latencia.

Además, tengo en cuenta que relatime/noatime (reduce las operaciones de escritura de metadatos), lazytime (retrasa los valores de mtime/atime de forma más persistente) y las barreras de registro. Precisamente en los controladores RAID o en las máquinas virtuales, es fundamental que la semántica de la caché sea correcta: unas cachés de escritura mal configuradas echan por tierra todo el trabajo de «dirty tuning».

E/S directa, O_SYNC y comportamiento de la aplicación

No todas las aplicaciones pasan por la caché de páginas. Con O_DIRECTO o O_SYNC/O_DSYNC Algunos procesos eluden partes de la caché o requieren una persistencia inmediata. Las bases de datos suelen escribir un WAL/registro de rehacer de forma sincrónica y las áreas de datos de forma asincrónica. Calibro los límites de datos modificados especialmente para las rutas asincrónicas, mientras que garantizo la latencia de las rutas sincrónicas mediante registros rápidos (NVMe, LUN dedicado). Cuando las aplicaciones realizan con mucha frecuencia fsync() a la hora de acceder a ellos, los búferes grandes no resultan muy útiles: en ese caso, la latencia depende más del controlador, la profundidad de la cola y el programador de E/S que de cociente_sucio.

Ajustes prácticos paso a paso

Antes de cada modificación, compruebo la valores reales con sysctl vm.dirty_ratio y sysctl vm.dirty_background_ratio, para documentar la situación inicial. Para las pruebas a corto plazo, anoto los valores directamente en /proc/sys/vm/por ejemplo echo 15 > /proc/sys/vm/dirty_ratio y echo 5 > /proc/sys/vm/dirty_background_ratio. Si el ajuste es permanente, lo guardo en /etc/sysctl.conf o /etc/sysctl.d/*.conf. Aplico los cambios con sysctl -p de inmediato, para poder medir el efecto lo antes posible. Quien se adentre más en el tema de las reglas del sistema se beneficiará de consejos prácticos sobre el Ajuste de sysctl en servidores de producción.

Cómo calcular valores: ejemplos de cálculo

Me gusta empezar con valores concretos. Ejemplo 1: servidor web/de aplicaciones con 64 GB de RAM y NVMe. El objetivo es una latencia baja. Yo utilizo dirty_background_bytes=1073741824 (1 GB) y dirty_bytes=3221225472 (3 GB). Con un rendimiento NVMe sostenido de 2 GB/s, esto supone entre 0,5 y 1,5 segundos para vaciarlo, lo cual es adecuado para cargas interactivas. Ejemplo 2: nodo de copia de seguridad con 128 GB de RAM y un RAID SATA rápido con 800 MB/s. Elijo dirty_background_ratio=10, dirty_ratio=35. En términos absolutos, son unos 12,8 GB y 44,8 GB; el RAID tarda entre 16 y 56 segundos en vaciarse. No pasa nada, porque la tarea no es interactiva.

Ejemplo 3: Servidor de base de datos con 256 GB de RAM, diario independiente en NVMe y datos en una matriz de SSD. Establezco un límite absoluto para evitar valores atípicos: dirty_background_bytes=2147483648 (2 GB), dirty_bytes=8589934592 (8 GB). Esto permite prever el tamaño de la ventana de colisión y reduce las frenadas bruscas durante los puntos de control.

Tiempo de reescritura y parámetros relacionados

Además de los valores límite, influyen Temporizador el comportamiento de reescritura y, por lo tanto, la sensación que transmiten las aplicaciones. Con vm.dirty_writeback_centisegundos controlo el intervalo en el que el «kernel flusher» se activa, mientras que vm.dirty_expire_centisecs define la antigüedad máxima que pueden alcanzar las páginas sucias. Los intervalos más cortos garantizan vaciados más frecuentes, aunque de menor tamaño; los intervalos más largos ahorran llamadas de E/S, pero conllevan el riesgo de que los lotes sean más grandes. Solo ajusto estos valores cuando las mediciones revelan desventajas reales, como vaciados demasiado espaciados en unidades NVMe rápidas. Quien actúe de forma metódica en este sentido evitará oscilaciones entre una actividad de reescritura demasiado intensa y otra demasiado lenta.

Seguimiento y ajuste preciso

Tras realizar algunos ajustes, observo que continuo los indicadores clave para poner de manifiesto los logros y los efectos secundarios. En /proc/meminfo Compruebo „Dirty“ y „Writeback“ para ver los niveles de la memoria intermedia y los flushing activos. Herramientas como iostat, sar o atop me muestran el rendimiento, las colas y las tendencias de latencia. Este artículo ofrece una buena introducción a las métricas sobre el Analizar la espera de E/S. Solo a partir de estos datos reduzco o aumento los límites poco a poco, para evitar que se produzcan efectos secundarios inesperados.

Contenedores, cgroups y distribución equitativa

En entornos de contenedores, las cargas de trabajo comparten los mismos mecanismos del núcleo. El «writeback» de cgroups garantiza que las páginas sucias se asignen a quien las ha generado. Utilizo los controladores de E/S de los cgroups (blkcg) para limitar el ancho de banda o las IOPS por contenedor cuando algunos inquilinos almacenan en búfer de forma demasiado agresiva. Límites absolutos de bytes a nivel de host (bytes_sucios) para evitar que un solo invitado se lleve todo el presupuesto de «Dirty». Además, limito el espacio de almacenamiento mediante memoria.max, para que Writeback no reaccione solo ante una presión global. El objetivo sigue siendo el mismo: ninguna carga del invitado debe provocar restricciones a nivel del host en el cociente_sucio imponer.

Entornos de alojamiento y máquinas virtuales

En entornos multicliente y máquinas virtuales, presto atención a Overbooking de la RAM y la E/S, ya que los límites porcentuales tienen allí un efecto diferente. Los límites absolutos en bytes pueden evitar que determinados invitados acumulen demasiado búfer y ralenticen a los vecinos. Tengo en cuenta la deduplicación de memoria, el «ballooning» y las cachés de los controladores, ya que se superponen a los efectos del almacenamiento en búfer. En el caso de los servidores gestionados, resulta beneficioso que el proveedor establezca valores predeterminados adecuados para que los clientes disfruten de tiempos de respuesta constantes. Quien gestione sus propios nodos se beneficia de una configuración de perfiles bien definida para cada clase de carga de trabajo.

Malentendidos y obstáculos habituales

  • „Más capacidad de reserva = cada vez más rendimiento“.“ Esto no es adecuado para cargas de trabajo con un alto porcentaje de operaciones aleatorias o para dispositivos con poca profundidad de cola. Unos búferes demasiado grandes provocan picos de vaciado y colas.
  • „dirty_ratio no afecta a las lecturas“.“ Indirectamente, sí: las fases agresivas de reescritura desplazan las páginas de la caché y aumentan las latencias de lectura.
  • „Los bytes y la razón se suman“.“ No. Si utilizas variantes de «bytes», anulan a sus equivalentes de «ratio». Quedan bien claras.
  • „fsync() hace que los límites de datos sucios dejen de tener importancia“.“ No. Aunque las sincronizaciones frecuentes reducen la ventana de riesgo, el resto de la carga sigue estando sujeta a los valores límite.
  • „Un soporte de datos rápido lo resuelve todo“.“ No, si la capa de bloques reduce el rendimiento (WBT) o si el sistema de archivos está montado de forma subóptima.
  • „Drop_caches es una herramienta de optimización“.“ Vaciar la caché distorsiona las mediciones y agrava los picos de latencia. En el entorno de producción, lo evito.

Solución de problemas: síntomas típicos y soluciones

Amontonar Picos de latencia, lo primero que hago es reducir el umbral de fondo para que los flusher se activen antes y se produzcan con menos frecuencia grandes oleadas de escritura. Si las aplicaciones se bloquean de forma intermitente, suele ser porque el límite máximo es demasiado alto o porque el soporte no puede gestionar las ráfagas de flushing que se generan. En tales casos, reduzco el valor de dirty_ratio, compruebo la configuración de lectura anticipada (readahead) y reviso las opciones de registro en diario del sistema de archivos. Con hardware NVMe muy rápido, aumento gradualmente el umbral de fondo para no limitar artificialmente el rendimiento. Tras cada cambio, me guío por los datos de medición, no por corazonadas.

Resumen breve para la práctica

Con pocos Tornillos de ajuste Puedo influir en cómo Linux almacena en búfer los datos de escritura, cuándo se inician los flusher y cuándo el kernel frena el sistema. El «Dirty Background Ratio» garantiza una limpieza silenciosa, mientras que el «Dirty Ratio» limita el consumo de RAM de forma más estricta. La relación entre ambos valores determina si tu sistema se orienta más hacia latencias uniformes o hacia un rendimiento máximo. Documento los valores predeterminados, realizo cambios poco a poco y evalúo las mediciones de forma sistemática. De este modo se crea una configuración que equilibra de forma razonable la carga de trabajo, el medio y el riesgo, y que en la práctica resulta notablemente más rápida.

Artículos de actualidad