Antecedentes: servidor físico, aproximadamente dos años de antigüedad, unidades SATA de 7200 RPM conectadas a una tarjeta RAID 3Ware, ext3 FS montado noatime y data = ordenado, no bajo carga loca, kernel 2.6.18-92.1.22.el5, tiempo de actividad 545 días . El directorio no contiene ningún subdirectorio, solo millones de archivos pequeños (~ 100 bytes), con algunos más grandes (algunos KB).
Tenemos un servidor que se ha vuelto un poco loco en el transcurso de los últimos meses, pero solo lo notamos el otro día cuando comenzó a no poder escribir en un directorio debido a que contenía demasiados archivos. Específicamente, comenzó a arrojar este error en / var / log / messages:
ext3_dx_add_entry: Directory index full!
El disco en cuestión tiene muchos inodos restantes:
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda3 60719104 3465660 57253444 6% /
Supongo que eso significa que llegamos al límite de cuántas entradas pueden estar en el archivo de directorio. No tengo idea de cuántos archivos serían, pero no puede ser más, como puede ver, más de tres millones. ¡No es que eso sea bueno, eso sí! Pero esa es la primera parte de mi pregunta: ¿cuál es exactamente ese límite superior? ¿Es sintonizable? Antes de gritar en Las-Quiero ajustarlo hacia abajo ; Este enorme directorio causó todo tipo de problemas.
De todos modos, rastreamos el problema en el código que estaba generando todos esos archivos, y lo hemos corregido. Ahora estoy atascado con la eliminación del directorio.
Algunas opciones aquí:
rm -rf (dir)
Intenté esto primero. Me di por vencido y lo maté después de que hubiera funcionado durante un día y medio sin ningún impacto perceptible.
- unlink (2) en el directorio: Definitivamente vale la pena considerarlo, pero la pregunta es si sería más rápido eliminar los archivos dentro del directorio a través de fsck que eliminarlos a través de unlink (2). Es decir, de una forma u otra, tengo que marcar esos inodes como no utilizados. Esto supone, por supuesto, que puedo decirle a fsck que no suelte entradas a los archivos en / lost + found; de lo contrario, acabo de mover mi problema. Además de todas las otras preocupaciones, después de leer un poco más sobre esto, resulta que probablemente tenga que llamar a algunas funciones internas de FS, ya que ninguna de las variantes de Unlink (2) que puedo encontrar me permitiría eliminar alegremente un directorio con entradas en el mismo. Pooh
while [ true ]; do ls -Uf | head -n 10000 | xargs rm -f 2>/dev/null; done )
Esta es en realidad la versión abreviada; el verdadero que estoy ejecutando, que solo agrega algunos informes de progreso y una parada limpia cuando nos quedamos sin archivos para eliminar, es:
exportar i = 0; tiempo (mientras [verdadero]; hacer ls -Uf | cabeza -n 3 | grep -qF '.png' || rotura; ls -Uf | cabeza -n 10000 | xargs rm -f 2> / dev / null; exportar i = $ (($ i + 10000)); echo "$ i ..."; hecho )
Esto parece estar funcionando bastante bien. Mientras escribo esto, ha eliminado 260,000 archivos en los últimos treinta minutos más o menos.
- Como se mencionó anteriormente, ¿se puede ajustar el límite de entrada por directorio?
- ¿Por qué tardó "7m9.561s reales / usuario 0m0.001s / sys 0m0.001s" para eliminar un solo archivo que fue el primero en la lista devuelto por
ls -U
, y tardó tal vez diez minutos en eliminar las primeras 10.000 entradas con el comando en el n. ° 3, pero ahora se está transportando muy felizmente. Para el caso, eliminó 260,000 en aproximadamente treinta minutos, pero ahora le tomó otros quince minutos eliminar 60,000 más. ¿Por qué los enormes cambios de velocidad? - ¿Hay una mejor manera de hacer este tipo de cosas? No almacenar millones de archivos en un directorio; Sé que es una tontería, y no habría sucedido en mi reloj. Buscar en Google el problema y mirar SF y SO ofrece muchas variaciones
find
que no van a ser significativamente más rápidas que mi enfoque por varias razones evidentes. Pero, ¿la idea delete-via-fsck tiene piernas? ¿O algo completamente diferente? Estoy ansioso por escuchar el pensamiento fuera de la caja (o dentro de la caja no conocida).
Salida del guión final !:
2970000...
2980000...
2990000...
3000000...
3010000...
real 253m59.331s
user 0m6.061s
sys 5m4.019s
Entonces, tres millones de archivos eliminados en poco más de cuatro horas.
rm -rfv | pv -l >/dev/null
. pv debería estar disponible en el repositorio EPEL .Respuestas:
La
data=writeback
opción de montaje merece ser probada, para evitar el registro en diario del sistema de archivos. Esto debe hacerse solo durante el tiempo de eliminación; sin embargo, existe el riesgo de que el servidor se apague o reinicie durante la operación de eliminación.De acuerdo con esta página ,
La opción se establece en
fstab
o durante la operación de montaje, reemplazandodata=ordered
condata=writeback
. El sistema de archivos que contiene los archivos que se eliminarán debe volver a montarse.fuente
commit
opción : "Este valor predeterminado (o cualquier valor bajo) perjudicará el rendimiento, pero es bueno para la seguridad de los datos. Establecerlo en 0 tendrá el mismo efecto que dejarlo en el valor predeterminado (5 segundos ". Establecerlo en valores muy grandes mejorará el rendimiento".data=writeback
todavía registra metadatos antes de escribirlos en el sistema de archivos principal. Según tengo entendido, simplemente no impone el orden entre cosas como escribir un mapa de extensión y escribir datos en esas extensiones. Tal vez hay otras restricciones de orden que también relaja, si viste una ganancia de rendimiento de esto. Por supuesto, montar sin el diario en absoluto podría ser un rendimiento aún mayor. (Puede permitir que los cambios de metadatos sucedan en la RAM, sin necesidad de tener nada en el disco antes de que se complete la operación de desvinculación).Si bien una de las principales causas de este problema es el rendimiento ext3 con millones de archivos, la causa raíz real de este problema es diferente.
Cuando un directorio necesita ser listado, se llama a readdir () en el directorio que produce una lista de archivos. readdir es una llamada posix, pero la llamada real del sistema Linux que se usa aquí se llama 'getdents'. Los solicitantes enumeran las entradas del directorio llenando un búfer con entradas.
El problema se debe principalmente al hecho de que readdir () usa un tamaño de búfer fijo de 32Kb para recuperar archivos. A medida que un directorio se hace más y más grande (el tamaño aumenta a medida que se agregan archivos) ext3 se vuelve más y más lento para buscar entradas y el tamaño de búfer adicional de 32 Kb de readdir solo es suficiente para incluir una fracción de las entradas en el directorio. Esto hace que readdir se repita una y otra vez e invoque la costosa llamada del sistema una y otra vez.
Por ejemplo, en un directorio de prueba que creé con más de 2.6 millones de archivos dentro, ejecutar "ls -1 | wc-l" muestra una gran salida de muchas llamadas al sistema getdent.
Además, el tiempo que pasó en este directorio fue significativo.
El método para hacer de este un proceso más eficiente es llamar a getdents manualmente con un búfer mucho más grande. Esto mejora el rendimiento significativamente.
Ahora, se supone que no debe llamar a getdents manualmente, por lo que no existe una interfaz para usarlo normalmente (consulte la página del manual para ver getdents), sin embargo, puede llamarlo manualmente y hacer que la invocación de llamadas de su sistema sea mucho más eficiente.
Esto reduce drásticamente el tiempo que lleva recuperar estos archivos. Escribí un programa que hace esto.
Si bien esto no combate el problema fundamental subyacente (muchos archivos, en un sistema de archivos que funciona mal en él). Es probable que sea mucho, mucho más rápido que muchas de las alternativas publicadas.
Como previsión, uno debe eliminar el directorio afectado y rehacerlo después. Los directorios solo aumentan de tamaño y pueden seguir teniendo un bajo rendimiento, incluso con algunos archivos dentro debido al tamaño del directorio.
Editar: he limpiado esto bastante. Se agregó una opción para permitirle eliminar en la línea de comandos en tiempo de ejecución y eliminó un montón de cosas del paseo del árbol que, sinceramente, mirar hacia atrás era cuestionable en el mejor de los casos. También se demostró que produce daños en la memoria.
Ahora puedes hacer
dentls --delete /my/path
Nuevos resultados Basado en un directorio con 1.82 millones de archivos.
Me sorprendió que esto todavía funcione tan bien.
fuente
[256]
probablemente debería ser[FILENAME_MAX]
, y dos, mi Linux (2.6.18 == CentOS 5.x) no parece incluir una entrada d_type en dirent (al menos según getdents (2)).¿Sería posible hacer una copia de seguridad de todos los demás archivos de este sistema de archivos en una ubicación de almacenamiento temporal, formatear la partición y luego restaurar los archivos?
fuente
No hay límite por archivo de directorio en ext3, solo el límite de inodo del sistema de archivos (aunque creo que hay un límite en el número de subdirectorios).
Es posible que aún tenga problemas después de eliminar los archivos.
Cuando un directorio tiene millones de archivos, la entrada del directorio se vuelve muy grande. La entrada del directorio tiene que ser escaneada para cada operación de eliminación, y eso toma varias cantidades de tiempo para cada archivo, dependiendo de dónde se encuentre su entrada. Desafortunadamente, incluso después de que se hayan eliminado todos los archivos, la entrada del directorio conserva su tamaño. Por lo tanto, las operaciones adicionales que requieren escanear la entrada del directorio aún llevarán mucho tiempo, incluso si el directorio ahora está vacío. La única forma de resolver ese problema es cambiar el nombre del directorio, crear uno nuevo con el nombre anterior y transferir los archivos restantes al nuevo. Luego borre el renombrado.
fuente
No lo he comparado, pero este tipo hizo :
fuente
find simplemente no funcionó para mí, incluso después de cambiar los parámetros de ext3 fs como lo sugirieron los usuarios anteriores. Consumió demasiada memoria. Este script PHP hizo el truco: uso de CPU rápido e insignificante, uso de memoria insignificante:
Publiqué un informe de error con respecto a este problema con find: http://savannah.gnu.org/bugs/?31961
fuente
Recientemente me enfrenté a un problema similar y no pude obtener la
data=writeback
sugerencia de ring0 para trabajar (posiblemente debido al hecho de que los archivos están en mi partición principal). Mientras investigaba soluciones, me topé con esto:Esto apagará el diario por completo, independientemente de la
data
opción que le desmount
. Combiné esto connoatime
y el volumen se habíadir_index
establecido, y parecía funcionar bastante bien. La eliminación en realidad terminó sin que tuviera que matarla, mi sistema siguió respondiendo y ahora está funcionando nuevamente (con el diario nuevamente) sin problemas.fuente
Asegúrate de hacer:
lo que debería acelerar un poco las cosas también.
fuente
Es un comando muy lento. Tratar:
fuente
strace -r -p <pid of rm>
para adjuntar al proceso rm que ya se está ejecutando. Luego puede ver qué tan rápidounlink
pasan las llamadas del sistema. (-r
pone el tiempo desde la llamada al sistema anterior al comienzo de cada línea.)¿Está
dir_index
configurado para el sistema de archivos? (tune2fs -l | grep dir_index
) Si no, habilítelo. Suele estar activado para el nuevo RHEL.fuente
Hace un par de años, encontré un directorio con 16 millones de archivos XML en el
/
sistema de archivos. Debido a la crítica del servidor, utilizamos el siguiente comando que tardó aproximadamente 30 horas en finalizar:Era un viejo disco duro de 7200 rpm , y a pesar del cuello de botella de IO y los picos de CPU, el viejo servidor web continuó su servicio.
fuente
Mi opción preferida es el enfoque newfs, ya sugerido. El problema básico es, nuevamente como ya se señaló, el escaneo lineal para manejar la eliminación es problemático.
rm -rf
debería ser casi óptimo para un sistema de archivos local (NFS sería diferente). Pero en millones de archivos, 36 bytes por nombre de archivo y 4 por inodo (una suposición, sin verificar el valor de ext3), eso es 40 * millones, que se mantendrán en la RAM solo para el directorio.Supongo que está agotando la memoria caché de metadatos del sistema de archivos en Linux, por lo que los bloques para una página del archivo de directorio se eliminan mientras todavía está usando otra parte, solo para golpear esa página del caché nuevamente la próxima vez El archivo es eliminado. El ajuste del rendimiento de Linux no es mi área, pero / proc / sys / {vm, fs} / probablemente contenga algo relevante.
Si puede permitirse el tiempo de inactividad, puede considerar activar la función dir_index. Cambia el índice del directorio de lineal a algo mucho más óptimo para su eliminación en directorios grandes (hashed b-trees).
tune2fs -O dir_index ...
seguido pore2fsck -D
funcionaría. Sin embargo, aunque estoy seguro de que esto ayudaría antes de que haya problemas, no sé cómo se realiza la conversión (e2fsck con el-D
) cuando se trata de un directorio v.large existente. Copias de seguridad + suck-it-and-see.fuente
/proc/sys/fs/vfs_cache_pressure
podría ser el valor a utilizar, pero no sé si el directorio en sí cuenta para la memoria caché de la página (porque eso es lo que es) o la memoria caché de inodo (porque, a pesar de no ser un inodo, son metadatos FS y agrupados allí por esa razón). Como digo, el ajuste de Linux VM no es mi área. Juega y mira lo que ayuda.Obviamente no manzanas con manzanas aquí, pero configuré una pequeña prueba e hice lo siguiente:
Creó 100,000 archivos de 512 bytes en un directorio (
dd
y/dev/urandom
en un bucle); olvidé cronometrarlo, pero me llevó aproximadamente 15 minutos crear esos archivos.Ejecutó lo siguiente para eliminar dichos archivos:
ls -1 | wc -l && time find . -type f -delete
Esta es una caja Pentium 4 2.8GHz (un par de cientos de GB IDE 7200 RPM, creo; EXT3). Kernel 2.6.27.
fuente
rm
es terriblemente lento en una gran cantidad de archivos, de ahí lafind -delete
opción. Con un comodín en el shell, expandirá cada nombre de archivo coincidente, y supongo que hay un búfer de memoria limitado para eso, por lo que podría ver cómo eso se volvería ineficiente.A veces, Perl puede hacer maravillas en casos como este. ¿Ya has probado si un script pequeño como este podría superar a bash y los comandos básicos del shell?
U otro enfoque de Perl, quizás incluso más rápido:
EDITAR: Acabo de probar mis scripts de Perl. Cuanto más detallado se hace algo bien. En mi caso, probé esto con un servidor virtual con 256 MB de RAM y medio millón de archivos.
time find /test/directory | xargs rm
resultados:comparado con
fuente
*(oN)
]Por lo que recuerdo, la eliminación de inodes en los sistemas de archivos ext es O (n ^ 2), por lo que cuantos más archivos elimine, más rápido irá el resto.
Hubo una vez que me enfrenté a un problema similar (aunque mis estimaciones miraron el tiempo de eliminación de ~ 7h), al final fue la ruta sugerida por jftuga en el primer comentario .
fuente
Bueno, esta no es una respuesta real, pero ...
¿Sería posible convertir el sistema de archivos a ext4 y ver si las cosas cambian?
fuente
De acuerdo, esto se ha cubierto de varias maneras en el resto del hilo, pero pensé en tirar mis dos centavos. El culpable de rendimiento en su caso es probablemente readdir. Está obteniendo una lista de archivos que no son necesariamente secuenciales en el disco, lo que está causando acceso al disco en todo el lugar cuando se desvincula. Los archivos son lo suficientemente pequeños como para que la operación de desvinculación probablemente no salte demasiado poniendo a cero el espacio. Si lee y luego ordena por inodo ascendente, probablemente obtendrá un mejor rendimiento. Entonces readdir en ram (ordenar por inodo) -> desvincular -> beneficio.
Inode es una aproximación aproximada aquí, creo ... pero basándome en su caso de uso podría ser bastante preciso ...
fuente
Probablemente habría sacado un compilador de C y hecho el equivalente moral de tu guión. Es decir, use
opendir(3)
para obtener un identificador de directorio, luego usereaddir(3)
para obtener el nombre de los archivos, luego registre los archivos a medida que los desvío y de vez en cuando imprima "% d archivos eliminados" (y posiblemente el tiempo transcurrido o la marca de tiempo actual).No espero que sea notablemente más rápido que la versión del script de shell, es solo que estoy acostumbrado a tener que extraer el compilador de vez en cuando, ya sea porque no hay una manera limpia de hacer lo que quiero del shell o porque Si bien es factible en shell, es improductivamente lento de esa manera.
fuente
Es probable que tenga problemas de reescritura con el directorio. Intente eliminar primero los archivos más nuevos. Mire las opciones de montaje que diferirán la reescritura en el disco.
Para una barra de progreso intente ejecutar algo como
rm -rv /mystuff 2>&1 | pv -brtl > /dev/null
fuente
Así es como elimino los millones de archivos de rastreo que a veces se pueden reunir en un gran servidor de base de datos Oracle:
Me parece que esto da como resultado una eliminación bastante lenta que tiene un bajo impacto en el rendimiento del servidor, generalmente algo así como una hora por millón de archivos en una configuración "típica" de 10,000 IOPS.
A menudo tomará varios minutos antes de que se hayan escaneado los directorios, se haya generado la lista de archivos inicial y se elimine el primer archivo. De allí en adelante, a. se repite para cada archivo eliminado.
La demora causada por el eco en el terminal ha demostrado ser una demora suficiente para evitar cualquier carga significativa mientras la eliminación está progresando.
fuente
find /u* -maxdepth 3 -mindepth 3 -type d -path '*/app/*' -name diag -print0 | xargs -0I = find = -mindepth 4 -maxdepth 4 -type d -name 'trace' -print0 | xargs -0I = find = -mindepth 1 -maxdepth 1 -name '*.tr'
:? Agregue-delete
al último para eliminar realmente las cosas; tal como está escrito, solo enumera lo que eliminaría. Tenga en cuenta que esto está optimizado para circunstancias en las que tiene muchas cosas sin interés en directorios cercanos; Si ese no es el caso, puede simplificar mucho la lógica.rm
sucede), por lo que tiene relativamente eficiente de E / S al arrancar de eso, seguido por dolorosa, fuera de ordenrm
s eso probablemente no cause mucha E / S, sino que impliquescandir
recorrer el directorio repetidamente (sin causar E / S porque ya se ha cargado en el caché de bloques; ver tambiénvfs_cache_pressure
). Si desea ralentizar las cosas,ionice
es una opción, pero probablemente usaría s de fracción de segundosleep
.find /u*/app/*/diag -path '*/trace/*.tr' -execdir rm {} +
ejecutaría unorm
por directorio, por lo que tendría menos sobrecarga de CPU. Supongo que siempre que tenga toneladas de tiempo de CPU libre, acelere la E / S del disco al bifurcar todo unrm
proceso para cadaunlink
trabajo, pero es feo. Perl con un sueño por desvinculación sería mejor si dormir entrerm
directorios completos a la vez es demasiado explosivo. (-execdir sh -c ...
quizás)Puede usar las funciones de paralelización de 'xargs':
fuente
fuente
en realidad, este es un poco mejor si el shell que usas sí expande la línea de comandos:
fuente