Исходная страница в нативном Markdown: Производительность / Адаптивная оптимизация
Примечание
Эта страница извлечена из официального PDF в нативный Markdown. Текст слайдов, код и комментарии сохранены в порядке чтения; исходные PDF и HTML остаются эталоном макета.
HTML source:pgpro_09_aqo.htmlArchive:PGPRO-handouts-16.zipСтраниц PDF: 29
Некоммерческое использование материалов курса (презентации, демонстрации) разрешается без ограничений. Коммерческое использование возможно только с письменного разрешения компании Postgres Professional. Запрещается внесение изменений в материалы курса.
Обратная связь
Отзывы, замечания и предложения направляйте по адресу: edu@postgrespro.ru
Отказ от ответственности
Компания Postgres Professional не несет никакой ответственности за любые повреждения и убытки, включая потерю дохода, нанесенные прямым или непрямым, специальным или случайным использованием материалов курса. Компания Postgres Professional не предоставляет каких-либо гарантий на материалы курса. Материалы курса предоставляются на основе принципа «как есть» и компания Postgres Professional не обязана предоставлять сопровождение, поддержку, обновления, расширения и изменения.
Page 2: Темы
Предназначение AQO
Принцип работы AQO
Обучение без классификации
Классификация запросов
Песочница
Page 3: Предназначение AQO
Исправление неверных оценок кардинальности, когда штатные средства не помогают
отклонение кардинальности от вычисленной планировщиком
AQO улучшает оценки в запросах с простыми условиями
атрибут оператор-сравнения константа
Основная сфера применения — сложные аналитические запросы
Комментарии преподавателя
Модуль адаптивной оптимизации запросов AQO (Adaptive Query Optimization) можно применить в тех случаях, когда стандартные инструменты улучшения оценок селективности и кардинальности (подробно описанные в курсе QPT) не срабатывают.
Модуль AQO работает с простыми условиями вида «атрибут = константа», «атрибут > константа» и подобными.
AQO улучшает оценку количества строк, умножая оценку планировщика на коэффициент коррекции AQO. Это может способствовать выбору лучшего плана и, следовательно, ускорению выполнения запросов.
Основная сфера применения AQO — сложные аналитические запросы с повторяющейся структурой, для которых EXPLAIN ANALYZE выдает неверную оценку кардинальности.
Причина неверной оценки в том, что рейс PG0007 всегда вылетает из Внуково, но планировщику об этом неизвестно.
В курсе QPT показано, как использовать расширенную статистику по функциональным зависимостям, чтобы исправить неверную оценку кардинальности. В этой теме мы улучшим оценку с помощью расширения AQO.
Page 5: Принцип работы AQO
{#source-figure-005 caption=“Source slide page 5: Принцип работы AQO”}
Сбор статистики по выполняемым запросам и улучшение оценок количества строк с помощью машинного обучения
Лучшие оценки — основа более оптимальных планов
Комментарии преподавателя
Модуль AQO собирает статистику по выполненным запросам для обучения модели, с помощью которой AQO предоставляет уточненные оценки. Именно это концептуально отличает AQO от обычного планировщика, на работу которого никак не влияют ранее выполненные запросы.
Для оценки кардинальности используется метод машинного обучения на базе алгоритма поиска ближайших соседей k-NN вместо обычной статистики планировщика.
Page 6: Принцип работы AQO
{#source-figure-006 caption=“Source slide page 6: Принцип работы AQO”}
Комментарии преподавателя
Расширение AQO включается параметром aqo.enable.
При исполнении плана запроса AQO получает статистическую информацию (признаки, features) о реальной кардинальности каждого узла плана — происходит обучение модели. Полученные данные сохраняются в файлах и при запуске AQO загружаются в разделяемую память.
Когда статистическая информация накоплена, модель может использоваться для уточнения оценки планировщика.
По умолчанию все запросы считаются принадлежащими одному классу. Поэтому статистика, собранная для узлов плана, будет применяться и к другим планам с теми же узлами.
Если установить параметру aqo.advanced значение on, выполняется классификация: в один класс попадают запросы, отличающиеся только константами. В таком случае статистика будет собираться и применяться для каждого класса отдельно.
Как именно обучается и используется модель, определяется режимом работы (параметр aqo.mode).
Если классификация не применяется (aqo.advanced = off), все запросы относятся к одному общему классу. В этом случае имеют смысл два режима работы: learn и frozen.
В использующемся по умолчанию режиме learn модель обучается на всех выполняемых запросах и используются для уточнения оценок всех планов.
Этот режим используется для начального обучения AQO: после включения расширения надо выполнять проблемные запросы, варьируя константы, до тех пор, пока не будет достигнуто необходимое качество их планирования. Обучать модель на всех запросах не требуется: обычно большая их часть планируется правильно и без AQO, а в некоторых случаях применение AQO может и ухудшить производительность.
Затем расширение переводится в режим frozen, в котором собранная статистика продолжает использоваться, но новые данные не собираются.
Этот режим позволяет стабилизировать планы и снизить влияние AQO на время планирования запросов. Конечно, при дальнейшем изменении данных точность планирования может ухудшиться; в таком случае можно на время вернуться в режим обучения, чтобы обновить статистику.
Page 8: Без классификации: режим learn
С помощью параметра aqo.enable включим aqo на уровне кластера и перечитаем конфигурацию:
QUERY PLAN
------------------------------------------------------------------------------------------
----------------------------------------
Bitmap Heap Scan on flights (cost=4.62..132.14 rows=2 width=63) (actual rows=61 loops=1)
AQO not used, fss=-4388671362309763767
Recheck Cond: (flight_no = 'PG0007'::bpchar)
Filter: (departure_airport = 'VKO'::bpchar)
Heap Blocks: exact=2
-> Bitmap Index Scan on flights_flight_no_scheduled_departure_key (cost=0.00..4.62
rows=44 width=0) (actual rows=61 loops=1)
Index Cond: (flight_no = 'PG0007'::bpchar)
Using aqo: true
AQO mode: LEARN
AQO advanced: OFF
Query hash: -6712624238167333493
JOINS: 0
(12 rows)
Для AQO этот запрос является новым (AQO not used), поэтому для планирования использовались обычные оценки. Но AQO уже получил данные для обучения модели. Найдем статистику AQO по идентификатору запроса (query hash):
QUERY PLAN
------------------------------------------------------------------------------------------
---------------------------------------
Bitmap Heap Scan on flights f (cost=4.62..132.14 rows=9 width=63) (actual rows=9
loops=1)
AQO: rows=9, error=0%, fss=-4388671362309763767
Recheck Cond: (flight_no = 'PG0047'::bpchar)
Filter: (departure_airport = 'OVB'::bpchar)
Heap Blocks: exact=1
-> Bitmap Index Scan on flights_flight_no_scheduled_departure_key (cost=0.00..4.62
rows=44 width=0) (actual rows=9 loops=1)
Index Cond: (flight_no = 'PG0047'::bpchar)
Using aqo: true
AQO mode: LEARN
AQO advanced: OFF
Query hash: -6712624238167333493
JOINS: 0
(12 rows)
Обратите внимание, что расширение ориентируется на дерево разбора, и поэтому не чувствительно к таким изменениям, как регистр текста, переносы строк, псевдонимы таблиц и т. п.
Более того: поскольку статистика не делится по классам запросов, накопленная информация об узлах будет применяться к любым планам, которые содержат известные узлы (сравните подписи к узлам Nested Loop и Bitmap Heap Scan).
QUERY PLAN
------------------------------------------------------------------------------------------
----------------------------------------
Bitmap Heap Scan on flights (cost=4.63..132.15 rows=31 width=63) (actual rows=26
loops=1)
AQO: rows=31, error=16%, fss=-4388671362309763767
Recheck Cond: (flight_no = 'PG0005'::bpchar)
Filter: (departure_airport = 'DME'::bpchar)
Heap Blocks: exact=2
-> Bitmap Index Scan on flights_flight_no_scheduled_departure_key (cost=0.00..4.62
rows=44 width=0) (actual rows=26 loops=1)
Index Cond: (flight_no = 'PG0005'::bpchar)
Using aqo: true
AQO mode: FROZEN
AQO advanced: OFF
Query hash: -6712624238167333493
JOINS: 0
(12 rows)
Page 12: Классификация запросов
Режимы learn и frozen
Режим controlled
статистика применяется (use_aqo),
но обновляется (learn_aqo) только для известных запросов
стабилизация без «заморозки»
Режим intelligent
автоматическое управление обучением и применением (auto_tune)
сначала только сбор статистики;
применение при реальном улучшении, иначе — отказ от использования
Тонкая настройка
ручное управление свойствами use_aqo, learn_aqo, auto_tune
Комментарии преподавателя
При включенной классификации (aqo.advanced = on) статистика собирается отдельно для каждого класса запросов; класс составляют запросы, у которых деревья разбора отличаются только константами. В этом случае к уже рассмотренным режимам работы learn и frozen добавляются еще два: controlled и intelligent (без классификации они не отличаются от обычного learn).
В режиме controlled модель применяется для уже известных классов запросов: она продолжает обучаться на них (свойство learn_aqo) и используется для уточнения оценок (use_aqo). Но для новых классов запросов статистика не собирается. Этот режим можно использовать вместо frozen, чтобы тратить ресурсы только на нужные запросы, но при этом не «замораживать» статистику, а продолжать обновлять ее.
В режиме intelligent расширение самостоятельно управляет обучением и использованием (свойство auto_tune). Новые запросы сначала выполняются шесть раз без AQO, а затем обученная модель начинает использоваться (включается свойство use_aqo). Далее модель может выключаться или включаться в зависимости от того, уменьшает ли ее использование время планирования и выполнения запроса. Если после 50 итераций улучшения не происходит, AQO перестает собирать статистику по данному классу запросов и пытаться ее применить (выключает свойства use_aqo и learn_aqo).
Этот режим работы следует применять с осторожностью, поскольку принимаемые решения могут оказаться недостаточно точными.
Свойствами use_aqo, learn_aqo и auto_tune можно управлять вручную отдельно для каждого класса запросов.
Page 13: С классификацией: режим controlled
Включим теперь классификацию запросов, удалим все данные машинного обучения и повторим эксперименты.
=> SET aqo.advanced = on;
SET
=>SELECTaqo_reset();
aqo_reset
-----------
5
(1 row)
Сначала обучим модель на запросах одного класса (класс составляют запросы с одинаковым деревом разбора без учета констант).
QUERY PLAN
------------------------------------------------------------------------------------------
---------------------------------------
Bitmap Heap Scan on flights (cost=4.64..132.16 rows=61 width=63) (actual rows=9 loops=1)
AQO: rows=61, error=85%, fss=-4388671362309763767
Recheck Cond: (flight_no = 'PG0047'::bpchar)
Filter: (departure_airport = 'OVB'::bpchar)
Heap Blocks: exact=1
-> Bitmap Index Scan on flights_flight_no_scheduled_departure_key (cost=0.00..4.62
rows=44 width=0) (actual rows=9 loops=1)
Index Cond: (flight_no = 'PG0047'::bpchar)
Using aqo: true
AQO mode: LEARN
AQO advanced: ON
Query hash: -6712624238167333493
JOINS: 0
(12 rows)
Если раньше информация об известных узлах применялась ко всем запросам, то теперь для каждого класса собирается собственная статистика. Поэтому в следующем запросе все узлы имеют подпись AQO not used — именно для такого класса узел Bitmap Heap Scan еще не встречался:
QUERY PLAN
------------------------------------------------------------------------------------------
----------------------------------------
Bitmap Heap Scan on flights (cost=4.64..132.16 rows=61 width=63) (actual rows=61
loops=1)
AQO: rows=61, error=0%, fss=-4388671362309763767
Recheck Cond: (flight_no = 'PG0505'::bpchar)
Filter: (departure_airport = 'SVO'::bpchar)
Heap Blocks: exact=1
-> Bitmap Index Scan on flights_flight_no_scheduled_departure_key (cost=0.00..4.62
rows=44 width=0) (actual rows=61 loops=1)
Index Cond: (flight_no = 'PG0505'::bpchar)
Using aqo: true
AQO mode: CONTROLLED
AQO advanced: ON
Query hash: -6712624238167333493
JOINS: 0
(12 rows)
При включенной классификации можно вручную управлять отдельными свойствами классов, устанавливая, должна ли модель обучаться и должна ли она использоваться для уточнения оценок:
QUERY PLAN
------------------------------------------------------------------------------------------
----------------------------------------
Bitmap Heap Scan on flights (cost=4.63..132.15 rows=51 width=63) (actual rows=26
loops=1)
AQO: rows=51, error=49%, fss=-4388671362309763767
Recheck Cond: (flight_no = 'PG0005'::bpchar)
Filter: (departure_airport = 'DME'::bpchar)
Heap Blocks: exact=2
-> Bitmap Index Scan on flights_flight_no_scheduled_departure_key (cost=0.00..4.62
rows=44 width=0) (actual rows=26 loops=1)
Index Cond: (flight_no = 'PG0005'::bpchar)
Using aqo: true
AQO mode: CONTROLLED
AQO advanced: ON
Query hash: -6712624238167333493
JOINS: 0
(12 rows)
В режиме intelligent можно управлять и свойством auto_tune.
С классификацией: режим intelligent
Отключим параллельное выполнение для упрощения планов запросов:
=> SET max_parallel_workers_per_gather = 0;
SET
Сбросим еще раз статистику AQO и установим режим intelligent:
Page 17: Page 17
=>SELECTaqo_reset();
aqo_reset
-----------
10
(1 row)
=> SET aqo.mode = intelligent;
SET
Включим параметр, позволяющий собирать статистику при отключенном использовании AQO:
=> SET aqo.force_collect_stat = on;
SET
Несколько раз выполним запрос с большой ошибкой оценки кардинальности. В режиме intelligent сначала происходит обучение, но оценки при планировании не уточняются.
QUERY PLAN
------------------------------------------------------------------------------------------
------------------------------------------
Nested Loop (cost=0.42..30075.24 rows=38 width=136) (actual time=0.194..403.252
rows=30133 loops=1)
AQO not used, fss=0
-> Seq Scan on ticket_flights tf (cost=0.00..29754.52 rows=38 width=32) (actual
time=0.145..211.525 rows=30133 loops=1)
AQO not used, fss=0
Filter: ((amount > '50000'::numeric) AND ((fare_conditions)::text =
'Business'::text) AND (length(ticket_no) = 13))
Rows Removed by Filter: 1015593
-> Index Scan using tickets_pkey on tickets t (cost=0.42..8.44 rows=1 width=104)
(actual time=0.006..0.006 rows=1 loops=30133)
AQO not used, fss=0
Index Cond: (ticket_no = tf.ticket_no)
Planning Time: 842.994 ms
Execution Time: 405.288 ms
Using aqo: false
AQO mode: INTELLIGENT
AQO advanced: ON
Query hash: 4602601022030847932
JOINS: 1
(16 rows)
=> \g /dev/null
=> \g /dev/null
=> \g /dev/null
=> \g /dev/null
=> \g /dev/null
На седьмой раз AQO начинает использовать модель и исправляет оценку кардинальности. Обратите внимание, что при этом поменялся и алгоритм соединения — вложенному циклу планировщик предпочел хеширование:
Теперь, когда модель используется, автоматика режима intelligent будет смотреть на среднее время планирования и выполнения запроса с AQO и без AQO, включая или выключая использование модели. Выполним запрос еще несколько раз и проверим статистику:
Если AQO не даст выигрыша по времени выполнения в течение 50 выполнений, оптимизация для данного класса запроса совсем отключится.
Page 20: Песочница
Отдельная база знаний
в общей памяти сервера
изолирована от основной базы
общая для всех сеансов, где включена (aqo.sandbox = on)
Позволяет
экспериментировать с AQO, не влияя на основную активность
работать на физической реплике
Комментарии преподавателя
В AQO есть возможность организовать дополнительную базу знаний, не связанную с основной — песочницу.
На сервере существует единственная песочница, которая располагается в его общей памяти. Во всех сеансах, в которых задано значение aqo.sandbox = on, расширение AQO будет работает не с основной базой знаний, а с общей песочницей.
Это дает возможность экспериментировать с настройками, не влияя на выполняющиеся запросы пользователей, а затем переносить статистику в основную базу знаний.
Также песочница позволяет обучать AQO на запросах, выполняющихся на физической реплике. По умолчанию (если aqo.wal_rw = true) изменения основной базы знаний записываются в WAL и передаются на реплику. Поэтому запросы на реплике могут использовать статистику AQO основного сервера. Однако обучение на реплике невозможно, поскольку на ней нельзя изменять данные. Песочница позволяет обойти это ограничение.
Page 21: Песочница
Переключим AQO на песочницу в текущем сеансе:
=> SET aqo.sandbox = on;
SET
=> SET aqo.advanced = off;
SET
=> SET aqo.mode = learn;
SET
Сбросим статистику песочницы, не затрагивая основную базу знаний:
=>SELECTaqo_reset();
aqo_reset
-----------
0
(1 row)
Выполняем запрос, пока оценка кардинальности не станет удовлетворительной:
QUERY PLAN
------------------------------------------------------------------------------------------
-----------------------------
Seq Scan on ticket_flights (cost=0.00..24525.89 rows=30133 width=32) (actual
time=0.365..255.763 rows=30133 loops=1)
AQO: rows=30133, error=0%, fss=8156007843730226217
Filter: ((amount > '50000'::numeric) AND ((fare_conditions)::text = 'Business'::text))
Rows Removed by Filter: 1015593
Planning Time: 0.256 ms
Execution Time: 257.376 ms
Using aqo: true
AQO mode: LEARN
AQO advanced: OFF
Query hash: 5235755627948238838
JOINS: 0
(11 rows)
Page 23: Итоги
Адаптивная оптимизация запросов AQO — инструмент для улучшения планов методом машинного обучения
Автоматическое исправление оценок кардинальности, которого сложно достичь традиционными методами
Несколько режимов работы и возможность тонкой настройки на уровне отдельных классов запросов
Page 24: Практика
В базе данных demo создайте индекс для таблицы flights по столбцу scheduled_departure и для ticket_flights по столбцу fare_conditions.
Отключите параллельное выполнение, увеличьте размер рабочей памяти до 256 Мбайт и выполните запрос для получения количества пассажиров, летавших бизнес-классом с 01.08.2016 и прибывших с опозданием не более часа.