Si administráis un sitio web que genera una gran cantidad de archivos temporales, como por ejemplo un sitio web de comercio electrónico, puede que en algún momento os encontréis con un error como este que os paraliza la web y, lo que es peor, os impide el acceso por phpMyAdmin:
Can't create/write to file '/tmp/#sql_bc9_0.MYI' (Errcode: 28): select * from ##### where (insale=1) and ...
El error 28 nos está indicando que no tenemos espacio suficiente, como podemos comprobar fácilmente con el siguiente comando:
$ perror 28 OS error code 28: No space left on device
Pero una simple comprobación nos indica que aún tenemos más de un 40% del disco duro libre, luego el problema no es exactamente ese. Optamos entonces por intentar reparar la tabla en cuestión, sin éxito. La tabla sigue «marked as crashed» y al ejecutar el comando repair se nos muestra un error diferente:
Can't create new tempfile: './#####/#####.TMD'
No puede crear un archivo temporal pese a tener casi medio disco duro vacío. Al ser un sitio web con muchos archivos, puede que el problema esté en los inodes (inodos, en español). Los inodes son unas estructuras de datos de Linux (y otros SO) que contienen las características de cualquier objeto del sistema de ficheros, ya se trate de archivos, directorios o enlaces simbólicos. En una web de este tipo y sin la configuración adecuada, puede que el número de inodes crezca y crezca hasta bloquear el sistema.
Para comprobar si este es el caso, ejecutamos df -i, lo que nos devuelve el siguiente resultado:
Filesystem Inodes IUsed IFree IUse% Mounted on udev 254877 1427 253450 1% /dev tmpfs 255448 927 254521 1% /run /dev/sda1 1509600 1509600 0 100% / none 255448 2 255446 1% /sys/fs/cgroup none 255448 1 255447 1% /run/lock none 255448 1 255447 1% /run/shm none 255448 2 255446 1% /run/user
Ese 100% en /dev/sda1 nos confirma que estamos sobre la pista correcta. Ahora hay que averiguar la ubicación exacta de esos inodes. Para ello vamos ejecutando este comando, empezando por el directorio raíz y profundizando poco a poco, para localizar cuál es el directorio que está saturado.
for i in /*; do echo $i; find $i -type f | wc -l; done
Esto nos devuelve una lista con el número de archivos de cada directorio. Nos adentramos a cada paso en aquél con mayor número de archivos, cambiando únicamente la ruta del for del comando por la ruta del directorio en cuestión. En nuestro ejemplo, esta búsqueda nos lleva finalmente hasta un directorio de sesiones de nuestra plataforma de e-commerce que contiene más de 76 millones de archivos.
La solución parece sencilla: borrar esos 76 millones de archivos, pero eso no es algo que Linux nos permita hacer con un simple rm * de los archivos anteriores a una determinada fecha. Son demasiados archivos. En un caso como este, debemos echar mano del útil comando xargs, que divide la lista de archivos en sublistas a las que va aplicando sucesivamente el comando de borrado. Otro detalle a tener en cuenta en este caso es el tiempo de sesión, pues no debemos borrar los inodes de sesiones activas, de modo que el comando queda así:
find /directoriosesiones/ -type f -cmin +$(/usr/lib/php5/maxlifetime) -print0 -exec | xargs rm {} \;
Y esto es todo. Para un caso como este con varios millones de archivos a borrar, debéis esperar un poco a que la plataforma vuelva a estar operativa.