Oberon space

General Category => Общий раздел => Тема начата: Губанов Сергей Юрьевич от Август 28, 2012, 01:57:31 pm

Название: Велосипедная СУБД
Отправлено: Губанов Сергей Юрьевич от Август 28, 2012, 01:57:31 pm
Однажды понадобилось иметь персистентную коллекцию неких записей. В процессе работы программы записи могут изменяться, удаляться, добавляться. Держать для неё отдельную СУБД - это как из пушки по воробъям, так что просто завёл файл. Встал вопрос как организовать с ним работу. Я выделил три условия:

1) С одной стороны размер этого файла не очень гигантский в том смысле, что при старте программа зачитывает его целиком в оперативку. Пусть программа стартует медленно, зато работает быстро.

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

3) Если во время записи в файл произойдёт авария (выключение питания, падение программы), то данные не должны серьёзно испортиться. При рестарте программа перечитывая файл должна сама исправить ошибки в файле.

Хотелось бы узнать кто как поступил бы в этой ситуации.

Я организовал работу с этим файлом так.
Каждая запись снабжается уникальным идентификатором. Данные в файле представляют собой набор команд двух типов:
1) добавить или перезаписать запись с таким-то идентификатором такими-то значениями,
2) удалить запись с таким-то идентификатором.
Программа стартует, читает файл и выполняет команды; после выполнения всех команд в оперативной памяти получается итоговый набор записей. Когда в процессе работы программы какая-то запись удаляется, то в конец того файла дописывается команда "удалить запись с таким-то идентификатором". Когда какая-то запись добавляется или изменяется, то в конец файла дописывается команда "добавить или перезаписать запись с таким-то идентификатором такими-то значениями". То есть во время работы программы в файл дописываются только изменения -- это очень быстро (правда занимает больше места на диске). Важно что запись всегда идёт только в конец файла. Если во время записи данных в файл произойдёт авария, то при рестарте программа дочитав до этого места обнаружит ошибку в формате данных и перезапишет файл автоматически удалив эту ошибку. Ошибка может быть только в конце файла! Если при старте программа прочитав файл обнаруживает, что количество команд в нём более чем в два раза превышает итоговое количество записей, то она перезапишет файл (тем самым уменьшит его размер более чем в 2 раза). Перезапись осуществляется естественно сначала во временный файл, а затем выполняется (как бы атомарная) замена старого файла новым.
Название: Re: Велосипедная СУБД
Отправлено: Geniepro от Август 28, 2012, 02:10:00 pm
Однажды понадобилось иметь персистентную коллекцию неких записей. В процессе работы программы записи могут изменяться, удаляться, добавляться. Держать для неё отдельную СУБД - это как из пушки по воробъям, так что просто завёл файл.
...
Хотелось бы узнать кто как поступил бы в этой ситуации.

А чем вас не устроила SQLite (http://www.sqlite.org/index.html)? Она ведь как раз для подобных целей разработана...
Конфигурации не требует, привязка простейшая, надёжность хранения данных высокая...
Название: Re: Велосипедная СУБД
Отправлено: valexey от Август 28, 2012, 03:04:59 pm
Хотелось бы узнать кто как поступил бы в этой ситуации.
Я бы посмотрел на jdbm3/4 (https://github.com/jankotek/JDBM3). Если бы у меня была java, я бы это использовал as is, если бы что-то другое, я бы поковырялся в исходниках на предмет интересующих меня деталей реализации.

Это работает всяко быстрее чем любая реляционная СУБД.
Название: Re: Велосипедная СУБД
Отправлено: Губанов Сергей Юрьевич от Август 28, 2012, 03:09:58 pm
Да, кстати, запись команд в конец файла делаю не при каждом изменении данных, а не чаще чем два раза в секунду. Чаще сохранять особого смысла нет так как у других частей системы латентность больше.
Название: Re: Велосипедная СУБД
Отправлено: valexey от Август 28, 2012, 03:22:49 pm
Да, кстати, запись команд в конец файла делаю не при каждом изменении данных, а не чаще чем два раза в секунду. Чаще сохранять особого смысла нет так как у других частей системы латентность больше.
Запись в конец файла можно делать каждый раз, а вот fflush делать раз в секунду/две а на fsync забить вообще.
Название: Re: Велосипедная СУБД
Отправлено: ilovb от Август 28, 2012, 03:45:10 pm
Ваше описание, Сергей, напоминает ранние версии Btrieve (http://ru.wikipedia.org/wiki/Btrieve)

add Тут описание: http://emanual.ru/download/5195.html (http://emanual.ru/download/5195.html)
Название: Re: Велосипедная СУБД
Отправлено: ilovb от Август 28, 2012, 04:21:44 pm
А еще возможно Microsoft Compound подойдет
http://www.openoffice.org/sc/compdocfileformat.pdf (http://www.openoffice.org/sc/compdocfileformat.pdf)

В винде вроде даже API для работы с ними есть. Да и самописные библиотеки думаю можно найти
Название: Re: Велосипедная СУБД
Отправлено: Geniepro от Август 28, 2012, 04:59:59 pm
Не понимаю, зачем все эти велосипеды -- jdbm-ы всякие, компаунды...
Есть же промышленный стандарт для таких вещей -- SQLite -- чуть ли не в каждом смартфоне, на большинстве компов установлена -- мало кто об этом просто знает...
Название: Re: Велосипедная СУБД
Отправлено: ilovb от Август 28, 2012, 05:09:44 pm
В общем да. SQLite наверно лучший вариант.

Но если хочется самому написать, то я бы делал что-то вроде Btrieve :)
Название: Re: Велосипедная СУБД
Отправлено: alexus от Август 28, 2012, 05:20:58 pm
Я организовал работу с этим файлом так.
Каждая запись снабжается уникальным идентификатором. Данные в файле представляют собой набор команд двух типов:
1) добавить или перезаписать запись с таким-то идентификатором такими-то значениями,
2) удалить запись с таким-то идентификатором.
Программа стартует, читает файл и выполняет команды; после выполнения всех команд в оперативной памяти получается итоговый набор записей. Когда в процессе работы программы какая-то запись удаляется, то в конец того файла дописывается команда "удалить запись с таким-то идентификатором". Когда какая-то запись добавляется или изменяется, то в конец файла дописывается команда "добавить или перезаписать запись с таким-то идентификатором такими-то значениями". То есть во время работы программы в файл дописываются только изменения -- это очень быстро (правда занимает больше места на диске). Важно что запись всегда идёт только в конец файла. Если во время записи данных в файл произойдёт авария, то при рестарте программа дочитав до этого места обнаружит ошибку в формате данных и перезапишет файл автоматически удалив эту ошибку. Ошибка может быть только в конце файла! Если при старте программа прочитав файл обнаруживает, что количество команд в нём более чем в два раза превышает итоговое количество записей, то она перезапишет файл (тем самым уменьшит его размер более чем в 2 раза). Перезапись осуществляется естественно сначала во временный файл, а затем выполняется (как бы атомарная) замена старого файла новым.
Посмотрите на Firebird в embedded варианте. Полноценная СУБД... в Вашей программе для 32- и 64-битного варианта (http://www.firebirdsql.org/en/firebird-2-5/).
Название: Re: Велосипедная СУБД
Отправлено: valexey от Август 28, 2012, 06:07:13 pm
Не понимаю, зачем все эти велосипеды -- jdbm-ы всякие, компаунды...
Есть же промышленный стандарт для таких вещей -- SQLite -- чуть ли не в каждом смартфоне, на большинстве компов установлена -- мало кто об этом просто знает...
Потому, что sqlite во-первых жуткий тормоз, во-вторых дико сложен (относительно поставленной задачи), в-третьих не thread safe.

Любая sql-СУБД жуткий тормоз относительно jdbm и ему подобным.
Название: Re: Велосипедная СУБД
Отправлено: Илья Ермаков от Август 28, 2012, 06:31:51 pm
По поводу решения Сергея:

когда-то сделал за вечер версионное хранилище контента для Web-сервера на таком же принципе. Клиент отправлял из настольного редактора обновление контента на некоторый URI (по своему протоколу сообщений), сервер заменял node в памяти и дописывал в файл-журнал. При запуске сервер прогонял журнал.
Название: Re: Велосипедная СУБД
Отправлено: valexey от Август 28, 2012, 06:44:11 pm
По поводу решения Сергея:

когда-то сделал за вечер версионное хранилище контента для Web-сервера на таком же принципе. Клиент отправлял из настольного редактора обновление контента на некоторый URI (по своему протоколу сообщений), сервер заменял node в памяти и дописывал в файл-журнал. При запуске сервер прогонял журнал.
Ну, вообще такой "журнал" называется write ahead log, и активно применяется во всяких там mysql и подобных для обеспечения отказоусточивости/транзакционности. (в сам файл базы странички скидываются в весьма ленивом темпе, а вот в этот лог пишутся сразу (+fsync каждые несколько секунд)). Лог не бесконечен по объему, и по сути своей является неким аналогом ring buffer'a.
Название: Re: Велосипедная СУБД
Отправлено: Илья Ермаков от Август 28, 2012, 07:02:59 pm
Или наборот: ваш write-ahead-log называется журнал с опережающей записью, если по-русски. :) Я его обычно называю дельта-журнал.
Название: Re: Велосипедная СУБД
Отправлено: valexey от Август 28, 2012, 07:40:29 pm
Или наборот: ваш write-ahead-log называется журнал с опережающей записью, если по-русски. :) Я его обычно называю дельта-журнал.
Опережающая что? :-)

Если действительно по русски, то это "упреждающая регистрация (записываемых данных)".
Название: Re: Велосипедная СУБД
Отправлено: Губанов Сергей Юрьевич от Август 29, 2012, 10:54:46 am
Не народ, я спрашивал всего лишь о том как организовать безопасную сохранку в файл...  :) :) :)

Оверхедные тормоза SQL мне даром не нужны (даже с доплатой не нужны). Так же мне совершенно не нужна симуляция бесконечной персистентной памяти (jdbm). Структура данных у меня должна быть "под рукой" всегда целиком в оперативке и мгновенна доступна по указателю (или индексу массива), а на диске должна быть её копия со средним запаздыванием примерно в половину секунды.

Что касается безопасной сохранки в файл, то видимо это и есть самое то:
1) Пишем только в конец файла. Если во время записи происходит авария, то портится только конец файла.
2) Когда файл становится слишком большим, копируем (с компактификацией) в другой файл, заменяем старый файл новым, старый выбрасываем.
Название: Re: Велосипедная СУБД
Отправлено: ilovb от Август 29, 2012, 11:00:48 am
Ну почему же? Вот valexey говорил про write ahead log. Оно и есть.

мокрософт копаунд примерно так и устроен. Там тоже пишется безопасно в конец. Это ж тупо контейнер с поддержкой транзакций. Микро файловая система.

Не обязательно использовать как есть. Но можно идеи скомуниздить...
Название: Re: Велосипедная СУБД
Отправлено: ilovb от Август 29, 2012, 11:11:52 am
Где то в тырнетах было описание... не помню где.
Вот люди тоже говорят:
Цитировать
Думаю стоит уточнить, что формат 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)
Название: Re: Велосипедная СУБД
Отправлено: albobin от Август 29, 2012, 11:53:40 am
В линухах есть logrotate для автоматизации работы с log-файлами.

Название: Re: Велосипедная СУБД
Отправлено: Peter Almazov от Август 29, 2012, 12:05:55 pm
Не народ, я спрашивал всего лишь о том как организовать безопасную сохранку в файл...  :) :) :)
Недостаток предложенного решения - избыточность данных в файле, даже после компрессии. Так как читая его, программа способна определить где был сбой.
Если на избыточность можно наплевать - то все и так хорошо. Если нет, то сделать 2 файла, в одном хранить данные в двоичном виде, в другом лог. Периодически лог обнулять.