О вреде неправильных оптимизаций

в 13:53, , рубрики: database, filesystems, linux, virtualization

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

Как родилась эта заметка

Я работаю инженером в Mail.Ru Cloud Solutions и занимаюсь в основном всякими вопросами «вокруг и около» блочного хранилища, на котором лежат виртуальные машины наших пользователей — и, соответственно, часто возникают интересные кейсы, связанные с производительностью и стабильностью виртуальных машин и запущенных в них приложений — и особенно баз данных.

Как правило, почти в половине случаев при «разборе полетов» мы видим одно и то же — файловую систему, смонтированную с опцией nobarrier. И когда мы спрашиваем — «а зачем вы написали эту опцию», то почти всегда получаем один из вариантов ответа «мне сказали что так быстрее / я прочитал что так быстрее / мне так настроили» — после чего мы вежливо и осторожно пытаемся объяснить, что ТАК делать не надо. Почему? Потому, что это первый уверенный шаг по дороге к потере данных.

Краткий экскурс

Файловая система — структура весьма сложная и высоконагруженная. Для обеспечения максимальной производительности в процессе работы активно используется кэширование и параллельное выполнение записи. Соответственно, часть данных попадает в кэш и сбрасывается по мере возможности/необходимости или «по требованию». Барьер (barrier) это специальная операция принудительного сброса всех кэшей на диск.

Когда речь идет о базах данных, мы должны быть уверены, что транзакция подтвержденная клиенту (клиентскому приложению) была персистирована и никуда не пропадет, с одной стороны, а с другой — СУБД активно используют собственное кэширование для достижения максимальной производительности — а чтобы обеспечить консистентность, используется журналирование — изменение пишется в журнал, журнал «синкается» и затем изменение пишется в данные (и при записи оно попадает в кэш). Когда журнал заполняется, делается принудительный sync всем данным лежащим в кэше и журнал начинает заполняться повторно.

Операция sync

При выполнении sync операционная система не только сбрасывает page cache, но по умолчанию отправляет команду на сброс всех кэшей диска (и, возможно, делает это неоднократно) — т.н. flush. Операция сброса буферов «дорогая» и занимает существенное время — но она необходима, поскольку в файловых системах порядок выполнения записи важен — если он будет нарушен, то (например) может получиться так, что при внезапной перезагрузке в файле вместо данных окажется мусор — поскольку устройство решило переупорядочить запись. А когда flush принудительно сбрасывают все кэши — то это гарантирует, что сначала запишется то что было до flush, и только затем то что было после него — то есть создается «барьер» разделяющий записи на «до барьера» и «после барьера» (отсюда и название «barrier write») — и это дает возможность гарантировать, что записи после барьера не окажутся примененными ранее чем записи до барьера.

Влияние nobarrier

Опция nobarrier отключает отправку принудительных flush в процессе работы файловой системы. Это приводит к тому, что записи могут быть переупорядочены — и если происходит сбой, то файловая система (и вообще данные в общем случае) могут оказаться неконсистентны — вспомним о чем говорилось в предыдущем абзаце насчет порядка записи.

Зачем эту опцию включают? Для недорогих SSD операция flush оказывается очень дорогой — например, недорогие SSD (да и многие дорогие, позиционируемые как «серверные») без flush выполняют по 10-20 тысяч операций записи в секунду, а с включенным flush падают до 1-2 тысяч. В такие случая nobarrier дает существенный прирост производительности, создавая описанные выше риски для целостности данных.

Виртуальная среда

В случае с виртуальной машиной — если, например, мы говорим о классической конфигурации виртуальных машин на линуксовом гипервизоре, у нас появляется QEMU — процесс, который собственно и отвечает за эмуляцию I/O для гостевой операционной системы. И самое важное — если мы используем не file-backed диски в виртуальной машине, то кэш такого виртуального диска (внезапно!?) лежит в юзерспейсе — в адресном пространстве соответствующего процесса QEMU. И если этот процесс упадет — например по SEGFAULT/SIGSEGV — то все его кэши умрут вместе с ним. Пример такого драйвера блочного устройства это драйвер RBD (Ceph).

И даже если у вас используется не Ceph а например iSCSI/FC — то уровень отказа не исчезает — он просто смещается от QEMU к операционной системе хоста (гипервизора). Упал гипервизор — умер его page cache (это актуально для io='threads' в сочетании с cache='writeback' или cache='unsafe'). Упс.

s/Облако/Чужой компьютер/g

Когда ваша виртуальная машина задеплоена в облако… Тогда вы не знаете, как настроен гипервизор, как настроен QEMU, какие драйверы дисков задействованы, работает ли page cache хоста и т.д., и не можете на это повлиять в подавляющем большинстве случаев. И даже если это ваше облако — где вы всё это знаете и более-менее контролируете, то совершенно не факт что ваш гипервизор не «упадёт» — похоронив при этом весь кэш с данными.

Резюме

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

Автор: outlingo

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js