KPTI-патч и базы данных: разбираем падение IOPS после изоляции таблиц страниц
Патчи ядра Linux против Meltdown работают через KPTI. На СУБД-серверах фиксируем просадку IOPS до 15% - разбираем механику и способы снизить потери.
Выход патчей ядра Linux с KPTI (Kernel Page-Table Isolation) против уязвимости Meltdown (CVE-2017-5754)
Неделю назад мы описывали первую реакцию на Meltdown - инвентаризацию, срочную раскатку патчей, первые наблюдения по производительности баз данных. Прошло несколько дней, и у нас накопилось больше данных, чтобы говорить конкретнее.
Картина неоднородная. На части серверов просадка практически незаметна. На СУБД-серверах с высокой частотой системных вызовов - заметна очень хорошо.
Как работает KPTI и почему это касается баз данных
KPTI - Kernel Page-Table Isolation - это механизм, который изолирует таблицы страниц ядра от пространства пользователя. До патча ядро и userspace использовали одно адресное пространство - это и давало Meltdown возможность читать память ядра из пользовательского процесса через спекулятивное выполнение.
После патча при каждом системном вызове - переходе из userspace в kernel и обратно - процессор должен переключаться между двумя наборами таблиц страниц. Это сбрасывает TLB (Translation Lookaside Buffer) - кеш трансляции виртуальных адресов в физические. Сброс TLB дорогостоящий: следующие обращения к памяти придётся транслировать заново через page walk по иерархии таблиц.
Базы данных делают системных вызовов очень много. PostgreSQL на OLTP-нагрузке - read(), write(), fsync(), pread(), сетевые вызовы на каждое соединение. Каждый из них теперь дороже. Накопительный эффект даёт заметную просадку именно там, где syscall'ов на единицу полезной работы больше всего.
Что мы увидели на практике
На нескольких управляемых серверах с PostgreSQL 9.6 и MySQL 5.7 после применения патчей ядра картина примерно такая:
- OLTP с короткими транзакциями - здесь просадка наиболее выражена. Pgbench в режиме TPC-B на тестовом стенде показал снижение TPS порядка 10-15% по сравнению с непатченым ядром. Короткие транзакции - максимальное количество syscall'ов на запрос.
- Аналитические запросы с тяжёлыми SELECT'ами - просадка заметно меньше. Запрос, который выполняется секунду и делает несколько тысяч обращений к буферному кешу PostgreSQL, не уходит в kernel на каждое из них. Относительный overhead ниже.
- fsync-интенсивная нагрузка - запись с синхронизацией на диск. Вот тут неприятно: каждый
fsync()- системный вызов, и при высокой частоте коммитов это ощущается. На серверах с PostgreSQL в режимеsynchronous_commit = onи высоким TPS результат хуже среднего. - Нагрузка на CPU выросла - ожидаемо. TLB miss'ы стоят процессорного времени. На серверах с Hyper-Threading потери чуть меньше, на голом железе без HT - хуже.
Процессоры Intel Xeon Haswell и Broadwell в нашем парке - патч применили, PCID-поддержка есть. Ядра с поддержкой PCID (Process-Context Identifiers) умеют делать частичный сброс TLB вместо полного, что несколько снижает потери. Но это частичная компенсация, не полное устранение overhead.
Как минимизировать потери
Полностью убрать overhead нельзя - это цена изоляции. Но снизить его реально.
Первое - PCID. Убедитесь что ядро использует PCID-оптимизацию. Ядра 4.14+ с патчем KPTI умеют это на процессорах с поддержкой PCID (флаг pcid в /proc/cpuinfo). Проверить что она работает: dmesg | grep -i pcid. Если процессор поддерживает, но в dmesg нет упоминания - возможно, старое ядро или патч без этой оптимизации.
Второе - пересмотр частоты коммитов. Если приложение делает COMMIT на каждую строчку или каждые несколько миллисекунд - это прямой удар по fsync-нагрузке. Группировка транзакций, где логика позволяет, снизит количество syscall'ов на единицу работы.
Третье - connection pooling. PgBouncer перед PostgreSQL снижает количество активных соединений и соответственно накладные расходы на сетевые вызовы. Если его ещё нет - самое время.
Четвёртое - профилировать именно свою нагрузку. Цифры из интернета и наши наблюдения - ориентир, не приговор. На аналитической базе с редкими тяжёлыми запросами результат может быть 1-2%, на высокочастотном OLTP - 15% и больше. Бенчмарк в своём окружении с реальной нагрузкой даст честную картину.
Что делаем дальше
Пока мы в режиме наблюдения: собираем метрики с production-серверов до и после патча, сравниваем. Клиентам с критичными по latency БД - рекомендовали мониторить pg_stat_bgwriter, время коммитов и CPU-утилизацию после перехода.
По Meltdown на уровне ядра - закрыто. Остаётся Spectre CVE-2017-5715, который требует обновления микрокода Intel: для части процессоров микрокод ещё не вышел, следим за апдейтами от вендоров.
За managed-серверами следим централизованно - апдейты ядер уже раскатаны, метрики собираются. Если у вас СУБД-сервер без мониторинга после патча - это момент его наконец поставить.