Как родилась эта заметка
Я работаю инженером в 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 в облаке означает, что вы с достаточно высокой вероятностью подвергаете свои данные риску. Вы точно хотите получить повышение производительности ценой таких рисков?
Комментарии (34)
aamonster
22.10.2019 17:45+1Э… Я правильно понял, что в облаке, даже не выставляя "nobarrier" у своей vm — не можешь быть уверен, что он не выставлен уровнем выше?
gecube
22.10.2019 17:59Абсолютно верно.
outlingo Автор
22.10.2019 18:20Не совсем. Таки сервис-провайдеры достаточно разумны чтобы совсем уж в экстрим не влезать
gecube
22.10.2019 18:24Разные есть сервис провайдеры. Есть крупные, к которым клиенты идут, потому что они крупные. А есть мелкие, региональные. У которых даже своего ДЦ и нет, но клиенту деваться некуда — он не готов переплачивать *3 (опять же вспомню сравнения хетцнер вс амазон).
К тому же, проверить провайдера на то, что у него корректно реализован весь стек хранения… Попросту невозможно. Остается доверять. И это… Мы еще не говорим про самые обыкновенные баги в ПО...
outlingo Автор
22.10.2019 18:07При построении облака провайдер сервиса понимает все эти факторы, поэтому экстремальных кейсов у него вряд ли будет — то есть посланый flush гипервизор нормально отадресует на сторадж, поэтому если вы отправили в виртуальной машину flush на диск и получили на него ответ — то данные сохранены. Главное, чтобы ваша виртуальная машина этот flush сделала.
akhkmed
22.10.2019 18:01+1Спасибо за статью, очень важную тему подняли.
Бытует мнение, что nobarrier допустим и оправдан для контроллеров с батарейным кешем. Как по-вашему, для серверных ssd с суперконденсатором он тоже работает без потерь данных?gecube
22.10.2019 18:03Мне кажется, что вопрос не верен. Серверные ssd с супер конденсатором и с, и без nobarrier ничего не гарантируют. Я уж не говорю о ситуации, когда накопитель после аварийного выключения просто превращается в тыкву из-за ошибки в фирмваре, например. Слава Богу, такое не каждый день происходит
akhkmed
22.10.2019 18:39Как заверяет маркетинг, суперконденсатор как раз и должен спасти от потери данных (при условии, что всё остальное не криво и не сломано).
outlingo Автор
22.10.2019 18:11В физической среде — да, SSD с power loss protection хватает. В виртуальной среде не факт, нужно чтобы все уровни корректно отработали. То есть если вы подадите такой SSD в виртуальную машину и укажете io=threads + cache=writeback|unsafe то nobarrier в гостевой машине создает риски, а если cache=writethrough|none или гостевая система нормально выдает flush — то опять всё становится нормально
akhkmed
22.10.2019 18:43Как понимаю, всё зависит от того, как подать SSD. И тут безопасный, но непрактичный вариант, как понимаю — отдать его целиком, как устройство.
Не могли бы порекомендовать другие безопасные, но более практичные способы «подачи»?outlingo Автор
22.10.2019 19:24Если у вас SSD с PLP, то тут скорее дело в настройках гипервизора. Несовместимые настройки это nobarrier в виртуалке в сочетании с cache=writeback или cache=unsafe на гипервизоре. Ну и вообще cache=unsafe — он аналогичен безальтернативно включенному nobarrier в гостевой машине. Всё остальное работает нормально.
yleo
22.10.2019 18:49+1Проще и сложнее одновременно:
- От приложения до условной поверхности диска у вас могут быть несколько посредников: какой-нибудь asyncio-фреймворк, ядро, гипервизор, кеширующий контроллер с батарейкой или без, контроллер диска с батарейкой/конденсатором или без.
- Если хотя-бы кто-то из посредников "без батарейки" пере-упорядочивает запись, то целостность данных не гарантируется.
gecube
22.10.2019 18:02Так рекомендации какие? Я для себя вижу так.
- Обращение в техпод облака. Да, мы узнаем настройки в моменте, но они могут поменяться в любой момент, и мы об этом не узнаем.
- Строить распределенной систему с репликацией и пр. При этом мы теряем в быстродействии — rtt никто не отменял.
- Использовать managed решения. Те же БД. Они "как бы" гарантируют определенной уровень целостности и у нас не болит голова о низкоуровневых вещах вроде nobarrier.
- Ваш вариант
outlingo Автор
22.10.2019 18:15Любой вариант на ваше усмотрение — все имеют право на жизнь. Если с инфраструктурой не хочется заморочек — managed DB. Если надо чего то необычного от БД типа установки нетиповых расширений — то вариант 2. А техподдержка ответит «не надо отключать nobarrier, это категорически не рекомендуется»
akhkmed
22.10.2019 18:35Есть ещё и такая сторона вопроса: если ты уже сидишь на виртуалке в чужом облаке, по каким-то признакам можно понять, что всё настроено правильно и данные не пропадут из-за кривых настроек гипервизора?
yleo
22.10.2019 18:51Никак, кроме как попросить посадить вас на медленные HDD и мониторить latency ;)
outlingo Автор
22.10.2019 19:32+1Понять невозможно.
Можно предположить что «что-то не так» если виртуалка показывает нереально хорошие цифры в коротком тесте fio --rw=randwrite --runtime=1 --direct=1 и те же цифры с fio --rw=randwrite --runtime=1 --direct=1 --fsync=1. Естественно, тестировать напрямую на блочном устройстве а не на файле на файловой системе, то есть тест «деструктивный».
Но даже тогда достоверно сказать что «меня кидают» нельзя — может вашу машину положили на супер-пупер SSD локально на гипервизоре.
amarao
23.10.2019 10:12+1поправки:
1) unsafe в qemu разрешает ему игнорировать запросы на синхронизацию (даже когда они приходят).
2) SSD, которая может сделать 2000 flush'ей в секунду (во время записи) — это крутая энтерпрайзная SSD. Консьюмерские выдают порядка 100 или даже меньше.outlingo Автор
23.10.2019 13:42Самые убогие которые я видел меньше 250 не выдавали, а хоть сколь-нибудь приемлемые 500-600 и выше. Микрон который сейчас в десктопе отдает ~1000
amarao
23.10.2019 14:44А как вы бенчмаркали? Выше вы приводили хорошую строчку, вот её полная версия:
fio --rw=randwrite --runtime=1 --direct=1 --fsync=1 --iodepth=32 --filename=/test --size 4G--ioengine=libaio
Важно запускать её на файловую систему, а не на блочное устройство, потому что на блочное устройство не проходил fsync когда я проверял это.outlingo Автор
23.10.2019 15:24Без синка 36K IOPS: fio direct.fio --filename=/dev/mailbox/xxx --rw=randwrite --iodepth=1 --runtime=30
С синком 1K IOPS: fio direct.fio --filename=/dev/mailbox/xxx --rw=randwrite --iodepth=1 --runtime=30 --fsync=1 --fdatasync=1amarao
23.10.2019 16:04fsync/fdatasync взаимоисключающие (fdatasync облегчённая версия fsync). В списке ключей ещё добавить
--direct=1
и--blocksize=4k
. Ваша цифра выглядит довольно крутой, и я не думаю, что какие-то устройства покажут сильно больше без мухлежа с writeback'ом.outlingo Автор
23.10.2019 17:14При тесте на устройстве что fsync, что fdatasync равнозначны — результаты одни и те же. Мы взяли за основную практику не делать тестов поверх ФС, чтобы не добавлять оверхеда и спецэффектов от файловой системы.
amarao
23.10.2019 21:28+1У вас получилось сделать SCSI-команду SYNCHRONIZE CACHE при использовании
--fsync=1
для блочных устройств? Я вот, сколько не смотрел scsi-логгинг, ни разу не сумел добиться.
Я пребываю в уверенности, что без использования файловой системы заставить делать SYNCHRONIZE CACHE с помощью fio не получится. Если кто-то найдёт как — я буду очень благодарен.
outlingo Автор
25.10.2019 13:30+1Не занимались этим. Ограничились тем, что убедились что выставляется pre-flush на запросы — тем более, что у нас virtio-blk а не virtio-scsi
amarao
25.10.2019 20:46+1Так вот, вы уверены, что fio по блочному устройству хоть что-то такое отправляет и оно транслируется уровнем ниже? У меня большие сомнения.
outlingo Автор
25.10.2019 21:35+1Я внимательно прочёл документацию к ядру в Documentation/block касающуюся writeback cache, запустил fio без синков и с синками и действительно увидел обещание поведение когда на устройство отправляются пустые bio с флагом pre-flush — поэтому да, я уверен что запросы на сброс кэша на устройство отправляются. Не вижу причин не доверять увиденному
amarao
25.10.2019 22:05+1Окей, я перепроверю. В тот момент у меня оказалось, что fio на блочном устройстве с fsync не может воспроизвести нагрузку от ceph'а на OSD (т.к. на OSD идёт много flush'ей). Возможно, либо я сделал там какую-то ошибку, либо что-то с тех пор поменялось.
yleo
Не надо такие статьи писать, это сильно мешает естественному отбору (а он — мудрый)!
;)
outlingo Автор
Ну, отбор отбором, а людей таки слегка жалко.
justhabrauser
А сисадмины не люди?
Кто же их еще кормить будет?