Когда дело доходит до оптимизации системы для достижения максимальной производительности, можно очень легко наделать ошибок, если бездумно применять чужие практики. Одной из таких практик является указание 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 в облаке означает, что вы с достаточно высокой вероятностью подвергаете свои данные риску. Вы точно хотите получить повышение производительности ценой таких рисков?

Комментарии (34)


  1. yleo
    22.10.2019 17:29
    +1

    Не надо такие статьи писать, это сильно мешает естественному отбору (а он — мудрый)!


    ;)


    1. outlingo Автор
      22.10.2019 17:36
      +1

      Ну, отбор отбором, а людей таки слегка жалко.


      1. justhabrauser
        23.10.2019 01:07

        А сисадмины не люди?
        Кто же их еще кормить будет?


  1. aamonster
    22.10.2019 17:45
    +1

    Э… Я правильно понял, что в облаке, даже не выставляя "nobarrier" у своей vm — не можешь быть уверен, что он не выставлен уровнем выше?


    1. gecube
      22.10.2019 17:59

      Абсолютно верно.


      1. outlingo Автор
        22.10.2019 18:20

        Не совсем. Таки сервис-провайдеры достаточно разумны чтобы совсем уж в экстрим не влезать


        1. gecube
          22.10.2019 18:24

          Разные есть сервис провайдеры. Есть крупные, к которым клиенты идут, потому что они крупные. А есть мелкие, региональные. У которых даже своего ДЦ и нет, но клиенту деваться некуда — он не готов переплачивать *3 (опять же вспомню сравнения хетцнер вс амазон).
          К тому же, проверить провайдера на то, что у него корректно реализован весь стек хранения… Попросту невозможно. Остается доверять. И это… Мы еще не говорим про самые обыкновенные баги в ПО...


    1. outlingo Автор
      22.10.2019 18:07

      При построении облака провайдер сервиса понимает все эти факторы, поэтому экстремальных кейсов у него вряд ли будет — то есть посланый flush гипервизор нормально отадресует на сторадж, поэтому если вы отправили в виртуальной машину flush на диск и получили на него ответ — то данные сохранены. Главное, чтобы ваша виртуальная машина этот flush сделала.


  1. akhkmed
    22.10.2019 18:01
    +1

    Спасибо за статью, очень важную тему подняли.
    Бытует мнение, что nobarrier допустим и оправдан для контроллеров с батарейным кешем. Как по-вашему, для серверных ssd с суперконденсатором он тоже работает без потерь данных?


    1. gecube
      22.10.2019 18:03

      Мне кажется, что вопрос не верен. Серверные ssd с супер конденсатором и с, и без nobarrier ничего не гарантируют. Я уж не говорю о ситуации, когда накопитель после аварийного выключения просто превращается в тыкву из-за ошибки в фирмваре, например. Слава Богу, такое не каждый день происходит


      1. akhkmed
        22.10.2019 18:39

        Как заверяет маркетинг, суперконденсатор как раз и должен спасти от потери данных (при условии, что всё остальное не криво и не сломано).


    1. outlingo Автор
      22.10.2019 18:11

      В физической среде — да, SSD с power loss protection хватает. В виртуальной среде не факт, нужно чтобы все уровни корректно отработали. То есть если вы подадите такой SSD в виртуальную машину и укажете io=threads + cache=writeback|unsafe то nobarrier в гостевой машине создает риски, а если cache=writethrough|none или гостевая система нормально выдает flush — то опять всё становится нормально


      1. akhkmed
        22.10.2019 18:43

        Как понимаю, всё зависит от того, как подать SSD. И тут безопасный, но непрактичный вариант, как понимаю — отдать его целиком, как устройство.
        Не могли бы порекомендовать другие безопасные, но более практичные способы «подачи»?


        1. outlingo Автор
          22.10.2019 19:24

          Если у вас SSD с PLP, то тут скорее дело в настройках гипервизора. Несовместимые настройки это nobarrier в виртуалке в сочетании с cache=writeback или cache=unsafe на гипервизоре. Ну и вообще cache=unsafe — он аналогичен безальтернативно включенному nobarrier в гостевой машине. Всё остальное работает нормально.


    1. yleo
      22.10.2019 18:49
      +1

      Проще и сложнее одновременно:


      • От приложения до условной поверхности диска у вас могут быть несколько посредников: какой-нибудь asyncio-фреймворк, ядро, гипервизор, кеширующий контроллер с батарейкой или без, контроллер диска с батарейкой/конденсатором или без.
      • Если хотя-бы кто-то из посредников "без батарейки" пере-упорядочивает запись, то целостность данных не гарантируется.


  1. gecube
    22.10.2019 18:02

    Так рекомендации какие? Я для себя вижу так.


    1. Обращение в техпод облака. Да, мы узнаем настройки в моменте, но они могут поменяться в любой момент, и мы об этом не узнаем.
    2. Строить распределенной систему с репликацией и пр. При этом мы теряем в быстродействии — rtt никто не отменял.
    3. Использовать managed решения. Те же БД. Они "как бы" гарантируют определенной уровень целостности и у нас не болит голова о низкоуровневых вещах вроде nobarrier.
    4. Ваш вариант


    1. outlingo Автор
      22.10.2019 18:15

      Любой вариант на ваше усмотрение — все имеют право на жизнь. Если с инфраструктурой не хочется заморочек — managed DB. Если надо чего то необычного от БД типа установки нетиповых расширений — то вариант 2. А техподдержка ответит «не надо отключать nobarrier, это категорически не рекомендуется»


  1. akhkmed
    22.10.2019 18:35

    Есть ещё и такая сторона вопроса: если ты уже сидишь на виртуалке в чужом облаке, по каким-то признакам можно понять, что всё настроено правильно и данные не пропадут из-за кривых настроек гипервизора?


    1. yleo
      22.10.2019 18:51

      Никак, кроме как попросить посадить вас на медленные HDD и мониторить latency ;)


      1. Sly_tom_cat
        22.10.2019 19:19

        К сожалению это тоже ничего не гарантирует.


    1. outlingo Автор
      22.10.2019 19:32
      +1

      Понять невозможно.

      Можно предположить что «что-то не так» если виртуалка показывает нереально хорошие цифры в коротком тесте fio --rw=randwrite --runtime=1 --direct=1 и те же цифры с fio --rw=randwrite --runtime=1 --direct=1 --fsync=1. Естественно, тестировать напрямую на блочном устройстве а не на файле на файловой системе, то есть тест «деструктивный».

      Но даже тогда достоверно сказать что «меня кидают» нельзя — может вашу машину положили на супер-пупер SSD локально на гипервизоре.


  1. amarao
    23.10.2019 10:12
    +1

    поправки:
    1) unsafe в qemu разрешает ему игнорировать запросы на синхронизацию (даже когда они приходят).
    2) SSD, которая может сделать 2000 flush'ей в секунду (во время записи) — это крутая энтерпрайзная SSD. Консьюмерские выдают порядка 100 или даже меньше.


    1. outlingo Автор
      23.10.2019 13:42

      Самые убогие которые я видел меньше 250 не выдавали, а хоть сколь-нибудь приемлемые 500-600 и выше. Микрон который сейчас в десктопе отдает ~1000


      1. amarao
        23.10.2019 14:44

        А как вы бенчмаркали? Выше вы приводили хорошую строчку, вот её полная версия: fio --rw=randwrite --runtime=1 --direct=1 --fsync=1 --iodepth=32 --filename=/test --size 4G--ioengine=libaio Важно запускать её на файловую систему, а не на блочное устройство, потому что на блочное устройство не проходил fsync когда я проверял это.


        1. 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=1


          1. amarao
            23.10.2019 16:04

            fsync/fdatasync взаимоисключающие (fdatasync облегчённая версия fsync). В списке ключей ещё добавить --direct=1 и --blocksize=4k. Ваша цифра выглядит довольно крутой, и я не думаю, что какие-то устройства покажут сильно больше без мухлежа с writeback'ом.


            1. outlingo Автор
              23.10.2019 17:14

              При тесте на устройстве что fsync, что fdatasync равнозначны — результаты одни и те же. Мы взяли за основную практику не делать тестов поверх ФС, чтобы не добавлять оверхеда и спецэффектов от файловой системы.


              1. amarao
                23.10.2019 21:28
                +1

                У вас получилось сделать SCSI-команду SYNCHRONIZE CACHE при использовании --fsync=1 для блочных устройств? Я вот, сколько не смотрел scsi-логгинг, ни разу не сумел добиться.


                Я пребываю в уверенности, что без использования файловой системы заставить делать SYNCHRONIZE CACHE с помощью fio не получится. Если кто-то найдёт как — я буду очень благодарен.


                1. outlingo Автор
                  25.10.2019 13:30
                  +1

                  Не занимались этим. Ограничились тем, что убедились что выставляется pre-flush на запросы — тем более, что у нас virtio-blk а не virtio-scsi


                  1. amarao
                    25.10.2019 20:46
                    +1

                    Так вот, вы уверены, что fio по блочному устройству хоть что-то такое отправляет и оно транслируется уровнем ниже? У меня большие сомнения.


                    1. outlingo Автор
                      25.10.2019 21:35
                      +1

                      Я внимательно прочёл документацию к ядру в Documentation/block касающуюся writeback cache, запустил fio без синков и с синками и действительно увидел обещание поведение когда на устройство отправляются пустые bio с флагом pre-flush — поэтому да, я уверен что запросы на сброс кэша на устройство отправляются. Не вижу причин не доверять увиденному


                      1. amarao
                        25.10.2019 22:05
                        +1

                        Окей, я перепроверю. В тот момент у меня оказалось, что fio на блочном устройстве с fsync не может воспроизвести нагрузку от ceph'а на OSD (т.к. на OSD идёт много flush'ей). Возможно, либо я сделал там какую-то ошибку, либо что-то с тех пор поменялось.


    1. outlingo Автор
      23.10.2019 14:15

      Насчет cache='unsafe' — да, оно разрешает игнорировать flush'и, просто описывать его логику не показалось необходимым.


      1. amarao
        23.10.2019 14:44

        Я к тому, что если виртуалка запущена в режиме cache-unsafe, дальше уже не важно, есть там barrier или нет.