Oberon space
General Category => Общий раздел => Тема начата: Губанов Сергей Юрьевич от Август 28, 2012, 01:57:31 pm
-
Однажды понадобилось иметь персистентную коллекцию неких записей. В процессе работы программы записи могут изменяться, удаляться, добавляться. Держать для неё отдельную СУБД - это как из пушки по воробъям, так что просто завёл файл. Встал вопрос как организовать с ним работу. Я выделил три условия:
1) С одной стороны размер этого файла не очень гигантский в том смысле, что при старте программа зачитывает его целиком в оперативку. Пусть программа стартует медленно, зато работает быстро.
2) С другой стороны его размер достаточно велик для того чтобы при изменении одной или нескольких записей перезаписывать файл целиком сбрасывая все записи из оперативки на диск. Жесткий диск может интенсивно использоваться другими программами работающими на этом же компьютере, поэтому полная перезапись файла может занять несколько секунд.
3) Если во время записи в файл произойдёт авария (выключение питания, падение программы), то данные не должны серьёзно испортиться. При рестарте программа перечитывая файл должна сама исправить ошибки в файле.
Хотелось бы узнать кто как поступил бы в этой ситуации.
Я организовал работу с этим файлом так.
Каждая запись снабжается уникальным идентификатором. Данные в файле представляют собой набор команд двух типов:
1) добавить или перезаписать запись с таким-то идентификатором такими-то значениями,
2) удалить запись с таким-то идентификатором.
Программа стартует, читает файл и выполняет команды; после выполнения всех команд в оперативной памяти получается итоговый набор записей. Когда в процессе работы программы какая-то запись удаляется, то в конец того файла дописывается команда "удалить запись с таким-то идентификатором". Когда какая-то запись добавляется или изменяется, то в конец файла дописывается команда "добавить или перезаписать запись с таким-то идентификатором такими-то значениями". То есть во время работы программы в файл дописываются только изменения -- это очень быстро (правда занимает больше места на диске). Важно что запись всегда идёт только в конец файла. Если во время записи данных в файл произойдёт авария, то при рестарте программа дочитав до этого места обнаружит ошибку в формате данных и перезапишет файл автоматически удалив эту ошибку. Ошибка может быть только в конце файла! Если при старте программа прочитав файл обнаруживает, что количество команд в нём более чем в два раза превышает итоговое количество записей, то она перезапишет файл (тем самым уменьшит его размер более чем в 2 раза). Перезапись осуществляется естественно сначала во временный файл, а затем выполняется (как бы атомарная) замена старого файла новым.
-
Однажды понадобилось иметь персистентную коллекцию неких записей. В процессе работы программы записи могут изменяться, удаляться, добавляться. Держать для неё отдельную СУБД - это как из пушки по воробъям, так что просто завёл файл.
...
Хотелось бы узнать кто как поступил бы в этой ситуации.
А чем вас не устроила SQLite (http://www.sqlite.org/index.html)? Она ведь как раз для подобных целей разработана...
Конфигурации не требует, привязка простейшая, надёжность хранения данных высокая...
-
Хотелось бы узнать кто как поступил бы в этой ситуации.
Я бы посмотрел на jdbm3/4 (https://github.com/jankotek/JDBM3). Если бы у меня была java, я бы это использовал as is, если бы что-то другое, я бы поковырялся в исходниках на предмет интересующих меня деталей реализации.
Это работает всяко быстрее чем любая реляционная СУБД.
-
Да, кстати, запись команд в конец файла делаю не при каждом изменении данных, а не чаще чем два раза в секунду. Чаще сохранять особого смысла нет так как у других частей системы латентность больше.
-
Да, кстати, запись команд в конец файла делаю не при каждом изменении данных, а не чаще чем два раза в секунду. Чаще сохранять особого смысла нет так как у других частей системы латентность больше.
Запись в конец файла можно делать каждый раз, а вот fflush делать раз в секунду/две а на fsync забить вообще.
-
Ваше описание, Сергей, напоминает ранние версии Btrieve (http://ru.wikipedia.org/wiki/Btrieve)
add Тут описание: http://emanual.ru/download/5195.html (http://emanual.ru/download/5195.html)
-
А еще возможно Microsoft Compound подойдет
http://www.openoffice.org/sc/compdocfileformat.pdf (http://www.openoffice.org/sc/compdocfileformat.pdf)
В винде вроде даже API для работы с ними есть. Да и самописные библиотеки думаю можно найти
-
Не понимаю, зачем все эти велосипеды -- jdbm-ы всякие, компаунды...
Есть же промышленный стандарт для таких вещей -- SQLite -- чуть ли не в каждом смартфоне, на большинстве компов установлена -- мало кто об этом просто знает...
-
В общем да. SQLite наверно лучший вариант.
Но если хочется самому написать, то я бы делал что-то вроде Btrieve :)
-
Я организовал работу с этим файлом так.
Каждая запись снабжается уникальным идентификатором. Данные в файле представляют собой набор команд двух типов:
1) добавить или перезаписать запись с таким-то идентификатором такими-то значениями,
2) удалить запись с таким-то идентификатором.
Программа стартует, читает файл и выполняет команды; после выполнения всех команд в оперативной памяти получается итоговый набор записей. Когда в процессе работы программы какая-то запись удаляется, то в конец того файла дописывается команда "удалить запись с таким-то идентификатором". Когда какая-то запись добавляется или изменяется, то в конец файла дописывается команда "добавить или перезаписать запись с таким-то идентификатором такими-то значениями". То есть во время работы программы в файл дописываются только изменения -- это очень быстро (правда занимает больше места на диске). Важно что запись всегда идёт только в конец файла. Если во время записи данных в файл произойдёт авария, то при рестарте программа дочитав до этого места обнаружит ошибку в формате данных и перезапишет файл автоматически удалив эту ошибку. Ошибка может быть только в конце файла! Если при старте программа прочитав файл обнаруживает, что количество команд в нём более чем в два раза превышает итоговое количество записей, то она перезапишет файл (тем самым уменьшит его размер более чем в 2 раза). Перезапись осуществляется естественно сначала во временный файл, а затем выполняется (как бы атомарная) замена старого файла новым.
Посмотрите на Firebird в embedded варианте. Полноценная СУБД... в Вашей программе для 32- и 64-битного варианта (http://www.firebirdsql.org/en/firebird-2-5/).
-
Не понимаю, зачем все эти велосипеды -- jdbm-ы всякие, компаунды...
Есть же промышленный стандарт для таких вещей -- SQLite -- чуть ли не в каждом смартфоне, на большинстве компов установлена -- мало кто об этом просто знает...
Потому, что sqlite во-первых жуткий тормоз, во-вторых дико сложен (относительно поставленной задачи), в-третьих не thread safe.
Любая sql-СУБД жуткий тормоз относительно jdbm и ему подобным.
-
По поводу решения Сергея:
когда-то сделал за вечер версионное хранилище контента для Web-сервера на таком же принципе. Клиент отправлял из настольного редактора обновление контента на некоторый URI (по своему протоколу сообщений), сервер заменял node в памяти и дописывал в файл-журнал. При запуске сервер прогонял журнал.
-
По поводу решения Сергея:
когда-то сделал за вечер версионное хранилище контента для Web-сервера на таком же принципе. Клиент отправлял из настольного редактора обновление контента на некоторый URI (по своему протоколу сообщений), сервер заменял node в памяти и дописывал в файл-журнал. При запуске сервер прогонял журнал.
Ну, вообще такой "журнал" называется write ahead log, и активно применяется во всяких там mysql и подобных для обеспечения отказоусточивости/транзакционности. (в сам файл базы странички скидываются в весьма ленивом темпе, а вот в этот лог пишутся сразу (+fsync каждые несколько секунд)). Лог не бесконечен по объему, и по сути своей является неким аналогом ring buffer'a.
-
Или наборот: ваш write-ahead-log называется журнал с опережающей записью, если по-русски. :) Я его обычно называю дельта-журнал.
-
Или наборот: ваш write-ahead-log называется журнал с опережающей записью, если по-русски. :) Я его обычно называю дельта-журнал.
Опережающая что? :-)
Если действительно по русски, то это "упреждающая регистрация (записываемых данных)".
-
Не народ, я спрашивал всего лишь о том как организовать безопасную сохранку в файл... :) :) :)
Оверхедные тормоза SQL мне даром не нужны (даже с доплатой не нужны). Так же мне совершенно не нужна симуляция бесконечной персистентной памяти (jdbm). Структура данных у меня должна быть "под рукой" всегда целиком в оперативке и мгновенна доступна по указателю (или индексу массива), а на диске должна быть её копия со средним запаздыванием примерно в половину секунды.
Что касается безопасной сохранки в файл, то видимо это и есть самое то:
1) Пишем только в конец файла. Если во время записи происходит авария, то портится только конец файла.
2) Когда файл становится слишком большим, копируем (с компактификацией) в другой файл, заменяем старый файл новым, старый выбрасываем.
-
Ну почему же? Вот valexey говорил про write ahead log. Оно и есть.
мокрософт копаунд примерно так и устроен. Там тоже пишется безопасно в конец. Это ж тупо контейнер с поддержкой транзакций. Микро файловая система.
Не обязательно использовать как есть. Но можно идеи скомуниздить...
-
Где то в тырнетах было описание... не помню где.
Вот люди тоже говорят:
Думаю стоит уточнить, что формат Compound file был создан специально для хранения составных OLE-документов и предоставляет собой подобие файловой системы внутри одного файла.
К тому же, API предоставляет возможность поддержки транзакций при работе с Compound file и назначение CLSID для отдельных хранилищ (Storage) внутри документа.
Многие функции API имеют названия типа Stgxxxx и работают со ссылками на интерфейсы IRootStorage (корневое хранилище), IStorage (вложеное хранилище), IStream (поток данных).
Стоит также посмотреть справку по интерфейсу ILockBytes, который позволяет размещать корневое хранилище составного документа на диске, в памяти или в БД.
http://www.delphikingdom.com/asp/answer.asp?IDAnswer=24094 (http://www.delphikingdom.com/asp/answer.asp?IDAnswer=24094)
-
В линухах есть logrotate для автоматизации работы с log-файлами.
-
Не народ, я спрашивал всего лишь о том как организовать безопасную сохранку в файл... :) :) :)
Недостаток предложенного решения - избыточность данных в файле, даже после компрессии. Так как читая его, программа способна определить где был сбой.
Если на избыточность можно наплевать - то все и так хорошо. Если нет, то сделать 2 файла, в одном хранить данные в двоичном виде, в другом лог. Периодически лог обнулять.