database - manejo de archivos en php y mysql
Manteniendo mi base de datos y sistema de archivos sincronizados (3)
Estoy trabajando en un software que almacena archivos en un sistema de archivos, así como referencias a esos archivos en una base de datos. Por lo tanto, la consulta de los archivos cargados se puede hacer en la base de datos sin tener que acceder al sistema de archivos. Por lo que he leído en otras publicaciones, la mayoría de la gente dice que es mejor usar un sistema de archivos para el almacenamiento de archivos en lugar de almacenar datos binarios directamente en una base de datos como BLOB.
Así que ahora estoy tratando de entender la mejor manera de configurar esto para que tanto la base de datos como el sistema de archivos se mantengan sincronizados y no termine con referencias a archivos que no existen, o archivos que ocupan espacio en el Sistema de archivos que no son referenciados. Aquí hay un par de opciones que estoy considerando.
Opción 1: Agregar referencia de archivo primero
//Adds a reference to a file in the database
database.AddFileRef("newfile.txt");
//Stores the file in the file system
fileStorage.SaveFile("newfile.txt",dataStream);
Esta opción sería problemática porque la referencia al archivo se agrega antes que el archivo real, por lo que otro usuario puede terminar intentando descargar un archivo antes de que realmente se almacene en el sistema. Aunque, dado que la referencia al archivo se crea de antemano, el valor de la clave principal podría usarse al almacenar el archivo.
Opción 2: Almacenar archivo primero
//Stores the file
fileStorage.SaveFile("newfile.txt",dataStream);
//Adds a reference to the file in the database
//fails if reference file does not existing in file system
database.AddFileRef("newfile.txt");
Esta opción es mejor, pero permitiría a alguien cargar un archivo en el sistema al que nunca se hace referencia. Aunque esto podría remediarse con una función "Purgar" o "CleanUpFileSystem" que borra cualquier archivo sin referencia. Esta opción tampoco permitiría que el archivo se almacenara utilizando el valor de clave principal de la base de datos.
Opción 3: Estado pendiente
//Adds a pending file reference to database
//pending files would be ignored by others
database.AddFileRef("newfile.txt");
//Stores the file, fails if there is no
//matching pending file reference in the database
fileStorage.SaveFile("newfile.txt",dataStream); database
//marks the file reference as committed after file is uploaded
database.CommitFileRef("newfile.txt");
Esta opción permite que se cree la clave principal antes de cargar el archivo, pero también evita que otros usuarios obtengan una referencia a un archivo antes de que se cargue. Sin embargo, es posible que un archivo nunca se cargue y que una referencia de archivo quede pendiente. Sin embargo, también sería bastante trivial purgar las referencias pendientes de la base de datos.
Me inclino por la opción 2, porque es simple y no tengo que preocuparme por los usuarios que intentan solicitar archivos antes de que se carguen. El almacenamiento es barato, por lo que no es el fin del mundo si termino con algunos archivos sin referencia que ocupan espacio. Pero esto también parece ser un problema común, y me gustaría escuchar cómo otros lo han resuelto u otras consideraciones que debería estar haciendo.
¿Cuál es el uso real de la base de datos? Si es solo una lista de archivos, no creo que lo necesites en absoluto, y no tenerlo te ahorra la molestia de sincronizar.
Si está convencido de que lo necesita, entonces las opciones 1 y 2 son completamente idénticas desde un punto de vista técnico: los 2 recursos pueden no estar sincronizados y necesita un proceso regular para consolidarlos nuevamente. Así que aquí debes elegir las opciones que mejor se adapten a la aplicación.
La opción 3 no tiene ninguna ventaja, pero usa más recursos.
Tenga en cuenta que el uso de hashes, como lo sugiere usr, conlleva un riesgo teórico de colisión. Y también necesitaría un proceso de consolidación periódico, como para las opciones 1 y 2.
Otra pregunta es cómo lidiar con las cargas parciales y las cargas en curso. Aquí la opción 2 podría ser útil, pero también podría usar un segundo archivo de "bandera" que se crea antes del inicio de la carga y que se elimina cuando se realiza la carga. Esto te ayudaría a determinar qué subidas han sido abortadas.
Para remediar el inconveniente que mencionó de la opción 1 , uso algo como fileStorage.FileExists("newfile.txt");
y filtrar el resultado para el que devuelve un negativo.
En la jerga de Python
:
import os
op = os.path
filter(lambda ref: op.exists(ref.path()), database.AllRefs())
Quiero proponer otra opción. Haga que el nombre del archivo sea siempre igual al hash de su contenido. Luego, puede escribir cualquier contenido de forma segura en todo momento siempre que lo haga antes de agregar una referencia a él en otro lugar.
Como los contenidos nunca cambian nunca hay un problema de sincronización.
Esto le da deduplicación de forma gratuita. Sin embargo, las eliminaciones se vuelven más difíciles. Recomiendo un proceso nocturno de recolección de basura.