LINUX.ORG.RU

[СИ] База данных

 


0

0

[СИ] База данных

Язык СИ
ОС UNIX

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

Много чего не понимаю. Вот первая тестовая задача:

Задача 1.

Имеется файл-счетчик count.txt малой длины (100 байт).
и некая программа, время от времени наращивающая
этот счетчик.
Требуется: переделать так, чтобы при мягком сбое (отключение питания)
не потерять счетчик.

Думал - думал и ничего лучше не придумал,
кроме следующего.

Создаю два равнозначных файла-счетчика,
дублирующих друг друга.

count_1.txt
count_2.txt

Формат файла такой
count=4; st_tr=36; ks=123345
где
count=4; -основной счетчик;
st_tr=36; -вспомогательный кольцевой счетчик наращиваний,
например, после 99 следующее число 0;
ks=123345 -контрольная сумма.

Наращивание делать так:

-читаем каждый из этих файлов, проверяем контрольные суммы
и совпадение данных в обоих файлах;

Далее по одной из четырех ветвей

-если все в порядке, то изменяем данные и перезаписываем
эти два файла. Дождать, когда они окажутся на диске. Вот и всё.
-если файлы отличаются, и в одном из них правильная ks,
то восстановить, т. е. скопировать правильный в не правильный,
дождать, когда он
окажется на диске (как проверить - не ведомо), затем
нарастить счетчики в обоих файлах.
-если файлы отличаются, и в каждом правильная ks, лучшим из них
считать тот, у которого больше (по кольцу) вспомогательный
счетчик, сделать восстановление, затем нарастить оба.
-другие варианты считать не подлежащими восстановлению.

В чем я заблуждаюсь?

Кто знает прошу ответить.



Если вам нужно сохранять какие-то структуры на диске, воспользуйтесь функцией mmap: как только в отображаемой в файл области памяти что-то изменится, тут же произойдет перезапись содержимого файла.

Для хранения большого количества однотипных данных я делал следующее: создал структуру, содержащую нужные мне поля, и по мере поступления новых данных, заполнял структуру и записывал ее на диск. Ну, а чтобы в гигабайтных файлах ускорить поиск для клиентской программы (которая рисует графики и выполняет всякие статистические расчеты), в отдельный файл записывались смещения для каждой 1024-й структуры данных и идентификатор этой структуры (т.е. здесь также используется структура с двумя полями: long long - смещение, time_t - временнóй идентификатор).

Программа-клиент, когда ей необходимо вывести данные за определенный промежуток времени, сначала в кэш-файле определяет смещение для примерного начала временнóго диапазона, затем делает lseek на это смещение в файле с данными и ищет нужные. Так доступ значительно ускоряется (чем если бы мы перебирали гигабайт-другой данных, чтобы найти что-нибудь из хвоста).

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от ipc

Спасибо за отклик.

Но всё-же не понятно.

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

mmap
Перезапись содержимого произойдет где?
Скорее всего в буфере, но не на диске.

Про fsync знаю, но его опорочили тем, что якобы
он способствует сбросу системных буферов
на дисковод, но не на диск ещё. И там в современных
дисководах якобы есть большие буферы, где и оказываются
данные. А когда они попадут собственно на диск, об
этом знает только дисковод, но не система.
Об этом я прочитал в статье, но ссылку не дам (не помню).

И в любом случае сбой (отключение питания) произойдет
именно в момент записи на диск. Как я понимаю, в этом
случае данные на диске могут быть
-записаны новые
-остаться старые
-содержать мусор
Допустим, что fsync работает так, как нам нужно,
т. е. при возврате данные гарантированно на диске.
Хотелось бы узнать критику на моё решение конкретной задачи 1.

Спасибо.

oleg_2
() автор топика
Ответ на: комментарий от oleg_2

mmap

Перезапись содержимого произойдет где? Скорее всего в буфере, но не на диске.

На диске. Проверено (когда приложение, едва успев изменить данные в отображаемой области памяти, убивается по kill -9).

И в любом случае сбой (отключение питания) произойдет именно в момент записи на диск.

Купите UPS :)

Хотите надежности, можно еще так сделать: в отдельный файл записывать всего одно число: 0 или 1. До того, как мы начинаем изменять содержимое файла с данными, в контрольный файл пишем 0, после изменения данных, пишем туда 1. В итоге, если произойдет сбой в момент записи, по 0 в контрольном файле вы узнаете, что последняя порция данных нарушена, и ее надо удалить.

Eddy_Em ☆☆☆☆☆
()

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

azure ★★
()
Ответ на: комментарий от azure

Оно рушится не только при некорректном выключении питания, но и при некорректном завершении приложения (segmentation fault). Восстанавливать слишком геморно — надо сдампить базу, всё что не попротилось сдампится, потом в пустую базу загрузить дамп.

Из встраиваемых баз лучше взять berkeleydb, оно хоть и не SQL, но зато к сбоям в отличие от ... устойчива.

Reset ★★★★★
()
Ответ на: комментарий от Eddy_Em

> Если вам нужно сохранять какие-то структуры на диске, воспользуйтесь функцией mmap: как только в отображаемой в файл области памяти что-то изменится, тут же произойдет перезапись содержимого файла.

Если я запишу 2 байта в ммапнутую память, они атомарно запишутся? Даже если они поперёк границы блока? Ничего волшебного mmap не сделает, лучше уж write и fsync юзать, но для нормального решения задачи (ACID по сути) и этого мало.

const86 ★★★★★
()
Ответ на: комментарий от oleg_2

> Про fsync знаю, но его опорочили тем, что якобы он способствует сбросу системных буферов на дисковод, но не на диск ещё.

Это решается выключением буфера на диске, а лучше - покупкой бесперебойника. По-другому никак, fsync - это самое убойное, что есть в ОС.

const86 ★★★★★
()
Ответ на: комментарий от const86

Спасибо всем.

И попробую ответить тоже всем.

Eddy_Em ссылается на kill -9.
Не согласен.
Файл, отображенный в память, это всё равно файл.
И если рушится процесс, то файл не обязан тоже
рушится, где бы он не находился.
С вторым его предложением о нуле и единице тоже не согласен,
т. к. сбой произойдет именно в момент записи на диск
этого нуля или единицы и я потеряю контрольный файл.
Задача 1 это и есть в некотором смысле задача о контрольном файле.

По велосипеду и другим решениям.
В старте поставлена конкретная тестовая задача и приведено
даже конкретное предполагаемое решение (хорошее или нет).
Там, правда, ещё упоминается о базе данных.
Но представте на минуту, что это не тестовая, а конечная
целевая задача для некоего приложения.
Выглядит как будто просто. Что же? Нельзя ли самому решить
эту, казалось бы, простую задачу? Ну хоть попробовать.

const86 пишет про fsync.
Согласен.
Правда я не умею отключить буфер, да и не позволят.
Но это только часть задачи. А всё остальное?
Два файла, контрольные суммы, метод?

Хотелось бы еще критики.

Спасибо.

oleg_2
() автор топика
Ответ на: комментарий от oleg_2

Забыл еще про бесперебойник.
Не годится. Машина-сервер в дата-центре.
Там есть. Но всякое может случится.

oleg_2
() автор топика
Ответ на: комментарий от Eddy_Em

>На диске. Проверено (когда приложение, едва успев изменить данные в отображаемой области памяти, убивается по kill -9).

Ты сильно удивишься, но даже при гибели от 11-го сигнала «грязные» страницы будут записаны на диск, файлы и сокеты закрыты, память, занимаемая кодом и данными приложения освобождены. Одно только «но»: при внезапном исчезновении питания всего этого не будет, так что использовать mmap в данном случае не стоит.

linuxfan
()

Если можно делать два файла, то халява: просто дописываешь новое значение счетчика в файл в любом формате фиксированной длины (например, fprintf(fcount, «%08x», count)). В случае, если дозапись в файл увеличивает его размер более максимально допустимого, переименовываешь count.txt в count.txt.old (переименование файла в рамках файловой системы является атомарной операцией) и начинаешь писать count.txt по новой.

Правильное значение извлекается элементарно: наибольшее их последних значений в count.txt и count.txt.old по максимальному «правильному» (делящемуся нацело на размер записи) смещению.

linuxfan
()
Ответ на: комментарий от linuxfan

Спасибо.

Но не всё понятно.
Пусть длина записи 4 байта и максимальная длина
файла 100 байт. И пусть уже записано 10 записей (40 байт).
И пусть вместо fprintf() будет
write();
fsync();
А что произойдет, если при дозаписи 11-ой записи
в самый неподходящий момент отключат питание?
Что станется с этим файлом.
Я прошу ответить на этот вопрос, т. к. он самый важный.
Как я понимаю, в этом
случае данные на диске могут быть
-записаны новые
-остаться старые
-содержать мусор.
Если я правильно понимаю, пусть тога файл count.txt
будет содержать мусор (самый плохой случай).
Согласен. Есть еще файл count.txt.old.
Но что мне вернет функция
k=read(fd_count_txt, buf, sizeof(buf));
и как я (вернее программа) узнаю, что файл испорчен?
Вот самый важный вопрос-что вернет функция?
Если она вернет, например, 44 или 80 байт мусора, то не годится.

Прошу ответить.


oleg_2
() автор топика
Ответ на: комментарий от oleg_2

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

MikeDM ★★★★★
()
Ответ на: комментарий от oleg_2

Да ты просто в пучине отчаянья, потому-что производители дисков так и норовят записать мусор поверх твоих любимых данных.

Импрувед версия от linuxfan:

Дублируешь запись очередного значения, то есть файл выглядит как:

1
1
2
2
3
3

Легче всего так и делать строковое представление числа через \n.

Вытаскивание правильного значения даже после сбоя тривиальна.

baverman ★★★
()

ИМХО, у oleg_2 паранойя какая-то. Такую проверку на все и вся стоит делать только для систем реального времени, управляющими какими-нибудь ядерными реакторами :)

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от ddos3

Спасибо ddos3 за ссылку.

При моем английском я не быстро осилю это,
но спасибо.

oleg_2
() автор топика
Ответ на: комментарий от Eddy_Em

Любой компьютер иногда выключается аварийно в результате сбоя програм, сбоя железа, ошибок пользователя, и т.д. Если бы разработчики ПО полагались на удачу в вопросе хранения данных, то мы бы все уже давно были приучены к регулярным бекапам ;) (вспоминаем частые уходы множества файлов и/или всей ФС после жесткого ребута в досе и ранних виндах).

Так что олег_2 все правильно делает.

ddos3
()
Ответ на: комментарий от oleg_2

>Что станется с этим файлом.

Ничего страшного: запись не допишется полностью. Дозапись в конец файла очень хорошая операция: она не затрагивает уже имеющиеся корректные данные (исключение: посыпалась файловая система).

linuxfan
()
Ответ на: комментарий от baverman

> Дублируешь запись очередного значения...

... и после жесткого ребута обнаруживаешь, что потерялся весь сектор целиком (а в силу специфики, данные обычно теряются секторами, а не строками) и унес на йух обе копии. да.

ddos3
()
Ответ на: комментарий от linuxfan

дозапись в конец файла перезаписывает целиком последний физический сектор и _теоретически_ может данные из этого сектора похерить. для небольших файлов - это все содержимое.

ddos3
()
Ответ на: комментарий от ddos3

О дозаписи.
Это кажется сомнительным.
Не пишет несколько байт.
Читает блок, меняет данные (несколько байт).
Пишет весь блок.
Об этом написано в упоминавшейся
ddos3 статье и в других книгах.

oleg_2
() автор топика
Ответ на: комментарий от oleg_2

правильно. и при аварии может весь блок потерять или искаверкать. современные ФС достатьчно умны, чтобы в таких ситуациях откатиться на предыдущую версию файла. Но не все.

ddos3
()
Ответ на: комментарий от const86

По-другому никак, fsync - это самое убойное, что есть в ОС.


лучше уже O_SYNC приписать, чем fsync() дёргать постоянно, или можно про***ть его вообще в нужном месте поставить.

Boy_from_Jungle ★★★★
()
Ответ на: комментарий от ddos3

Я так понял, вы предлагаете ТСу сделать свою журналируемую файловую систему (возможно, со своим самописным RAID1)? Как можно при аварийном выключении потерять безвозвратно блок данных (без восстановления хотя бы предыдущей версии) на более-менее приличной журналируемой ФС?

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

Нет. ТС хочет сделать безопасный атомарный коммит, я стараюсь ему в этом помочь. Остальные комментаторы, судя по комментам, вообще не знают о проблеме безопасного коммита (что весьма прискорбно). Никакая ФС этой проблемы не решает.

ddos3
()
Ответ на: комментарий от ddos3

>... и после жесткого ребута обнаруживаешь, что потерялся весь сектор целиком (а в силу специфики, данные обычно теряются секторами, а не строками) и унес на йух обе копии. да.

А кто тебя заставляет пользоваться FAT'ом? Современные ФС с журналированием страхуют от таких случаев.

linuxfan
()
Ответ на: комментарий от linuxfan

Современные системы могут откатить убитый сектор. Но не решают проблемы безопасного коммита.

ddos3
()

Может заюзать версионную ФС типа nilfs2?

sdio ★★★★★
()

Оставлю свои 5 копеек. :)

Идея с несколькими файлами в принципе работоспособна. Например, как это делается в некоторых «больших» БД: есть группа файлов журнала, есть рабочие файлы и есть группа файлов контроля версий (контрольных точек). В этом случае, принцип работы следующий: все желания по изменениям (пожелание вставить, удалить, изменить данные), которые мы хотим произвести в рабочих файлах, пишутся сначала в журнал (несколько параллельных файлов) в специальном формате. Затем, отдельные процессы извлекают желания из журнала и отдают одному или нескольким процессам database-engine, которые исполняют желания, отмечая контрольными точками границы транзакций. Отдельно следует упомянуть процессы организации пользовательского ввода-вывода данных и обеспечения сессионности работы пользователей. Действия асинхронные. Целостность данных на уровне БД контролируется в рамках транзакций (контрольных точек), состояние которых отслеживается в контрольных файлах (этот механизм становится актуальным при многопользовательской параллельной работе).

Проблемы поднятых и измененных несинхронизированных с диском буферов блоков данных частично решается за счёт пре-логгирования желаний, + контрольными точками (транзактивность изменений), частично особой блочной структурой (с контрольными суммами) основных рабочих файлов данных (реализация зависит от особенностей операционной системы и применяемой файловой системы), частично за счёт дублирования данных в основных рабочих файлах БД. Основной рабочий термин — «транзакция».

В твоём случае, IMHO, при синхронной последовательной работе следует опасаться потери одного файла (в случае отсутствия проблем буферизации на уровне файловой системы и контроллера диска, иначе придётся буфера принудительно и затратно намеренно «вымывать»). Очень аккуратно следует разбирать структуру файлов, ибо, в случае совсем неудачного сбоя на некоторых файловых системах в сбойном файле можно будет обнаружить кусок другого файла, соответственно твои «count=4; st_tr=36; ks=123345» переменные при считывании могут оказаться оооочень длинными. А в случае асинхронной параллельной работы с файлами можно с незавидной регулярностью периодически ловить на принимающей стороне неполные (частично заполненные) или пустые состояния файлов.

:-)

Neksys ★★★
()
Ответ на: комментарий от Eddy_Em

> Как можно при аварийном выключении потерять безвозвратно блок данных (без восстановления хотя бы предыдущей версии) на более-менее приличной журналируемой ФС?

ФС может обнулить мелкий файлик? :)

Neksys ★★★
()
Ответ на: комментарий от ddos3

> ТС хочет сделать безопасный атомарный коммит, я стараюсь ему в этом помочь. Остальные комментаторы, судя по комментам, вообще не знают о проблеме безопасного коммита (что весьма прискорбно). Никакая ФС этой проблемы не решает.

Боюсь, что у ТС проблема не в безопасном атомарном COMMIT/ROLLBACK, а в асинхронном бардаке записи-чтения файла данных разными процессами.

:-)

Neksys ★★★
()
Ответ на: комментарий от ddos3

и после жесткого ребута обнаруживаешь, что потерялся весь сектор целиком

Ой, не надо только паранойю разводить. Постгрес вообще надеется на атомарность записи страницы (да-да, не одного сектора, а целой страницы), со стороны ОС, файловой системы и железа.

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

ТС: не заморачивайся. Современные железяки способны при отключении питания атомарно записать сектор.

baverman ★★★
()
Ответ на: комментарий от Neksys

в асинхронном бардаке записи-чтения файла данных разными процессами.

Для этого есть всякие flock и семафоры.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Neksys

> Боюсь, что у ТС проблема не в безопасном атомарном COMMIT/ROLLBACK, а в асинхронном бардаке записи-чтения файла данных разными процессами.

атомарный коммит решает, в том числе, и эту проблему

ddos3
()
Ответ на: комментарий от baverman

> Современные железяки способны при отключении питания атомарно записать сектор.

далеко не все современные железяки на это способны.

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

Чтобы вам было понятнее, приведу пример: открываем файл на сотню МБ, читаем часть данных, меняем, начинаем писать обратно. После записи сорока метров из измененных пятидесяти происходит сбой. Файл испорчен.

ddos3
()

а если так: для этой специальной задачи взять флешку минимального объёма, разбить ее на 2 раздела, монтировать с отключением буферизации и писать в каждый раздел свой файл
так удобно и тестировать будет - просто вытащить флешку

x905 ★★★★★
()
Ответ на: комментарий от ddos3

Чтобы вам было понятнее, приведу пример: открываем файл на сотню МБ, читаем часть данных, меняем, начинаем писать обратно. После записи сорока метров из измененных пятидесяти происходит сбой. Файл испорчен.

Сюсюкаться со мной не надо.

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

baverman ★★★
()
Ответ на: комментарий от baverman

страница памяти обычно имеет объем в 4КБ. Как можно организовать безопасную транзакцию на десятки и сотни мегабайт, полагаясь _только_ на атомарную запись страницы?

ddos3
()
Ответ на: комментарий от ddos3

Очень просто. RAID + UPS. Нэ? Это гораздо дешевле, чем поддерживать в приложении код, ответственный за durability.

Более, того, если данные важны, то в принципе нет такой беды, как power loss. А если нет проблемы, то зачем козе баян?

baverman ★★★
()
Ответ на: комментарий от ddos3

полагаясь _только_ на атомарную запись страницы?

Кстати, а что трудного в организации какой-угодно по объему транзакции, если есть предположительно атомарный способ записать некоторую порцию данных?

baverman ★★★
()
Ответ на: комментарий от baverman

Сбои бывают разные. Может, например, сгореть блок питания. Или материнская плата. Тут вас ИБП не спасет.

ddos3
()
Ответ на: комментарий от ddos3

> атомарный коммит решает, в том числе, и эту проблему

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

:-)

Neksys ★★★
()
Ответ на: комментарий от baverman

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

ddos3
()
Ответ на: комментарий от Neksys

склайт имеет свою имплементацию, на описание которой я уже давал ссылку.

ddos3
()
Ответ на: комментарий от ddos3

Сбои бывают разные. Может, например, сгореть блок питания. Или материнская плата. Тут вас ИБП не спасет.

Master-slave репликация. Шах и мат.

baverman ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.