Поддержание синхронизации моей базы данных и файловой системы

Я работаю над программным обеспечением, которое хранит файлы в файловой системе, а также ссылки на эти файлы в базе данных. Таким образом, запрос загруженных файлов может выполняться в базе данных без доступа к файловой системе. Из того, что я читал в других сообщениях, большинство людей считают, что для хранения файлов лучше использовать файловую систему, чем хранить двоичные данные непосредственно в базе данных как BLOB.

Итак, теперь я пытаюсь понять, как лучше всего настроить это так, чтобы и база данных, и файловая система оставались синхронизированными, и я не получал ссылок на файлы, которые не существуют, или файлы, занимающие место в файловая система, на которую нет ссылок. Вот пара вариантов, которые я рассматриваю.

Вариант 1. Сначала добавьте ссылку на файл

//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); 

Этот вариант будет проблематичным, потому что ссылка на файл добавляется перед фактическим файлом, поэтому другой пользователь может в конечном итоге попытаться загрузить файл до того, как он будет фактически сохранен в системе. Хотя, поскольку ссылка на файл создается заранее, значение первичного ключа можно использовать при сохранении файла.

Вариант 2. Сначала сохранить файл

//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"); 

Этот вариант лучше, но он дает возможность кому-то загрузить в систему файл, на который никогда не ссылаются. Хотя это можно исправить с помощью функции «Purge» или «CleanUpFileSystem», которая удаляет все файлы, на которые нет ссылок. Этот параметр также не позволяет сохранять файл с использованием значения первичного ключа из базы данных.

Вариант 3: статус ожидания

//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"); 

Этот параметр позволяет создать первичный ключ перед загрузкой файла, но также не позволяет другим пользователям получить ссылку на файл до его загрузки. Тем не менее, возможно, что файл никогда не будет загружен, а ссылка на файл зависнет в ожидании. Тем не менее, также было бы довольно тривиально удалить ожидающие ссылки из базы данных.

Я склоняюсь к варианту 2, потому что он прост, и мне не нужно беспокоиться о том, что пользователи пытаются запрашивать файлы до их загрузки. Хранилище дешево, так что это не конец света, если некоторые файлы, на которые нет ссылок, занимают место. Но это тоже похоже на обычную проблему, и я хотел бы услышать, как другие решили ее, или другие соображения, которые я должен сделать.


person Eric Anastas    schedule 15.03.2013    source источник
comment
Очень умный вопрос. Многие люди никогда не задумываются о согласованности между своими разрозненными хранилищами данных.   -  person usr    schedule 15.03.2013


Ответы (3)


Хочу предложить другой вариант. Сделайте имя файла всегда равным хешу его содержимого. Затем вы можете безопасно писать любое содержимое в любое время при условии, что вы это сделаете до того, как добавите ссылку на него в другом месте.

Поскольку содержимое никогда не меняется, проблем с синхронизацией не возникает.

Это дает вам бесплатную дедупликацию. Однако удалять становится сложнее. Я рекомендую производить уборку мусора каждую ночь.

person usr    schedule 15.03.2013
comment
Не могли бы вы уточнить? Я бы получил хэш-код из файла и использовал бы этот код, чтобы определить, как файл хранится в файловой системе? Тогда база данных при этом хранит ссылку на файл как хэш-код, а не имя файла? Разве тогда мне не пришлось бы иметь дело с потенциальными столкновениями? - person Eric Anastas; 15.03.2013
comment
Если вы используете стандартную криптографическую хеш-функцию, вам вообще не нужно иметь дело с коллизиями (в противном случае вы бы уже выиграли в лотерею 10 раз). Старый алгоритм MD5 достаточно хорош, он встроен повсюду и является одним из самых быстрых алгоритмов .; Вы должны сначала определить хэш, затем получить из него имя файла (tohex(hashbytes) + ".dat") и записать его. Затем вы сохраняете хеш (или имя файла) в базе данных. Сделанный. - person usr; 15.03.2013

Каково реальное использование базы данных? Если это просто список файлов, я не думаю, что он вам вообще нужен, и его отсутствие избавляет вас от хлопот с синхронизацией.

Если вы уверены, что вам это нужно, то варианты 1 и 2 полностью идентичны с технической точки зрения - 2 ресурса могут быть не синхронизированы, и вам потребуется регулярный процесс для их повторной консолидации. Итак, здесь вы должны выбрать варианты, которые лучше всего подходят для приложения.

Вариант 3 не имеет никаких преимуществ, но требует больше ресурсов.

Обратите внимание, что использование хешей, предложенное usr, теоретически связано с риском коллизии. И вам также понадобится периодический процесс консолидации, как для вариантов 1 и 2.

Другой вопрос: как вы справляетесь с частичной загрузкой и незавершенной загрузкой. Здесь может быть использован вариант 2, но вы также можете использовать второй файл «флагов», который создается перед началом загрузки и удаляется после завершения загрузки. Это поможет вам определить, какие загрузки были прерваны.

person uselpa    schedule 15.03.2013
comment
В базе данных хранится другая небинарная информация, которая передается в файлы, так что да, она мне нужна. - person Eric Anastas; 15.03.2013

Чтобы исправить упомянутый вами недостаток варианта 1, я использую что-то вроде fileStorage.FileExists("newfile.txt"); и отфильтровываю результат, для которого он возвращает отрицательный результат.

На Python жаргоне:

import os
op = os.path

filter(lambda ref: op.exists(ref.path()), database.AllRefs())
person Bleeding Fingers    schedule 07.08.2013