Расшифровка доклада 2018 года Ильи Космодемьянского "Последние изменения в IO-стеке Linux с точки зрения DBA"


Проблемы с производительностью ввода-вывода находятся в повседневной повестке дня администраторов баз данных с тех пор, как базы данных существуют. В Linux — вероятно, самой популярной операционной системе для баз данных — в течение последних нескольких лет происходит капитальный ремонт IO-стека.


В этом докладе Илья расскажет о том, что происходит, почему IO-стек нуждается в срочном улучшении, к чему это может привести для баз данных. Как новые драйвера NVMe и blk-mq будут улучшены. В качестве полезной памятки я предложу контрольный список настроек PostgreSQL и Linux, чтобы максимизировать производительность подсистемы ввода-вывода в новых ядрах.




Меня зовут Илья Космодемьянский. Я работаю в компании Data Egret. Мы занимаемся консалтингом, поддержкой Postgres. И наш сегодняшний разговор будет об одном из краеугольных камней, важным для работы с любыми базами данных, а именно про особенности устройства операционной системы, на которой эта штука работает. В нашем случае – это Linux.


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



Во-первых, почему этот доклад и почему он важен, с моей точки зрения? Linux сейчас – это самая важная, самая основная операционная система для баз данных. Т. е. да, в enterprise по-прежнему применяются Solaris, HP-UX, а также достаточно большой кусок занимает Microsoft, что является отдельной историей, потому что это вообще не UNIX-подобная система, там все по-другому. Но поскольку мы все больше используем Open Source’сные базы данных, Linux уверенно завоевывает позиции, как главная операционная система для баз данных.


Почему ввод-вывод? Быстрый ввод-вывод – это самая критичная вещь для администратора баз данных. В CPU можно, конечно, упереться. Особенно, когда раньше машины были без большого количества CPU и это было довольно критичной вещью. В памяти тоже можно много поставить. Но ввод-вывод – это то, что способно изгадить все. Если у вас плохо с дисками, если у вас слишком много ввода-вывода, то ваша база данных будет работать медленно.


Проблема начинается в следующем. Для того чтобы у вас все хорошо заработало, вам надо настроить все. Не только базу данных. Даже Oracle, который очень высокоуровневый и сам практически местами представляет собой операционную систему, если посмотреть installation guide, то там написано: такие-то параметры ядра поменять, такие-то параметры поменять.


Если мы двигаемся от interprise’ных баз данных, типа Oracle в сторону Postgres, MySQL, то там этих изменений надо делать еще больше, потому что все эти технологии опираются на операционную систему, на ее механизмы. DBA, который работает с Postgres или c MySQL, или с современными какими-то noSQL, он в любом случае должен быть Linux operation engineer и должен крутить разные гайки операционной системы.


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


И эта вся штука многократно усложняется тем, что в последнее время, как development ядра Linux пошел быстро, так и переработка его Stack пошла быстро, потому что долгое время – это была такая штука, которая отставала сзади.



Сначала мы поговорим на примере Postgres, поскольку мы все любим Postgres, о том, как база данных взаимодействует с вводом-выводом. И я буду рассказывать о Postgres и буду немножко добавлять деталей о том, как с этим делом работают другие базы данных. Мы немножко по этому делу пройдемся, а потом рассмотрим линуксовый stack ввода-вывода. И потом поговорим о том, что нового и хорошего появилось.



В данной ситуации можно легко догадаться, что мы говорим о PostgreSQL, потому что буферизованный ввод-вывод. Она имеет следующие вещи. Она имеет шаредную память, которая менеджится в User space, с точки зрения операционной системы. И имеет такой же кусок кэша в кэше ядра в Kernel space.


И основная задача современной базы данных – это странички с диска подымать в память. И когда у нас произошло какое-то изменение, помечать эти странички как грязные, записывать writes ahead log. И после этого синхронизировать память, чтобы она была консистентна диску.


В ситуации с Postgres – это постоянно путешествует туда-сюда из шаредной памяти, которая управляется Postgres в Page cache ядра и далее на диск через весь линуксовый stack.


С той или иной разницей, если вы используете базу данных на файловой системе, она у вас будет так работать более-менее с любой UNIX-подобной системой и с любой базой данных. Если вы используете Oracle, то у вас будет немножко другая история. Oracle сам будет взаимодействовать с диском, но принцип такой же. Задача – как можно быстрее переместить эти странички через весь stack ввода-вывода, каким бы он не был.



На этом месте проблемы могут возникнуть практически на каждом этапе. Пока у нас все read only, у нас проблем нет. Мы прочитали, у нас достаточно памяти. Весь dataset, который нам нужно читать, вмещается в оперативную память. Мы его подняли, у нас все в памяти работает хорошо. То, что у нас при этом в случае Postgres в buffer cache лежит то же самое, нас не очень волнует.



Проблемы начинаются, когда нам это все нужно записать. Потому что, когда нам это нужно записать, нам нужно сильно большее количество памяти туда-сюда гонять. И, соответственно, нам нужно настроить Postgres и MySQL, чтобы в шаредной памяти это все хорошо поехало на диск. И в случае с Postgres нам надо еще хорошо настроить бэкграундное списывание грязных страниц в Linux, чтобы оно дальше ехало на диск. Это одна сторона проблемы с вводом-выводом, т. е. синхронизация кэша.


Вторая сторона, с которой часто сталкиваются DBA, это затык записи в writes ahead log, когда у нас настолько мощная нагрузка, что даже последовательно записываемый журнал, упирается в диск. И в этой ситуации его тоже надо записать быстро. И фактически она мало чем отличается от ситуации синхронизации кэша. С той только разницей, что там меньше приспособлений для этого. С Postgres работаем с большим количеством шаредных буферов. В базе есть механизмы, как эффективнее это списывать на диск. А writes ahead log он и без этого до предела оптимизировал. И единственное, что мы можем сделать, чтобы сам writes ahead log писался эффективнее, это только поменять настройки Linux, чтобы он эффективнее писал.



Основные особенности workload базы данных. Сегмент шаредной памяти может быть очень большой. Когда я начинал рассказывать об этом на конференциях в 2012-ом году, то я говорил, что сейчас память стала дешевая, даже встречаются сервера с 32 GB оперативной памятью. Но сейчас даже ноутбуки встречаются с таким количеством памяти и даже больше. Т. е. в серверах сейчас все больше и больше встречаются 128-256 GB и дальше.


Во-первых, это действительно много памяти. Даже, если мы банально будем это писать, то это занимает время, ресурсы и т. д. Во-вторых, технологии, которые мы для этого используем, они очень консервативные. Они старые, их давно разрабатывают, они потихоньку эволюционируют. И в них механизм не то, чтобы по последнему слову техники. Соответственно, когда мы синхронизируем все эти кэши, у нас возникает очень большой ввод-вывод.


И дальше возникает еще одна проблема, что мы не можем что-то покрутить, посмотреть на эффекты. Т. е. мы покрутили какие-то параметры в Postgres, настроили checkpoints, а эффекта не увидели. Т. е. мы не можем сделать step by step, как у ученых принято делать. Там покрутили, понаблюдали эффекты и дальше покрутили. Мы вынуждены настраивать сразу весь stack. И это приносит свои дополнительные сложности.



Что в случае Postgres генерирует большинство IO? Это checkpoints. Из тех, кто работает с Postgres, кто сталкивался с checkpoint spikes, когда у вас такая пила на графике? Раньше очень много людей поднимали руки. Сейчас есть мануалы, как это чинить, стало попроще и плюс еще SSD стали сильно спасать картину. Но, в принципе, у Postgres редко что-то упирается непосредственно в запись вала, упирается в эту синхронизацию, когда происходит checkpoint и вызывается fsync, и происходит наезжание одного checkpoint на другой. Слишком много ввода-вывода. Один checkpoint еще не закончился, не сделал все свои fsyncs, которые ему нужны, и начался другой checkpoint.


У Postgres есть еще такая уникальная фишка как автовакуум. Это многолетняя история подставления костылей под ту архитектуру, которая была сделана. И если у вас автовакуум не справляется, вы обычно настраиваете его, чтобы он работал агрессивно, т. е. чтобы у вас было много воркеров автовакуума, и он по чуть-чуть срабатывал часто, обрабатывал таблицы быстро и не мешал остальным вещам, потому что иначе у вас будут проблемы с блокировками.


Но когда автовакуум настроен вот так агрессивно, он у вас начинает использовать IO. И вот эта вся радость еще может накладываться с checkpoints, и у вас большую часть времени диски утилизованы почти на 100 %. И это все источник проблем.


Как ни странно, бывает такая проблема, как cache refill. Она обычно меньше известна DBA. Типичный пример – это, когда у вас база данных стартовала, и какое-то время все сильно тормозит. И даже если у вас огромное количество оперативки, нужно хорошие диски иметь, чтобы stack обеспечивал прогрев этого кэша.


На перфоманс эта штука имеет довольно серьезный эффект даже не после рестарта базы данных, а когда у вас прошел checkpoint, большое количество страниц было запачкано во всей базе. Они были сдамплены на диск, потому что их нужно синхронизовать, а потом ваши запросы запрашивают новую версию этой странички из диска. И у вас база начинает проседать. И вы можете на графиках видеть, что у вас cache refill после каждого checkpoint вносит какой-то процент в нагрузку.


И самая неприятная вещь во вводе-выводе касательно Postgres или разных других баз данных, это так называемый worker IO. В Oracle – это несколько проще, в Postgres может быть проблемой, когда у вас в каждый worker, в который вы обращаетесь с запросом, начинает генерировать свои IO, потому что у него больше не хватает кэша для того, чтобы прочитать новые страницы с диска. Т. е. у вас все шаренные buffers попачканные, checkpoint еще на было. Вот такая ситуация. И worker’у для того, чтобы выполнить простейший select, нужно взять откуда-то кэш. И для этого ему сначала надо сдамбить все это дело на диск. И у вас worker, а не специализированный процесс checkpoint’а, начинает выполнять fsync, чтобы немножко себе освободить место, и чтобы оно могла быть заполнено чем-то новым.


Тут возникает еще большая проблема, потому что worker – не специализированная вещь. И весь процесс вообще никак не оптимизирован. Т. е. мы можем что-то на уровне Linux где-то оптимизировать. Но в Postgres – это чисто аварийная мера, как слив-перелив в ванне. Он не предназначен для того, чтобы сливать, он для другого.



Какую задачу мы решаем, когда что-то тюним? Как максимизировать вот этот поток грязных страниц между диском и памятью?


И задействованы:


  • Диск,


  • Память,


  • CPU,


  • IO schedulers,


  • Файловые системы,


  • И настройки самой базы данных.



И часто бывает так, что эти вещи не прямо касаются диска. Вы можете видеть какие-то проблемы. Типичные случай – вы смотрите, что load average очень большой, потому что кто-то ждет диск и все остальные процессы тоже ждут этот процесс. И нет явной утилизации диска по записи, а просто что-то там заблочилось, например. И проблема все равно с вводом-выводом.


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



Смотрите, как исторически это сложилось. В принципе, никто не занимался оптимизацией многих из этих стадий при переходе с одной стадии на другую, потому что диски долгие годы были страшно медленные. Т. е. не имело смысла заниматься какой-то супероптимизацией fsync и тому подобными вещами, потому что у вас вращающийся диск, по которому как грампластинке ездят головки. И вот этот seek был настолько длинный, что какие-то проблемы не всплывали. Если вы работаете с базами данных, то вы знаете, что если база данных не настроена, то топ запросов смотреть бесполезно. Потому что вы настроите там достаточное количество шаредной памяти того-сего, пятого-десятого, но у вас будет новый топ запросов и надо будет заново его настраивать. Тут та же самая история. И весь stack Linux тоже был из этого расчета сделан.



Максимизировать поток страничек через весь stack до определенного этапа было очень просто. Мы придумали там сделать вспомогательный процесс bgwriter в Postgres, чтобы он разгружал checkpoints. И оно там стало чуть более параллельным, можно еще, наверное, добавить параллелизма.


А историю, как минимизировать latency, чтобы в итоге это приписывалось, это такая задача последней мили. Нам уже нужны какие-то супертехнологии. И этими супертехнологиями стали SSD. Эта вещь, которая попала в этот мир. И резко уменьшила latency. И на всех остальных этапах stack стала вылезать куча разных проблем, которые нужно было теперь решать. И это было со стороны баз данных, и со стороны производителей Linux.



В промежутке мы были вынуждены заниматься странными расставлением подпорок, т. е. как-то жить с текущей инфраструктурой Linux, но уже с новыми дисками.


И было совершенно не трудно догадаться, что когда мы смотрели перфоманс-тесты от производителя, то базе данных сильно лучше не становилось. Потому что база данных не столько про IOPS. Как бывает очень часто? Мы можем 50 000 IOPS пропустить и это, наверное, хорошо. Но если мы не знаем latency и не знаем распределение latency, и не знаем, что latency равномерная, то вообще ничего не можем сказать о перфомансе. Потому что в какой-то момент наша база данных начнет делать checkpoints. И в этот момент latency скакнет.


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



Вот это линуксовый stack ввода-вывода. У нас есть user space, т. е. та память, которая менеджится самой базой данных. И мы в нашей базе данных как-то настроили так, чтобы все было хорошо. Об этом можно отдельный доклад делать.


Дальше оно неизбежно либо через page cache, либо через интерфейс direct IO попадает в block IO. Что это такое? Представьте, что у нас есть интерфейс файловой системы. Через него съезжает вот эти наши странички, которые у нас были в buffer cache изначально, как они были в базе данных. Т. е. такие блоки.


Вот этот block IO слой, чем занимается? Есть С-структура – блок в ядре, которая берет эти блоки и собирает из них вектора, массивы requests на ввод или на вывод. И, соответственно, на этом BIO слое дальше под ним находится слой requests. На этом слое собираются вектора, как они туда поедут. И долгое время все это хозяйство, два эти слоя в Linux, они были заточены на то, чтобы эффективно писать на магнитные диски. И нужно было совершить такой переход. У нас есть блоки, которые удобно менеджить базе данных. И нам нужно эти блоки собрать в такие вектора, которые удобны записать на диск, желательно с наименьшим seek, чтобы они где-то рядом лежали.


И для того чтобы это эффективно работало, придумали так называемый Elevator или IO Scheduler. Чем они преимущественно занимались? Они брали эти вектора и сортировали каким-то образом, чтобы в блочный драйвер приехали расположенные в удобном порядке блоки, которые надо записать. А драйвер в свою очередь производил трансляцию из блоков в свои сектора и писал это все на диск.



И проблема была в том, что нужно было делать transitions. И на каждом transition нужно было свою логику, как это оптимально сделать, имплементировать.



До того, как было 2.6 ядро, был Linus Elevator. Это был самый простой IO Scheduler. И написал он был сами догадаетесь кем. Поэтому долгое время он считался абсолютно незыблемым и очень хорошим. Но по мере того, как один человек по-прежнему его считал хорошим, люди стали новые разрабатывать.


И он имел много проблем, потому что делал merging и sorting в зависимости от того, как эффективнее писать. И это в случае с вращающимися дисками приводило к тому, что возникала такая ситуация starvation. Если у нас есть диск, чтобы на него эффективно записать, его нужно повернуть так. А теперь нам внезапно с него нужно одновременно эффективно прочитать, а он уже так повернут. И, соответственно, читается с такой штуки плохо.



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


CFQ – это не то же самое, что scheduler операционной системы, просто называется похоже. И он придуман как универсальный. Что такое универсальный scheduler? Вы работаете с базами данных. Кто считает, что у него workload усредненный? А кто считает, что у него уникальный высоконагруженный какой-нибудь? Смысл в том, что очень плохо с универсальностью у баз данных. Т. е. универсальный workload, который мы можем себе представить, это обычный desktop, ноутбук. Там черт знает что происходит: то мы музыку слушаем, то мы текст в Word набираем. Для этих вещей универсальные schedulers и писались.


Какая у них основная задача? Мы делаем для каждого виртуального терминала, для каждого процесса свою очередь запросов. И когда мы хотим послушать музыку в каком-нибудь аудиопроигрывателе, то вводом-выводом для этого проигрывателя занимается эта очередь. Если мы хотим забэкапить что-нибудь с помощью команды CP, то этим занимается что-то еще.


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


Соответственно, для очень небольших workloads баз данных такой scheduler подходил, а так он был абсолютно бессмысленным. Было проще его выключить или не использовать, если это возможно.


Постепенно появилась такая штука, как Deadline scheduler. Это немножко более хитрая вещь, но базово – это все равно тот же merging и sorting для вращающихся дисков. Т. е. мы с учетом того, как у нас устроена конкретная дисковая подсистема, мы собираем векторы блоков, чтобы их оптимальным способом записать. У него было меньше проблем со starvation, но они все равно там были.


И поэтому к началу третьих ядер Linux стала появляться такая штука, как noop. И эта вещь гораздо лучше работала с SSD, которые тогда уже вовсю появлялись. И базовая идея была такая, что, включая scheduler, мы фактически scheduling отключаем. У нас нет merging, sorting.


Почему это стало лучше работать с SSD? Потому что SSD от природы параллельный, т. е. у него есть ячейки памяти. Соответственно, чем больше мы этих элементов напихаем на одной PCIe, тем у нас эта штука будет работать лучше и эффективнее.


А дальше мы выясняем, что у нас делает scheduler. А scheduler из своих потусторонних соображений, непонятных для SSD, собирает какие-то вектора и куда-то их посылает. И фактически это все заканчивается такой воронкой. Мы убиваем тем самым параллелизм SSD, мы не используем их на полную катушку. Поэтому просто это отключив, чтобы векторы ехали как попало без всякой сортировки, это сработало лучше по производительности. И поэтому народ любил говорить, что на SSD лучше всего идут какие-то random read и т. д. Просто потому что для такой задачи он не был приспособлен.



Начиная с 3.13 ядра появилась такая штука, как blk-mq. Его сложно называть scheduler’ом. Это фактически замена request layer в ядре. Т. е. архитектурно эта вещь стоит отдельно. Он начинался как scheduler. И потихоньку это привело к очень серьезной переработке всего линуксового stack ввода-вывода.


Какая идея? Идея такая – давайте использовать для ввода-вывода нативную возможность SDD делать эффективный параллелизм. Это и произошло. Там, в зависимости от того, сколько мы параллельных потоков ввода-вывода можем использовать, у нас есть очереди. Очереди честные. И мы через эти очереди пишем is is на наши SSD. И для каждой CPU – это своя очередь для записи.


В настоящий момент эта штука очень активно развивается, работает. И сейчас нет причин не использовать это дело, т. е. в современных 4.x ядрах выигрыш от blk-mq может быть очень большим. Это будет ощутимо. Это не 5-10 %, это больше. И сейчас мы считаем, что это лучшая опция для работы с SDD.


Надо понимать в нынешнем виде blk-mq – эта вещь, напрямую завязанная на NVMe драйвер Linux. Есть не только драйвер для Linux, есть, например, драйвер для Microsoft. Но именно вот эта идея сделать blk-mq и NVMe-драйвер – это переработка stack Linux, от которой база данных очень здорово выиграла.


Это была команда из нескольких компаний, которые собравшись вместе, решили сделать эту спецификацию, этот протокол. Сейчас он уже в production версии отлично работает для локальных PCIe SSD. И это практически готовая история для дисковых массивов, подключаемых по оптике, чтобы эта технология работала и с ними.


Давайте мы немножко больше в нее погрузимся, чтобы понять, что это такое. Спецификация NVMe очень большая. И поэтому все подробности мы сейчас не рассмотрим. Мы только пробежимся по ним.



Старый подход к elevators. Вот у нас есть CPU, есть его queue. Мы, соответственно, как-то идем на диски.


Более какие-то продвинутые elevators работали так: у нас есть несколько CPU, есть несколько очередей. И каким-то образом, например, в зависимости от какого родительского процесса отпочковались наши workers, идет в какие-то очереди и попадает на диски.



Соответственно, blk-mq – это абсолютно новый подход, когда фактически каждый CPU засовывает свой ввод-вывод в свою очередь. И дальше попадает на диски, не важно, как подключенные, потому что драйвер новый. Там нет SSD-драйвера, который апеллирует даже с SSD понятием цилиндров, блоков и т. д.


Если кто-нибудь обращал внимание, все вендоры RAID-массивов стали продавать такие дополнения, которые позволяли обходить кеш RAID, если у вас подключены SSD, напрямую писать туда. Фактически они тоже самое делали. Они отключали применение SSD-драйвера для своих продуктов.



Как выглядит новый stack? В принципе, сверху там осталось все то же самое. Т. е. базы данных, например, довольно сильно отстают от реализации этого дела, потому что это сейчас происходит на наших глазах. Ввод-вывод как и раньше попадает в Block IO слой. И там есть blk-mq, который заменяет request слой. Он не заменяет scheduler.


Когда было ядро 3.13, это было примерно все. Но в современных ядрах стали делать новые подходы, новые технологии. Стали появляться специальные schedulers для blk-mq, которые на более развесистые параллелизмы рассчитаны. Из двух, которые сейчас в 4-х ядрах Linux считаются зрелыми, это Kyber и BFQ IO schedulers, которые рассчитана на работу с параллелизмом и с blk-mq.


Чем они отличаются? BFQ – это аналог CFQ. Они абсолютно не похожи, хотя один из другого вырос. Основная идея – это такой scheduler со cложной математикой, как сделать хорошо. И идея такая – какое приложение, каждый процесс имеет некую квоту на IO. И согласно этой квоте имеет такую полосу, мы в нее пишем. Насколько хорошо эта штука работает – вопрос сложный. Есть масса статей с огромным количеством математики, лежащей в основе этого дела. Это можно почитать.


И альтернативно был сделан Kyber, который представляет собой все тоже самое, только с оторванной математикой. У него очень небольшой scheduler по количеству кода. Его основная задача – от этого CPU пришло и туда дальше поехало. И оно легковесное, и призвано работать лучше.


Вот такие новые schedulers появились на этой штуке.


Но очень важный момент для всего stack заключается в том, что blk-mq не смотрит в SSD-драйвер. У него нет очередного слоя конверсии, на который я жаловался, когда показывал, как раньше выглядел stack. Из blk-mq все приходит в NVMe-драйвер в таком виде, в котором ему нужно. Никто не пересчитывает блоки в цилиндры и т. д.


И тут возникло несколько интересных моментов, о которых стоит упомянуть. И на последних слайдах я еще расскажу о них. Резко упало latency и этого слоя в том числе. Т. е. сначала появились SSD, которые хорошо работают. Нужно стало перерабатывать вот этот слой. Как только мы перестали конвертировать туда-сюда все эти вещи, выяснилось, что у нас и в NVMe слое и в blk-mk есть свои bottleneck, которые надо оптимизировать. Сейчас мы о них немножко поговорим.



https://www.thomas-krenn.com/en/wiki/Linux_Storage_Stack_Diagram


Пример диаграммы для Linux Kernel 4.10 под спойлером


Заголовок спойлера


Вот это диаграмма упрощенная, чтобы было только то, что нам нужно для этого доклада. Если кто-то интересуется, есть такой Thomas Krenn. Он живет в Германии. У него есть постоянно обновляемые к актуальным ядрам диаграммы stacks ввода-вывода Linux. Посмотрите, там видно, кто над кем стоял, какие взаимоотношения между драйверами, какие elevators являются частью какого слоя и т. д. И поскольку диаграмма регулярно обновляемая, то это хороший способ следить за тем, как эволюционирует ядро. И это нужно делать администратору баз данных, и любому человеку, кто работает с базой данных.



Давайте дальше немного закопаемся в NVMe. Как я уже говорил, это такая спецификация стандарта и в Linux она сейчас хорошо имплементирована, т. е. Linux – это такая движущая сила, как это сделать.


На 2018 года в production третья версия. И вот этот драйвер по своей спецификации через себя может пропускать очень много. Третья, по-моему, в районе 20 GB в секунду на 1 SSD-блок. А в современной 5-ой вообще ничего ничем не ограничено. У SSD-драйвера нет ни интерфейсов, ни механизмов внутри, чтобы такую пропускную способность обеспечить. Вот эта спецификация существенно быстрее, чем все, что было до этого.


Возникает вопрос – готовы ли к этому базы данных? Т. е. насколько базы данных способны проживать такую нагрузку? Потому что у них есть индексы в виде дерева и т. д. И они тоже когда-то писались под вращающиеся диски.



И этот вопрос открытый. И сейчас активно происходит взаимодействие. Если вы смотрели, то недавно в исходный код Postgres появились новые коммиты про pre write и тому подобные вещи. Разработчики Postgres и MySQL взаимодействуют с разработчиками ядра, хотя, конечно, хотелось бы, чтобы больше было бы этого взаимодействия. Базы данных потихоньку к этому адаптируются.


Важный момент, который был сделан в 2017-2018 годах – в NVMe добавили IO polling. Что это такое? Как я уже говорил, у нас была проблема, что у нас была очень высокая latency вращающихся дисков, а тут у нас появились SSD, которые гораздо быстрее. И возник некоторый косяк. Идет fsync, начинает что-то писать. И на очень низком уровне, глубоко в драйвере, оправляется request непосредственно в железку – запиши. И механизм раньше был очень простой. Отправили и ждем, пока обработается прерывание. И по сравнению с тем, что такое записать на вращающийся диск, подождать обработки прерывания, с этим не было никаких проблем. Т. е. настолько долго надо было ждать, что как только запись закончилась, прерывание тут сработало.


В результате были вынуждены сделать такой механизм, поскольку SSD это делает очень быстро, как время от времени опрашивать железку о том, произошла ли запись. Т. е. до 50 %, насколько я читал, в первых версиях было возрастание скорости ввода-вывода из-за того, что мы не ждем прерывания, а сами активно умеем спрашивать железку о том, произошла запись или нет. Это называется IO polling, который был в последних версиях представлен.


В версии 4.12 появились IO Schedulers, специально заточенные для работы с blk-mq и NVMe: Kyber и BFQ. И они уже официально в ядре, их можно использовать.


Сейчас уже в пригодном для использования виде есть IO tagging. Это делается в основном производителями облаков и виртуалок. Они контрибьютят это дело. Но, грубо говоря, ввод от определенного приложения можно затегить и дать ему какой-нибудь приоритет. Это вещь, к которой пока не готова база данных, но следите за обновлениями. Очень скоро, я думаю, будет популярно.



И много мелких вещий по улучшению Direct IO. Но тут сразу возникает вопрос, что, например, в Postgres Direct IO сейчас не поддерживается. И есть ряд проблем, которые мешают включить поддержку этих вещей. Т. е. в 2018 году это только для WAL и только, если у нас не включена репликация.


Но поскольку очень много специфичного кода надо написать, то пока что все с этим делом воздерживаются. И несмотря на то, что Linus сильно ругается на эту идею и на то, как она сделана, но, в принципе, все базы данных туда идут. Сейчас в Oracle и в MySQL это активно используется.


Вопросы:


Добрый день! Спасибо за доклад. В презентации несколько раз мелькали слова «хорошие SSD», «современные SSD», это какие?


Есть такое понятие, как хорошее серверное SSD. Есть характеристики дисков: отношение к перегреву, к вибрации. И десктопный дешевый SSD всем этим не обладает. Он может внезапно выйти из строя. Но главный признак хорошего SSD – это наличие конденсатора. Смысл в чем? Представляете, как read с батарейкой работает? Т. е. есть кэш, запитанный батарейкой. И если умерло электричество, мы за счет этой батарейки держим в кэше данные, стартуем и они проваливаются на диск. В SSD есть похожий механизм, но более интересный. Там есть конденсатор, который позволяет из временной памяти записать в постоянную, если пропало электричество. И он лучше батарейки, потому что он вечный, он живет дольше жесткого диска и нормально работает.


Если вы возьмете хороший intel’ский серии 32 или 36 (я не буду точную серию сейчас говорить), то они все этими свойствами обладают. А если вы возьмете какой-нибудь дешевый Samsung для ноутбука, то я бы туда базу данных не ставил, потому что пропадет напряжение и все – привет. Вам нужно тогда держать барьер на файловой системе. И это резко убьет всю производительность. Вот такое разделение.


Спасибо большое за доклад! Вы очень вкусно рассказываете про NVMe и SSD. Мы из тех людей, которые используют десктопные SSD под базу данных – Samsung 860 Pro. И как вы правильно заметили, параметр по глубине очереди для стандартных SATA SSD порядка 32-х, и для NVMe порядка 64-х. Никакое сравнение не происходит. Два вопроса. Я так понимаю, что настало то время, когда можно отказаться полностью от SATA SSD и полностью переехать на NVMe? И второй вопрос. Если мы переходим на NVMe, то мы ядро используем не менее 4-ой версии?


На первый вопрос – да. Я могу видеть какие-то резоны для каких-то систем с небольшой нагрузкой и супертребованиями к надежности, где можно использовать SAS’ы. А SATA для серверов и баз данных – это мертвый стандарт. Потому что, грубо говоря, это IDE по механике с более быстрым интерфейсом. Т. е. у них с надежностью все очень плохо. От SATA надо отказываться.


Что касается второго вопроса, то – да, 4-ые ядра сильно лучше. Если вы используете PCI Express вещи, то можно и на более ранних ядрах работать, потому что там это все уже включено. Там нет опций других schedulers. Там написано none, но на самом деле – это blk-mq. И с 4.10 все гораздо вкуснее по скорости.


Здравствуйте, Илья! Благодарю за доклад! Можете немного осветить тему про Postgres sync surprise? Как от него защититься?


Там есть несколько задач. Три задачи. Я об этом скажу вкратце, поскольку это долго. Найдите меня на стенде, я вам расскажу подробней. Первая задача – это настроить checkpoints, чтобы они были реже, больше и в соответствие с тем, сколько вы можете там реально записать. Второй момент – это настроить background writer, чтобы он помогал checkpoint, чтобы не было fsyncs, постоянно крутящихся в системе. Т. е. редкие checkpoints с большим количеством fsyncs, распределенные между двумя checkpoints. 0, 9 параметр ставите. И третий момент – это автовакуум. Автовакуум нужно оттюнить, чтобы не было лишнего паразитного ввода-вывода.


А дальше возникает вопрос – куда смотреть в Linux? И в некоторых случаях имеет смысл потюнить background data, в соответствие с тем, сколько у вас дисковая система пропускает. Но это на еще один такой доклад, поэтому я бегло обозначил в какую сторону смотреть, а дальше приходите – расскажем.


Здравствуйте! Меня зовут Дмитрий. Если использовать bcache и базу данных поверх этого bcache, то какой scheduler лучше всего использовать?


Я бы не сказал, что это такой setup, предназначенный для базы данных.


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


Имеет смысл попробовать на относительно новых ядрах сравнить blk-mq с noop. И если будет разница, то лучше использовать blk-mq, а если нет, то noop.


Не может так получится, что когда оно будет упираться в HDD, то bkl-mq вдруг внезапно будет давать очень плохие результаты?


Какие у вас диски под этим делом?


Там в качестве SSD использовались Intel 3710


Т. е. нормальные SSD.


Да, а в качестве HDD HGST, я уже точно не скажу, какие. Серверные.


Т. е. если оно наедет на HDD, то будет плохо?


Моя мысль в том, что использование bkl-mq может добавить непредсказуемости. И когда это будет работать с реальными HDD, может произойти все, что угодно. И даже если на первых тестах покажется, что это работает лучше, то является ли это целесообразным?


Если у вас там есть непонятные HDD, то лучше, конечно, не рисковать. А еще лучше от них избавиться. Но, возможно, да, в такой ситуации использовать noop будет безопасней.