Давайте вернемся к такому скользкому моменту, как производительность при работе с базой данных. Вероятно вы сталкивались сами или слышали о таких ситуациях, когда есть ощущение, что что-то где-то тормозит. Обычно первая мысль у руководства — что косяк на стороне администраторов (службы эксплуатации, ну, в общем, вы поняли). И начинаются советы или даже упреки:
Очень сильно повезет, если доходят до разработчиков и начинают проверять, что же там на самом деле происходит.
Но это все плохо. Чтобы решить проблему «тормозов», нужно действовать так, как это делают (или должны делать) врачи в медицине.
Пациент: — Доктор, у меня все болит. И тут болит (тыкает пальцем в грудь), и тут (тыкает пальцем в живот), и даже тут (тыкает пальцем в колено)! Доктор: — Дайте посмотреть. (смотрит...) — Голубчик, так у вас палец сломан!
Сначала нужно сделать обследование, поставить правильный диагноз и уже потом лечить. Так сказать, бороться с причиной, а не со следствием.
Мы установили сервер БД, дали ему ресурсов с запасом (исходя из тестов). Настроили конфигурацию. Теперь остается только проверить, как БД обрабатывает запросы (которые были написаны командой разработчиков, а те, наверняка, будут утверждать, что они следовали всем канонам программирования и у них на десктопе все работает быстро).
Спорить в этом случае бесполезно, а вот сухие цифры и графики действуют очень убедительно. Пора научиться искать, где могут быть «тормоза», разбираться, почему они возникли, и как (и стоит ли вообще) с ними бороться.
Почему? В современной разработке важно сделать работающий продукт быстро. А не идеально вылизанный и оптимизированный, но через год. Поэтому используются фреймворки, различные ORM и другие средства и инструменты для ускорения разработки продукта. А вот когда проект уже «взлетел» и приносит деньги — его нужно оптимизировать, причесывать и наращивать мощность. Обычно все начинается снаружи. Ну, кому понравится, допустим, интернет-магазин, в котором поиск товара занимает 2 секунды — это очень много. И оптимизацию нужно начинать с конфигурации базы: правильной настройки памяти, диска, автовакуума и пр.
И только, когда это сделано, переходить к оптимизации «медленных запросов». Алгоритм этой оптимизации довольно прост:
Что стоит оптимизировать? Ответ на этот вопрос многогранен, если в двух словах — оптимизировать нужно только то, что имеет смысл оптимизировать. Нужно взять топ «медленных» запросов и посмотреть на них.
Можно выделить три основных сценария:
— Привет. — Привет. — Как там ваши дела с Кристиной? Еще не поженились? — Нет, мы расстались. — А что случилось? — Мне надоело, у нее были слишком большие запросы. — Например, какие? — Ну например, update instance inner join (select group.id as group_id, (select message.id from message inner join thread on thread.id = message.thread_id where location_id = @location_id and language_id = @language_id and concat(group_key, '.') like concat(group.
key, '.%') order by message.created desc limit 1) as last_message_id, (select count(*) from thread where location_id = @location_id and language_id = @language_id and concat(group_key, '.') like concat(group.key, '.%')) as thread_count, (select if(sum(thread.message_count) is null, 0, sum(thread.message_count)) from thread where location_id = @location_id and language_id = @language_id and concat(group_key, '.') like concat(group.key, '.%')) as message_count from group where @channel_key like concat(key, '.%')) as statistics on statistics.group_id = instance.group_id set instance.message_id = statistics.last_message_id, instance.thread_count = statistics.thread_count, instance.message_count = statistics.message_count where instance.location_id = @location_id and instance.language_id = @language_id;
Вывод — берем только те запросы, что приносят пользу (имеют больший вес), и начинаем оптимизировать.
Как?
EXPLAIN:
Быстродействие запросов зависит от многих факторов. На некоторые из них могут воздействовать пользователи, а другие являются фундаментальными особенностями системы.
Выполняя любой полученный запрос, PostgreSQL разрабатывает для него план запроса. Выбор правильного плана, соответствующего структуре запроса и характеристикам данным, крайне важен для хорошей производительности. Поэтому в системе работает сложный планировщик, задача которого — подобрать хороший план. Узнать, какой план был выбран для какого-либо запроса, можно с помощью команды EXPLAIN. Понимание плана — это искусство, и чтобы овладеть им, нужен определенный опыт, мы же с вами слегка коснемся только самых простых методов.
EXPLAIN ANALYZE:
Не только строит план выполнения запроса, но еще выполняет его и показывает как изначальный план, так и реальный процесс его выполнения. Размерность, в которой команды оценивают полезность — COST (это время, затраченное на извлечение одного блока размером 8Kb при последовательном seq scan).
Обращайте внимание на следующее:
Следует отметить, что полный просмотр таблицы далеко не всегда медленнее просмотра по индексу. Если, например, в таблице несколько сотен записей, умещающихся в одном-двух блоках на диске, то использование индекса приведет лишь к тому, что придется читать еще и пару лишних блоков индекса. Если в запросе придется выбрать 80% записей из большой таблицы, то полный просмотр опять же получится быстрее.
При тестировании запросов с использованием EXPLAIN ANALYZE можно воспользоваться настройками, запрещающими оптимизатору использовать определенные планы выполнения. Например,
SET enable_seqscan=false;
запретит использование полного просмотра таблицы, и вы сможете выяснить, прав ли был оптимизатор, отказываясь от использования индекса.
Ни в коем случае не следует прописывать подобные команды в postgresql.conf! Это может ускорить выполнение нескольких запросов, но сильно замедлит все остальные!
Использование индексов
Опыт показывает, что наиболее значительные проблемы с производительностью вызываются отсутствием нужных индексов. Поэтому, столкнувшись с медленным запросом, в первую очередь проверьте, существуют ли индексы, которые он может использовать. Если нет — постройте их.
Излишек индексов, впрочем, тоже чреват проблемами:
Единственное, что можно сказать с большой степенью определенности — поля, являющиеся внешними ключами, и поля, по которым объединяются таблицы, индексировать надо обязательно.
${base_domain}).INSERT INTO pgsql SELECT n , md5 (random()::text) FROM generate_series (1, 100000) AS foo(n);
Получите план выполнения запроса, который̆ выбирает все записи из таблицы pgsql, с помощью команды EXPLAIN.
EXPLAIN SELECT * FROM pgsql;
В выводе EXPLAIN найдите и сохраните в файл /opt/cost_preview.txt стоимость выборки первой строки, стоимость выборки всех строк, количество рядов (rows), а также среднею ширину кортежа в байтах. В формате:
cost=*.**..****.** rows=****** width=**
Выполните ANALYSE таблицы pgsql. Затем повторите EXPLAIN запрос из предыдущего задания.
Вы должны увидеть, что результат вывода изменился, найдите и сохраните в файл /opt/cost.txt стоимость выборки первой строки, стоимость выборки всех строк, количество рядов (rows), а также среднюю ширину кортежа в байтах.
Выполните запрос стоимости, который возвращают все строки, идентификатор которых принадлежит диапазону от 10 до 20 с помощью команды:
EXPLAIN ANALYZE SELECT * FROM pgsql WHERE id >= 10 and id < 20;
EXPLAIN SELECT * FROM pgsql WHERE upper(id::text)::int < 20;
Создать таблицу success_practice с полями (id int, description text, pgsql_id int references pgsql(id)) в бд task12.
Заполнить таблицу success_practice данными.
INSERT INTO success_practice (id, description, pgsql_id) SELECT n, md5(n::text), random()*99999+1 FROM generate_series(1,200000) AS foo(n);
Получите историю успеха из таблицы pgsql, написав простой запрос, соединяющий таблицы pgsql и success_practice.
EXPLAIN ANALYZE SELECT * FROM table_name1 inner JOIN table_name2 on table_name1.id = table_name2.table_name1_id WHERE table_name1_id = 1000;
Cоздайте индекс по внешнему ключу для таблицы success_practice.
CREATE index on success_practice (pgsql_id);
Повторите запрос из п.14.
Чтобы начать выполнение задания, укажите в настройках вашу должность, название компании и аккаунт в Telegram
Заполнить