¿Cómo resolver el límite de número de subdirectorios de Linux?

9

Tengo un sitio web que almacenará imágenes de perfil de usuario. Cada imagen se almacena en un directorio (Linux) específico para el usuario. Actualmente tengo una base de clientes de más de 30, lo que significa que tendré más de 30 carpetas. Pero mi actual caja de Linux (ext2 / ext3) no admite la creación de más de 32000 directorios. ¿Cómo paso esto? Incluso los chicos de YouTube tienen el mismo problema, con miniaturas de video. Pero lo resolvieron moviéndose a ReiserFS. ¿No podemos tener una mejor solución?

Actualización: cuando se les preguntó en IRC, la gente preguntaba sobre cómo actualizarlo a ext4, que tiene un límite de 64k y, por supuesto , incluso puede superarlo . O pirateo de kernel para cambiar el límite.

Actualización: ¿Qué hay de dividir la base de usuarios en carpetas en función del rango de ID de usuario? Significa 1-1000 en una carpeta, 1000-2000 en la otra así. Esto parece ser simple. ¿Qué dicen chicos?

Francamente, ¿no hay otra manera?

Ninguno-da
fuente
1
¿Por qué no quieres cambiar el sistema de archivos? Si esto es una limitación de ext2 / 3, no tendrá otro cambio que cambiar el sistema de archivos o dividir el FS actual en FS más pequeños (más puntos de montaje diferentes).
Manuel Faux
1
Manuel: Si cambia el sistema de archivos, está vinculando un FS específico a su aplicación. Aunque esa podría ser la respuesta, creo que este es probablemente un problema que debe resolverse a nivel de aplicación. Si necesita hackear el kernel o el sistema de archivos, probablemente esté yendo por el camino equivocado a menos que tenga unos requisitos muy especiales.
Kyle Brandt

Respuestas:

16

Ese límite es por directorio, no para todo el sistema de archivos, por lo que podría solucionarlo subdividiendo más cosas. Por ejemplo, en lugar de tener todos los subdirectorios de usuario en el mismo directorio, divídalos por los dos primeros caracteres del nombre para que tenga algo como:

top_level_dir
|---aa
|   |---aardvark1
|   |---aardvark2
|---da
|   |---dan
|   |---david
|---do
    |---don

Aún mejor sería crear algún tipo de hash de los nombres y usarlo para la división. De esta manera obtendrá una mejor distribución entre los directorios en lugar de, con el ejemplo de letras iniciales, "da" está muy lleno y "zz" está completamente vacío. Por ejemplo, si toma el nombre de CRC o MD5 y usa los primeros 8 bits, obtendrá algo como:

top_level_dir
|---00
|   |---some_username
|   |---some_username
|---01
|   |---some_username
...
|---FF
|   |---some_username

Esto puede extenderse a profundidades adicionales según sea necesario, por ejemplo, si usa el nombre de usuario no un valor hash:

top_level_dir
|---a
|   |---a
|       |---aardvark1
|       |---aardvark2
|---d
    |---a
    |   |---dan
    |   |---david
    |---o
        |---don

Este método se usa en muchos lugares, como el caché del calamar, para copiar el ejemplo de Ludwig y los cachés locales de los navegadores web.

Una cosa importante a tener en cuenta es que con ext2 / 3 comenzará a tener problemas de rendimiento antes de acercarse al límite de 32,000 de todos modos, ya que los directorios se buscan linealmente. Pasar a otro sistema de archivos (ext4 o reiser, por ejemplo) eliminará esta ineficiencia (reiser busca directorios con un algoritmo dividido en binario para que los directorios largos se manejen de manera mucho más eficiente, ext4 también puede hacerlo), así como el límite fijo por directorio.

David Spillett
fuente
Acabo de actualizar la descripción de la pregunta para incluir esto: "Actualización: ¿Qué hay de dividir la base de usuarios en carpetas basadas en el rango de ID de usuario? Significa 1-1000 en una carpeta, 1000-2000 en la otra de esa manera. Esto parece ser simple. ¿tú dices?"
None-da
1
Eso funcionaría bien y sería más eficiente que un hash, si los usuarios generalmente se identifican por ID de usuario en lugar de (o también) nombre de usuario. Sin embargo, si siempre se refiere a ellos por nombre en otra parte del sistema, tendrá que agregar búsquedas adicionales de nombre-> id en todo el lugar.
David Spillett
¡Gracias David! Intenté incluso una solución diferente. Creé apenas 4 carpetas con el rango 1-30000, 30000-60000, etc. Creo que obtener un archivo de un directorio tan grande llevará más tiempo que un directorio que tiene 1000 archivos (enfoque anterior). ¿Qué dices?
None-da
1
Eso depende del sistema de archivos. Si está usando ext2 o ext3, le recomendaría mucho menos de 30,000 por directorio. Algunas herramientas emiten advertencias sobre 10,000. Puede activar la indexación de directorios en ext3 / 4 para ayudar: tune2fs -O dir_index / dev / <volumename> pero solo mantener el número de objetos en un directorio más bajo (¿un par de miles o menos?) Es lo que recomendaría aquí .
David Spillett el
@Maddy, desea esta solución debido a otras limitaciones sobre cómo Ext2 / 3 maneja grandes cantidades de archivos. Ver serverfault.com/questions/43133/… para algunos detalles. Desglosar los nombres en cubos como subdirectorios alivia otros problemas con los que eventualmente se habría encontrado. Tenga en cuenta que esta es la misma estrategia que Squid usa cuando configura el caché de objetos por primera vez, por ejemplo, 64 directorios cada uno con 64 directorios dentro de ellos, solo como un ejemplo.
Avery Payne
7

Si está obligado a ext2 / ext3, la única posibilidad que veo es particionar sus datos. Encuentre un criterio que divida sus datos en fragmentos manejables de tamaño similar.

Si solo se trata de las imágenes de perfil que haría:

  1. Utilice un hash (por ejemplo, SHA1) de la imagen.
  2. Use el SHA1 como nombre de archivo y directorio

Por ejemplo, el caché SQUID lo hace de esta manera:

f / 4b / 353ac7303854033

El directorio de nivel superior es el primer dígito hexadecimal, el segundo nivel son los siguientes dos dígitos hexadecimales y el nombre del archivo son los dígitos hexadecimales restantes.

Ludwig Weinzierl
fuente
2

¿No podemos tener una mejor solución?

Tiene una solución mejor: use un sistema de archivos diferente, hay muchos disponibles, muchos de los cuales están optimizados para diferentes tareas. Como señaló, ReiserFS está optimizado para manejar muchos archivos en un directorio.

Vea aquí para una comparación de sistemas de archivos.

Solo alégrate de no estar atascado con NTFS, lo que es realmente abismal para muchos archivos en un directorio. Recomiendo JFS como reemplazo si no te gusta usar el relativamente nuevo (pero aparentemente estable) ext4 FS.

gbjbaanb
fuente
¿Tiene buenos enlaces al rendimiento del sistema de archivos NTFS?
Thorbjørn Ravn Andersen
sí, aparte de la experiencia personal con una aplicación que se dejó demasiado tiempo creando nuevos archivos en un directorio ... (tomó horas eliminarlos a todos), y el rendimiento de subversión aumentó al limitar el número de archivos en un directorio a 1000. O lea : support.microsoft.com/kb/130694 No creo que alguna vez "arreglen" esto, ya que todavía se nota como un rendimiento. ajustar para NTFS.
gbjbaanb
1

¿Es pequeña la imagen de perfil? ¿Qué hay de ponerlo en la base de datos con el resto de los datos del perfil? Puede que esta no sea la mejor opción para ti, pero vale la pena considerarla ...

Aquí hay un documento técnico de Microsoft (anterior) sobre el tema: BLOB o no BLOB .

Kyle Brandt
fuente
1

He pirateado una pequeña galería web, donde terminé con una variación de este problema; "Solo" tenía ~ 30,000 imágenes en el directorio de caché, que resultó ser bastante lento (ext2 usa listas vinculadas para los índices de directorio, según recuerdo).

Terminé haciendo algo en este sentido:

def key2path(key):
    hash = md5(key)
    return os.path.join(hash[0], hash[1], key)

Esto dividirá los datos en 256 directorios, lo que proporciona una búsqueda rápida de directorios para cada uno de los tres niveles.

  • Elegí usar MD5 sobre SHA-1, ya que MD5 garantiza una salida diferente si cambia cualquiera de 12 bits de 32, por lo que me parece un buen ajuste para los nombres de usuario hash, directorios y otras cosas cortas. Y también es rápido ...
  • No incluyo el hash completo, ya que producirá demasiados directorios y efectivamente destruirá el caché del disco una y otra vez.
Morten Siebuhr
fuente
1
Probablemente podría usar un hash más simple como CRC, ya que el hash no necesita ser criptográficamente fuerte como MD5 o SHA ... pero la diferencia de rendimiento probablemente sea insignificante de todos modos ...
sleske
0

No es una respuesta inmediata a su problema, pero hay que tener en cuenta para futuras referencias el proyecto vinculado OpenBSD llamado 'Epitome'

Epitome es un motor que proporciona almacenamiento de instancia única, almacenamiento de contenido direccionable y servicios de deduplicación.

Todos sus datos se almacenan en un almacén de datos como bloques hash, eliminando bloques no únicos para reducir el uso del espacio, y le permite esencialmente olvidarse del mecanismo de almacenamiento, ya que simplemente puede solicitar el contenido del almacén de datos por UUID.

Epitome es actualmente experimental, pero es algo para mirar en el futuro.

Mugir
fuente
0

En general, desea evitar tener directorios con una gran cantidad de archivos / directorios. La razón principal es que la expansión de comodines en la línea de comando dará como resultado errores de "Demasiados argumentos" que resultarán en mucho dolor al intentar trabajar con estos directorios.

Busque una solución que haga un árbol más profundo pero más angosto, por ejemplo, creando subcarpetas como otras han descrito.

Thorbjørn Ravn Andersen
fuente
0

Tuvimos un problema similar, la solución, como se mencionó anteriormente, es crear una jerarquía de directorios.

Por supuesto, si tiene una aplicación compleja que se basa en una estructura de directorio plana, probablemente necesitará muchos parches. Por lo tanto, es bueno saber que hay una solución alternativa, use enlaces simbólicos que no tengan el límite de 32k mencionado. Entonces tienes tiempo de sobra para arreglar la aplicación ...

Karoly Horvath
fuente
0

¿Por qué no utilizar un enfoque de marca de tiempo y luego tener una opción de desbordamiento?

Por ejemplo

Digamos que su marca de tiempo es: 1366587600

Omita los últimos 2 dígitos (o simplemente se vuelve un poco ridículo). Separe el sello en conjuntos de 4 (el recuento de directorios no debe alcanzar más de 9999; si lo desea, puede separarlo de manera diferente).

Esto debería dejarte con algo como esto:

/files/1366/5876/

Luego, también verifique la cantidad dentro del directorio antes de cargar, si está recibiendo una gran cantidad de cargas (es decir, 32000 + por 100 segundos), luego itere el directorio por la segunda o una letra, por ejemplo:

/files/1366/5876/a/file.txt

o

/files/1366/5876/00/file.txt

Luego, registre la marca de tiempo + letra o el código de ruta completo en una base de datos junto con el usuario y debería estar configurado.

sello de ruta: 1366587600 o 13665876a (si usa letras).

Esto termina con una gran cantidad de directorios, pero puede ser realmente útil para manejar revisiones de archivos. Por ejemplo, si un usuario desea usar una nueva imagen de perfil, aún tiene la versión antigua con la marca de tiempo de la anterior en caso de que desee deshacer los cambios (no solo se sobrescribe).

Fireacer
fuente
0

Sugeriría decidir cuántos subdirectorios máximos desea (o puede) tener en la carpeta principal.

Luego debe convertir su identificación de usuario para que comiencen desde 1.

Entonces puedes hacer: modulo = currentId % numberOfSubdirectories

moduloahora contendrá su número de subdirectorio que nunca será mayor de lo numberOfSubdirectoriesque ha elegido.

Haz lo que quieras con el módulo, hash, por ejemplo.

También de esta manera los subdirectorios se llenarán linealmente.

vitro
fuente