Комментарии 12
Предположим, что к одному моему знакомому приходил как-то раз приходил рекрутёр с таким описанием вакансии
я рекрутер в компании Postgres Professional. Мы поддерживаем известную СУБД PostgreSQL и на ее основе создаем уникальные решения для российского и, в перспективе, мирового рынка.
Сейчас мы ищем кандидата на роль Performance engineer. Нужно будет исследовать и предотвращать инциденты, связанные с проблемами производительности или повреждением данных при эксплуатации больших систем.
Возможен как гибрид, так и полная удаленка
Буду рад, если удалось вас заинтересовать, и вы согласитесь обсудить вакансию в 15-20-минутном звонке в удобное для вас время.
Как из описания друг, который не разу не DBA (у него ни разу эта аббревиатура не встречается, в языках сикуль не указан, да и по опыту видно, что друг не базы данных тюнил)
должен понять что под такой должностью гигант мысли, так сказать Лев Толстой из статьи подразумевается (друг сразу объяснил, что произошла чудовищная ошибка и его принимают не за того, кто он есть)
Друг интересуется, а как это - предотвращать инциденты с производительностью до их появления.
То есть перформанс инженер должен быть немножечко телепат?
Представьте картину: критически важный для бизнеса SQL-запрос ползет... 15 часов. Пятнадцать! Целый рабочий день, пока система мучительно перемалывает данные. Бизнес теряет деньги, пользователи на грани нервного срыва. А потом появляется он — перфоманс-инженер. Несколько часов напряженного анализа, пара точных правок в коде запроса — и вуаля! Тот же самый запрос выполняется за... 2 минуты. Фантастика? Нет, суровая и увлекательная реальность перфоманс-инжиниринга.
Вообще-то типичная задача и решение для рядового администратора баз данных. И несколько часов на анализ - у меня есть вопросов, как у человека, который переделывал запросы, которые прерывались после более суток работы, а после переделки этот же запрос отрабатывал... ээээ.... за приемлемое время (давно это было, лет 15 назад), перестраивал индексы таким образом, что запрос с 20с выполнялся за микросекунды.
Массовая загрузка данных шла полным ходом, но уперлась в неожиданное препятствие — долгая запись WAL (Write-Ahead Log).
...В итоге они обнаружили возможность, о которой все забыли — и клиент, и даже сами разработчики Postgres. Оказалось, можно увеличить «окно», с которым пишутся WALы — писать их реже, но большими порциями...
Забыли?! Её не используют, по причине увеличения количества потерянных транзакций в случае сбоев. Т.е. те транзакции, информация из WAL-ов которых не запишется на диск, будут потеряны. А людям очень не хочется терять данные.
Ещё момент - в случае массовой вставки данных необходимо существенно увеличивать количество сохраняемых WAL-ов для того, чтобы увеличить интервал между чекпойнтами. Потому что чекпойнты будут конфликтовать с WAL-ами за запись на диск. Что явно не увеличит скорость загрузки данных.
В общем, без деталей описанный случай не впечатляет от слова совсем.
Резкий всплеск подключений порождает конкуренцию за внутренние ресурсы СУБД, например, за LockManager. Система встает колом из-за блокировок.
У вас 48 ядер. Количество активных запросов - ~1000 (я на MySQL видел 24 ядра и 4,5+ тыс активных запросов, обнулили кэши nginx на нагруженном проекте, как же давно это было!). У вас в подобной ситуации запросы за процессорные такты будут драться, а не за блокировки.
Долой интуицию — только цифры.
А вот я считаю, что интуиция, по крайней мере, у инженера - это накопленный опыт. Со всеми вытекающими к подобным призывам.
Подытоживая. Описанные задачи и ситуации - ТИПИЧНЫЕ ситуации и задачи для администратора баз данных. Соответственно, статья - странная о странном. Что вызывает недоумение - серьёзная компания, и подобный материал. Это что такое было?
Соглашусь со сказанным, особенно про Wal, но статья наверное больше для привлечения молодых специалистов для их завлечения в эту сферу)
У вас 48 ядер. Количество активных запросов - ~1000 (я на MySQL видел 24 ядра и 4,5+ тыс активных запросов, обнулили кэши nginx на нагруженном проекте, как же давно это было!). У вас в подобной ситуации запросы за процессорные такты будут драться, а не за блокировки.
Ой, и правда, аж вспомнился 2001 год! У меня друг работал на MySQL, писал ядро и тестировали вместе. Пробовали тогда и на винде и линуксе и фре и полуоси... Упиралось всё в ОС. Самое интересно было на Win NT4: операционка честно открывала сокеты "сколько хотел", но после 1000 открытых только этим и занималась, а на остальное не реагировала. На FreeBSD вообще никаких проблем, только сразу настройки подтюнить и нормально и 5К на 4 ядрах держала. С линухом ядро пересобирали, но тоже такого же результата добились.
Спасибо за обратную связь, учтём!
Соответственно, статья - странная о странном. Что вызывает недоумение - серьёзная компания, и подобный материал. Это что такое было?
Статьи должны быть разными для разной аудитории. Эта статья задумывалась как введение в профессию для начинающих специалистов, поэтому глубокий технический разбор здесь не предполагался.
Если вам интересны сложные кейсы, рекомендую посмотреть другие материалы или доклады на PgConf — где мы, нашим отделом, подробно разбираем реальные проблемы и их решения.
Немного прокомментирую по поводу WAL, т. к. это одна из последних наших задач и, как мне кажется, достаточно интересная.
Забыли?! Её не используют, по причине увеличения количества потерянных транзакций в случае сбоев.
Пожалуй, здесь действительно недостаточно контекста. Ну что вы хотите от интервью :)
Речь идёт о массовой вставке данных при миграции в PostgreSQL с другой СУБД. По факту, нам тут не очень-то и важно, потеряем ли мы какие-то транзакции или нет. Если что-то идёт не так при миграции, будь то segmentation fault или просто кто-то рубильник дёрнул, нам в 99% случаев придётся делать эту миграцию заново.
Ещё момент - в случае массовой вставки данных необходимо существенно увеличивать количество сохраняемых WAL-ов для того, чтобы увеличить интервал между чекпойнтами.
Безусловно, это первое узкое горлышко, с которым сталкиваешься, когда пытаешься залить огромную кучу данных. Мы это поправили ещё до миграции, но столкнулись уже с более нестандартными проблемами при большом количестве потоков COPY (300+).
Изначальная проблема звучит так: в процессе массовой миграции данных в pg_stat_activity наблюдалось доминирование wait_event: LWLock:WalInsert.
Увеличили wal_buffers в 10 раз — ситуация не изменилась, поэтому начинаем думать.
Приступаем к анализу!
По утилизации сервера видно, что мы ещё не исчерпали ресурсы системы, а как раз стопоримся на внутренних блокировках PostgreSQL.
Попробую коротко описать суть блокировки LWLock:WalInsert: Перед тем как отправить WAL-запись на диск, эта запись помещается в WAL-буфер в shared memory.
Описать, как именно записи пишутся в буфер, лучше, чем Егор Рогов, я не смогу, поэтому — цитата из книги:https://edu.postgrespro.ru/postgresql_internals-17.pdf
Чтобы создать журнальную запись, процесс сначала резервирует место внутри страницы WAL, а затем заполняет его необходимыми данными. Резервирование строго упорядочено; процесс должен захватить спин-блокировку insert position, защищающую указатель вставки. Но заполнять уже зарезервированное место могут одновременно несколько процессов. Для этого процесс должен захватить любую из восьми легких блокировок, образующих транш WALInsert.
И вот, понимая это, первая мысль, которая приходит в голову: «Ну вот же, мы наконец достигли ситуации, когда 8-ми траншей не хватает и их нужно увеличить».
Разработчики сделали патч, попутно улучшив алгоритм взятия этих блокировок: https://www.postgresql.org/message-id/flat/3b11fdc2-9793-403d-b3d4-67ff9a00d447%40postgrespro.ru
Мы радостно его забираем, пробуем — и ничего... На текущей нагрузке и на наших серверах выигрыш совсем не заметен.
Пока тестировали предыдущий патч, разработчик попутно обнаружил ещё одну возможность оптимизации с WALBufMappingLock: https://www.postgresql.org/message-id/flat/39b39e7a-41b4-4f34-b3f5-db735e74a723%40postgrespro.ru
Но кардинально изменить ситуацию у нас не получалось.
Рассказывать, как мы пришли к решению, можно долго, поэтому подведу итоги: блокировки LWLock:WALInsert были вызваны недостаточной производительностью записи WAL из-за недостаточного размера буферов и частых сбросов на диск. Увеличив wal_buffers и wal_writer_flush_after до неприличных значений, мы добились ускорения на 30%. И проблема с блокировками внутри PostgreSQL больше не повторялась.
Считаем допустимым установить большие значения для этих параметров на время миграции. После миграции, конечно, нужно вернуть рекомендуемые значения.
Признаюсь, мне сложно остановиться — кажется, об этом можно написать отдельную статью. Если интересно, дайте знать — мы на низком старте!
Вот статьи бы с такими техническими деталями)
Подскажите, вы выкручивать wal_buffers начали до неприличных значений уже после того как увеличили кол-во траншей с 8ми до X? Сейчас просто имею схожую проблему с WALInsert на Ванили, увеличение wal_buffers, манипуляции с wal_writer_* ни к чему не приводят, все потоки по-прежнему проводят около 70% времени в ожидании WALInsert. Вероятно в моем случае только нехватка 8 траншей и ничего с этим не поделать (версия 13.8).
Мы проверяли производительность до и после увеличения количества траншей до 64 — прирост есть, но в нашем конкретном случае он был не слишком значительным.
Я бы рекомендовал попробовать сократить количество информации, которое записывается в WAL. Если есть возможность, установите wal_level в minimal и отключите full_page_writes (Опасно! Обязательно изучите риски в документации).
Нужно также проверить настройки checkpoint, чтобы он выполнялся реже, и с большим объемом данных (checkpoint_flush_after).
Если есть возможность, лучше перенести папку pg_wal на другой диск (не тот, где находится PGDATA), чтобы при массовой вставке не было конкуренции.
Также можно отключить fsync, synchronous_commit (Опасно! Обязательно прочитайте в документации о последствиях).
Если ничего не помогает и хочется попробовать увеличить количество траншей — это можно сделать очень просто, особенно на ванильной сборке PostgreSQL.
Замените в исходниках https://github.com/postgres/postgres/blob/4bc493d14409857090928ea51c02a20aba8db364/src/backend/access/transam/xlog.c#L123:
#define NUM_XLOGINSERT_LOCKS 8
на
#define NUM_XLOGINSERT_LOCKS 64
и пересоберите PostgreSQL с теми же параметрами, которые использовались в оригинале. Потом подменить бинари и можно тестировать. Я посмотрел в версии 13.8 - должно работать так же.
Ой, как давно это было когда запросы к ораклу оптимизировал, но незабываемое ощущение от х300 выигрыша по времени. Ажно олдскулы свело :-)
Профессия performance инженер: детектив с лицензией на производительность