Что нужно, чтобы быть прочным в Linux?
Я пишу некоторое программное обеспечение для работы с довольно важными данными, и мне нужно знать, что именно мне нужно сделать, чтобы добиться долговечности.
Везде, где я смотрю, есть противоречивая информация, поэтому я буду признателен за любую информацию.
Есть три способа записи на диск.
Использование O_DIRECT | O_DSYNC, а также pread'ing и затем pwrite'ing 512 байт - 16 МБ блоков.
Использование O_DIRECT, предварительная и последующая запись 512-байтовых блоков и регулярный вызов fdatasync по мере необходимости.
Использование файла с отображенной памятью, который я называю msync(..., MS_SYNC | MS_INVALIDATE) так регулярно, как это необходимо.
И это все на ext4 с флагами по умолчанию.
Возможно ли потерять данные (после возвращения записи или синхронизации) или повредить их из-за сбоя питания, паники, сбоя или чего-то еще?
Возможно ли, что если мой сервер умирает в середине pwrite, или между началом pwrite и концом fdatasync, или между измененной отображаемой памятью и msync, у меня будет смесь старых и новых данных, или это будет одна или другой? Я хочу, чтобы мои индивидуальные вызовы pwrite были атомарными и упорядоченными. Это тот случай? И так ли это, если они находятся в нескольких файлах? Так что, если я напишу с O_DIRECT | O_DSYNC в A, затем O_DIRECT | O_DSYNC для B, я гарантирую, что, независимо от того, что произойдет, если данные находятся в B, они также в A?
Гарантирует ли fsync запись данных? Это говорит не, но я не знаю, изменились ли вещи с тех пор.
Полностью ли журналирование ext4 решает проблему поврежденных блоков, которые, как говорит этот SO-ответ, существуют?
В настоящее время я расту файлы, вызывая posix_fallocate, а затем ftruncate. Нужны ли оба из них, и достаточно ли их? Я полагал, что ftruncate фактически инициализирует выделенные блоки, чтобы избежать этих проблем.
Чтобы добавить путаницу в микс, я запускаю это на EC2, я не знаю, влияет ли это на что-нибудь. Хотя это очень затрудняет тестирование, так как я не могу контролировать, насколько агрессивно его отключают.
2 ответа
(2018 год, много лет спустя после того, как этот вопрос был впервые задан)
Что нужно, чтобы быть прочным в Linux?
Читая ваш вопрос, я вижу, что между вами и диском есть файловая система. Таким образом, вопрос становится:
Что нужно, чтобы быть долговечным при использовании файловой системы Linux?
Лучшее, что вы можете сделать (в общем случае с файловой системой и неуказанным аппаратным обеспечением), это " fsync dance ", который выглядит примерно так:
preallocate_file(tmp);fsync(tmp);fsync(dir);rename(tmp, normal);fsync(normal);fsync(dir);
(бесстыдно украденный из комментария Andres Freund (Postgres Developer), оставленного на LWN), и вы должны проверять код возврата каждого вызова, прежде чем продолжить, чтобы убедиться, что он прошел успешно, и предположить, что что-то пошло не так, если какой-либо код возврата вернул ненулевое значение. Если вы используете mmap
затем msync(MS_SYNC)
является эквивалентом fsync
,
Пример, подобный приведенному выше, упоминается в книге Дэна Луу "Файлы трудные" (в которой есть хорошая таблица об атомарности перезаписи различных файловых систем), в статье LWN "Обеспечение доступа к данным на диске" и в статье Теда Цо "Не бойся FSYNC!",
Для всех этих [
O_DIRECT
|O_DSYNC
,O_DIRECT
+fdatasync
,mmap
+msync
], возможно ли потерять данные (после восстановления записи или синхронизации) или повредить из-за сбоя питания, паники, сбоя или чего-то еще?
Да, у вас может быть незаметное повреждение, потому что "распределение записей" из-за увеличения размера файла за его текущие границы может привести к операциям с метаданными, а вы не проверяете долговечность метаданных (только стойкость данных).
если мой сервер умирает в середине pwrite, или между началом pwrite и концом fdatasync, или между изменяемой отображаемой памятью и msync, у меня будет смесь старых и новых данных [и т. д.]
Поскольку состояние данных не определено, в случае прерывания перезаписи это может быть что угодно...
Я хочу, чтобы мои индивидуальные вызовы pwrite были атомарными и упорядоченными. Это тот случай?
Между fsync может произойти переупорядочение (например, если O_DIRECT
молча отступил к буферизации).
случай, если они в нескольких файлах?
У тебя еще больше проблем. Для этого вам нужно написать собственный журнал и, возможно, использовать переименования файлов.
если я напишу с O_DIRECT | O_DSYNC в A, затем O_DIRECT | O_DSYNC для B,
Нет.
Гарантирует ли fsync запись данных?
Да (с современным Linux и правдивым дисковым стеком, предполагающим отсутствие ошибок).
Решает ли журналирование ext4 полностью проблему коррумпированных блоков
Нет.
(ETOOMANYQUESTIONS)
Да, программный стек Linux может быть ошибочным, или аппаратное обеспечение может быть неисправным (или лежать так, как не может выполнить резервное копирование), но это не останавливает вышесказанное, это лучшее, что вы можете сделать, если все подходит к концу. сделка на файловой системе POSIX. Если вы знаете, что у вас есть конкретная ОС с определенной файловой системой (или без файловой системы) и определенной настройкой оборудования, то это правда, что вы можете уменьшить потребность в некоторых из вышеперечисленных, но в целом вам не следует пропускать ни одного шага.
Бонусный ответ: O_DIRECT
одно только не может гарантировать долговечность при использовании с файловыми системами (первоначальная проблема была бы "откуда вы знаете, что метаданные были сохранены?"). См. "Разъяснение семантики Direct IO" в вики Ext4 для обсуждения этого вопроса.
Возможно ли потерять данные (после возврата записи или синхронизации) или повредить их из-за сбоя питания, паники, сбоя или чего-то еще?
Абсолютно.
Гарантирует ли fsync запись данных? Это говорит не, но я не знаю, изменились ли вещи с тех пор.
Нет. Ответ зависит от устройства и, вероятно, зависит от файловой системы. К сожалению, эта файловая система может быть слоями и слоями над "фактическим" устройством хранения. (например md
, lvm
, fuse
, loop
, ib_srp
, так далее).
Хотя это очень затрудняет тестирование, так как я не могу контролировать, насколько агрессивно его отключают.
Это правда. Но вы все еще можете использовать NMI или sysrq-trigger
создать довольно резкую остановку.