Визуализация SQL-запросов
Ментальная модель, помогающая представить, как выполняются SQL-запросы.
Фактическая последовательность выполнения может отличаться от этой модели из-за стратегий оптимизации, применяемых оптимизатором запросов.
📲 Мы в MAX
#db
👉 @database_info
Ментальная модель, помогающая представить, как выполняются SQL-запросы.
Фактическая последовательность выполнения может отличаться от этой модели из-за стратегий оптимизации, применяемых оптимизатором запросов.
#db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🥴1
Андрей Бородин, руководитель разработки СУБД с открытым исходным кодом в Yandex Cloud, вошел в топ-50 главных контрибьюторов PostgreSQL
Разработчик получил статус major contributor за многолетний вклад в развитие сообщества и кодовой базы PostgreSQL: ревью патчей, менторинг младших разработчиков и улучшение инструментов WAL‑G, Odyssey и SPQR.
По данным Stack Overflow 2025 PostgreSQL используют более 55,6% опытных разработчиков баз данных в компаниях разного масштаба Особенно активно она применяется в ИИ-проектах.
Команда разработки СУБД Yandex Cloud работает в тесной связке с сообществом PostgreSQL — каждый год в релиз проекта попадает множество доработок от сотрудников компании. При этом процесс принятия изменений в ядро PostgreSQL считается одним из самых строгих в сообществе, поэтому успешный апстрим-патч — знак качества кода.
Помимо вклада в PostgreSQL, команда Андрея разработала решение с открытым исходным кодом SPQR. На его основе в 2025 году в режиме превью был запущен Managed Service for Shared PostgreSQL. Он обеспечивает горизонтальное масштабирование баз данных через шардирование — разделение данных между разными серверами.
Разработчик получил статус major contributor за многолетний вклад в развитие сообщества и кодовой базы PostgreSQL: ревью патчей, менторинг младших разработчиков и улучшение инструментов WAL‑G, Odyssey и SPQR.
По данным Stack Overflow 2025 PostgreSQL используют более 55,6% опытных разработчиков баз данных в компаниях разного масштаба Особенно активно она применяется в ИИ-проектах.
Команда разработки СУБД Yandex Cloud работает в тесной связке с сообществом PostgreSQL — каждый год в релиз проекта попадает множество доработок от сотрудников компании. При этом процесс принятия изменений в ядро PostgreSQL считается одним из самых строгих в сообществе, поэтому успешный апстрим-патч — знак качества кода.
Помимо вклада в PostgreSQL, команда Андрея разработала решение с открытым исходным кодом SPQR. На его основе в 2025 году в режиме превью был запущен Managed Service for Shared PostgreSQL. Он обеспечивает горизонтальное масштабирование баз данных через шардирование — разделение данных между разными серверами.
🔥11👍4❤1🏆1
SQL JOINs наглядно: как работать с объединением таблиц
Хотите лучше понимать SQL JOIN? Вот наглядная шпаргалка с примерами и визуализацией!
🔹 INNER JOIN – пересечение двух таблиц, возвращает только совпадающие строки.
🔹 FULL JOIN – объединяет все данные из обеих таблиц, заполняя пропущенные значения NULL.
🔹 FULL JOIN с фильтрацией NULL – выбирает только строки, которые есть только в одной из таблиц.
🔹 LEFT JOIN – возвращает все строки из A и совпадающие строки из B.
🔹 LEFT JOIN (только уникальные в A) – возвращает только строки из A, которых нет в B.
🔹 RIGHT JOIN – аналогично LEFT JOIN, но с приоритетом B.
🔹 RIGHT JOIN (только уникальные в B) – выбирает строки, которые есть в B, но отсутствуют в A.
Сохраняйте в закладки и пользуйтесь! ⚡
📲 Мы в MAX
#db
👉 @database_info
Хотите лучше понимать SQL JOIN? Вот наглядная шпаргалка с примерами и визуализацией!
🔹 INNER JOIN – пересечение двух таблиц, возвращает только совпадающие строки.
SELECT *
FROM A
INNER JOIN B ON A.key = B.key;🔹 FULL JOIN – объединяет все данные из обеих таблиц, заполняя пропущенные значения NULL.
SELECT *
FROM A
FULL JOIN B ON A.key = B.key;🔹 FULL JOIN с фильтрацией NULL – выбирает только строки, которые есть только в одной из таблиц.
SELECT *
FROM A
FULL JOIN B ON A.key = B.key
WHERE A.key IS NULL OR B.key IS NULL;🔹 LEFT JOIN – возвращает все строки из A и совпадающие строки из B.
SELECT *
FROM A
LEFT JOIN B ON A.key = B.key;🔹 LEFT JOIN (только уникальные в A) – возвращает только строки из A, которых нет в B.
SELECT *
FROM A
LEFT JOIN B ON A.key = B.key
WHERE B.key IS NULL;🔹 RIGHT JOIN – аналогично LEFT JOIN, но с приоритетом B.
SELECT *
FROM A
RIGHT JOIN B ON A.key = B.key;🔹 RIGHT JOIN (только уникальные в B) – выбирает строки, которые есть в B, но отсутствуют в A.
SELECT *
FROM A
RIGHT JOIN B ON A.key = B.key
WHERE B.key IS NULL;Сохраняйте в закладки и пользуйтесь! ⚡
#db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🔥5❤2
Оптимизация запросов: как найти узкое место? 🔍
Привет, коллеги! Сегодня я покажу вам, как находить узкие места в SQL-запросах и оптимизировать их. Если ваш запрос работает медленно, скорее всего, проблема в одном из трех мест:
1️⃣ Неверные индексы
- Проверьте
- Добавьте индексы на часто фильтруемые и соединяемые поля.
2️⃣ Проблемные JOIN'ы
- Проверьте, какие типы
- Используйте
3️⃣ Громоздкие операции (GROUP BY, ORDER BY, DISTINCT)
- Сортировка и группировка требуют много ресурсов.
- Можно ли заменить
- Используйте индексированные столбцы в
📲 Мы в MAX
#db
👉 @database_info
Привет, коллеги! Сегодня я покажу вам, как находить узкие места в SQL-запросах и оптимизировать их. Если ваш запрос работает медленно, скорее всего, проблема в одном из трех мест:
1️⃣ Неверные индексы
- Проверьте
EXPLAIN ANALYZE, если используется Seq Scan вместо Index Scan, значит, индексы либо отсутствуют, либо неэффективны. - Добавьте индексы на часто фильтруемые и соединяемые поля.
2️⃣ Проблемные JOIN'ы
- Проверьте, какие типы
JOIN используются. NESTED LOOP JOIN может быть проблемой на больших таблицах. - Используйте
HASH JOIN или MERGE JOIN, если это возможно. 3️⃣ Громоздкие операции (GROUP BY, ORDER BY, DISTINCT)
- Сортировка и группировка требуют много ресурсов.
- Можно ли заменить
DISTINCT на EXISTS? - Используйте индексированные столбцы в
ORDER BY. #db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
🧠 Почему PostgreSQL иногда "зависает" на UPDATE и как это пофиксить
Сегодня я расскажу про одну интересную особенность PostgreSQL, с которой сталкивался лично: внезапные подвисания при
📌 Проблема часто кроется в отсутствии индекса на колонку фильтра в
Если на
1. Прочитать кучу ненужных версий строк (MVCC).
2. Проверять видимость каждой строки.
3. Иногда ещё и ждать завершения других транзакций, держащих старые снапшоты.
🛠 Что делать:
- Проверить наличие индекса на колонку фильтра:
- Проверить состояние autovacuum:
- Можно вручную запустить:
🔥 Лайфхак: если
📲 Мы в MAX
#db
👉 @database_info
Сегодня я расскажу про одну интересную особенность PostgreSQL, с которой сталкивался лично: внезапные подвисания при
UPDATE большого количества строк. Причём CPU почти не загружен, а запрос как будто "висит".📌 Проблема часто кроется в отсутствии индекса на колонку фильтра в
WHERE. Пример:
UPDATE orders SET status = 'archived' WHERE created_at < '2022-01-01';
Если на
created_at нет индекса, то PostgreSQL делает sequential scan всей таблицы. А теперь внимание: если в таблице много "мертвых" строк, которых ещё не убрал autovacuum, то PostgreSQL должен:1. Прочитать кучу ненужных версий строк (MVCC).
2. Проверять видимость каждой строки.
3. Иногда ещё и ждать завершения других транзакций, держащих старые снапшоты.
🛠 Что делать:
- Проверить наличие индекса на колонку фильтра:
CREATE INDEX idx_orders_created_at ON orders(created_at);
- Проверить состояние autovacuum:
SELECT relname, n_dead_tup, last_vacuum, last_autovacuum
FROM pg_stat_user_tables ORDER BY n_dead_tup DESC;
- Можно вручную запустить:
VACUUM ANALYZE orders;
🔥 Лайфхак: если
UPDATE всё равно медленный, попробуй его разбить на батчи по 10 000 строк. Это снизит нагрузку и ускорит выполнение.#db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍5
📊 Зачем DBA нужно уметь читать планы выполнения запросов (EXPLAIN)?
Почему навык чтения плана выполнения запроса - это не просто галочка в резюме, а реальный способ спасать прод от тормозов и неожиданных фулл-сканов.
Когда приходит запрос от разработчика: "Почему тормозит?" - ты открываешь
И тут всё понятно: фильтрация идёт по колонке без индекса, Postgres делает полный проход по таблице. Один
Но не всё так просто. Иногда план говорит:
А запрос всё равно медленный. Почему?
➡️
Каждый EXPLAIN - как рентген. Не читаешь - лечишь наугад.
📲 Мы в MAX
#db
👉 @database_info
Почему навык чтения плана выполнения запроса - это не просто галочка в резюме, а реальный способ спасать прод от тормозов и неожиданных фулл-сканов.
Когда приходит запрос от разработчика: "Почему тормозит?" - ты открываешь
EXPLAIN (ANALYZE, BUFFERS) и видишь:
Seq Scan on users (cost=0.00..44231.00 rows=1000000 width=64)
Filter: (status = 'active')
И тут всё понятно: фильтрация идёт по колонке без индекса, Postgres делает полный проход по таблице. Один
CREATE INDEX - и запрос летит 🚀Но не всё так просто. Иногда план говорит:
Index Scan using idx_users_status on users
Index Cond: (status = 'active')
А запрос всё равно медленный. Почему?
➡️
Buffers: shared hit=5 read=100000 dirtied=0 - вот оно. Индекс-то используется, но данные не в кэше, приходится читать с диска. А диск медленный. Решение? Подумать о горячем кэше, пачке RAM или REINDEX, если индекс раздулся.Каждый EXPLAIN - как рентген. Не читаешь - лечишь наугад.
#db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤1
This media is not supported in your browser
VIEW IN TELEGRAM
Как работают джойны SQL?
На приведенной ниже схеме подробно показано, как работают 4 типа объединений SQL.
🔹INNER JOIN возвращает совпадающие строки в обеих таблицах
🔹LEFT JOIN возвращает все записи из левой таблицы и соответствующие записи из правой
🔹RIGHT JOIN возвращает все записи из правой таблицы и соответствующие записи из левой
🔹FULL OUTER JOIN возвращает все записи, в которых есть совпадения, в левой или правой таблице
📲 Мы в MAX
#db
👉 @database_info
На приведенной ниже схеме подробно показано, как работают 4 типа объединений SQL.
🔹INNER JOIN возвращает совпадающие строки в обеих таблицах
🔹LEFT JOIN возвращает все записи из левой таблицы и соответствующие записи из правой
🔹RIGHT JOIN возвращает все записи из правой таблицы и соответствующие записи из левой
🔹FULL OUTER JOIN возвращает все записи, в которых есть совпадения, в левой или правой таблице
#db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3❤1
Сегодня расскажу вам про одну часто недооценённую, но крайне полезную SQL-фишку — CROSS APPLY в SQL Server (и его аналог в других СУБД —
Когда обычный
Допустим, у нас есть таблица
Что делает
Он буквально говорит: «Для каждой строки из
Аналог в PostgreSQL:
🔥 Используйте
- Нужна подстрочная логика внутри запроса
- Не получается реализовать через обычный
- Вы работаете с функциями, которые возвращают таблицу (TVF)
📲 Мы в MAX
#db
👉 @database_info
LATERAL).Когда обычный
JOIN бессиленДопустим, у нас есть таблица
Orders, и мы хотим для каждой строки выбрать топ-1 продукт по сумме, но выборка зависит от строки — тут уже обычный JOIN не справится. Вот пример, где приходит на помощь CROSS APPLY:
SELECT
o.OrderID,
p.ProductName,
p.Amount
FROM Orders o
CROSS APPLY (
SELECT TOP 1 *
FROM Products p
WHERE p.OrderID = o.OrderID
ORDER BY p.Amount DESC
) p;
Что делает
CROSS APPLY?Он буквально говорит: «Для каждой строки из
Orders выполни подзапрос с её параметрами». Это похоже на foreach, где внутренняя выборка может меняться в зависимости от строки внешней таблицы.Аналог в PostgreSQL:
SELECT
o.order_id,
p.product_name,
p.amount
FROM orders o,
LATERAL (
SELECT *
FROM products p
WHERE p.order_id = o.order_id
ORDER BY p.amount DESC
LIMIT 1
) p;
🔥 Используйте
CROSS APPLY, когда:- Нужна подстрочная логика внутри запроса
- Не получается реализовать через обычный
JOIN- Вы работаете с функциями, которые возвращают таблицу (TVF)
#db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤3
SQL. Как работать с датами в SQL Server
Из строки в дату и обратно
Приведение строки к дате
Приведение даты к строке
Приведение даты к строке в заданном формате:
Полезные функции по работе с датами
Полезные вычисления по датам (типовые задачи)
📲 Мы в MAX
#db
👉 @database_info
Из строки в дату и обратно
Приведение строки к дате
-- формат dd.mm.yyyy
select try_convert(date, isnull(@fielddateBirth,'01.01.1980'), 104)
-- формат yyyy-mm-dd
select try_convert(date, isnull(@fielddateBirth,'1980-01-01'), 23)
Приведение даты к строке
-- формат dd.MM.yyyy
select try_convert(nvarchar(16), getdate(), 104)
-- формат yyyy-mm-dd
select convert(nvarchar, isnull(dateBirth,'1900-01-01'), 23)
Приведение даты к строке в заданном формате:
select format(getdate(), 'dd.MM.yyyy HH:mm')
Полезные функции по работе с датами
select getdate() -- получить текущую дату
select dateadd(day, 7, getdate()) -- добавить 7 дней к дате
select dateadd(month, -2, getdate()) -- отнять 2 месяца от даты
select datediff(minute, date1, date2) -- получить разницу в минутах между date2 и date1
SELECT DAY(GETDATE()) -- 28 получить день даты (аналогично month, year, week)
SELECT DATENAME(month, GETDATE()) -- July получить название месяца
SELECT DATEPART(month, GETDATE()) -- 7 получить часть даты (например, месяц, год или день)
SELECT DATEFROMPARTS(2017, 7, 28) -- 2017-07-28 строит дату из частей
SELECT ISDATE('2017-07-28') -- 1 - проверка является ли строка датой
Полезные вычисления по датам (типовые задачи)
-- начало и конец недели
SET DATEFIRST 4 /* or use any other weird value to test it */
DECLARE @d DATETIME = GETDATE()
SELECT
@d ThatDate,
cast( DATEADD(dd, 0 - (@@DATEFIRST + 5 + DATEPART(dw, @d)) % 7, @d) as date) Monday,
cast(DATEADD(dd, 6 - (@@DATEFIRST + 5 + DATEPART(dw, @d)) % 7, @d) as date) Sunday
-- начало месяца
SELECT DATEADD(month, DATEDIFF(month, 0, @d), 0) AS StartOfMonth
-- номер недели в году
set datefirst 1;
select datepart(week, '2017-02-01');
-- получение дня недели
select datename(dw,getdate()) --Thursday
select datepart(dw,getdate()) --2
#db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3❤1
🧩 Сегодня покажу вам простой, но крайне полезный приём, как находить “тяжёлые” запросы в PostgreSQL, которые тормозят базу.
📌 Если у вас база под нагрузкой, и “что-то всё стало медленно”, первым делом проверьте:
Этот запрос показывает топ-5 самых долгих активных запросов. Обратите внимание на
А если хотите посмотреть историю медленных запросов за последние часы/дни - подключайте pg_stat_statements:
🔍 Тут видно, какие запросы в сумме "съели" больше всего времени. И это гораздо честнее, чем смотреть только на
💡Совет: подключите
📲 Мы в MAX
#db
👉 @database_info
📌 Если у вас база под нагрузкой, и “что-то всё стало медленно”, первым делом проверьте:
SELECT pid, now() - query_start AS duration, query
FROM pg_stat_activity
WHERE state = 'active'
ORDER BY duration DESC
LIMIT 5;
Этот запрос показывает топ-5 самых долгих активных запросов. Обратите внимание на
query_start - именно он поможет понять, кто завис и тормозит остальных.А если хотите посмотреть историю медленных запросов за последние часы/дни - подключайте pg_stat_statements:
SELECT
calls,
total_time,
mean_time,
query
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 10;
🔍 Тут видно, какие запросы в сумме "съели" больше всего времени. И это гораздо честнее, чем смотреть только на
mean_time или calls по отдельности.💡Совет: подключите
pg_stat_statements на проде и делайте такой анализ хотя бы раз в неделю. Это поможет находить проблемные места в приложении до того, как начнётся пожар.#db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12
🎯 Сегодня покажу простой способ ускорить запросы в PostgreSQL, даже не трогая сам SQL-код.
Часто вижу, как разработчики и админы оптимизируют запросы, играя с индексами или переписывая
👨🔧 Простой пример:
Запускаешь — и вдруг сложный
🧠 Совет: если ты регулярно заливаешь данные в таблицы (например, через ETL или бэкапы) — добавь
Можно даже так:
Так ты и "мусор" уберёшь, и статистику обновишь за один проход.
📲 Мы в MAX
#db
👉 @database_info
Часто вижу, как разработчики и админы оптимизируют запросы, играя с индексами или переписывая
JOIN'ы. Но забывают про один мощный инструмент — ANALYZE.ANALYZE обновляет статистику по таблицам. Эта статистика — хлеб для планировщика запросов. Если она устарела, PostgreSQL может выбрать неэффективный план, даже если у вас всё индексировано как надо.👨🔧 Простой пример:
ANALYZE my_big_table;
Запускаешь — и вдруг сложный
JOIN срабатывает в разы быстрее. Потому что PostgreSQL теперь знает, какие там объемы данных, сколько уникальных значений в колонках и т.п.🧠 Совет: если ты регулярно заливаешь данные в таблицы (например, через ETL или бэкапы) — добавь
ANALYZE в конец процедуры. Это дёшево, но может дать мощный прирост производительности.Можно даже так:
VACUUM ANALYZE my_big_table;
Так ты и "мусор" уберёшь, и статистику обновишь за один проход.
#db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
🧩 Как сделать backup PostgreSQL с минимальной нагрузкой на прод?
Сегодня покажу один из самых эффективных способов бэкапа PostgreSQL — с помощью
Сценарий: у нас есть продовый PostgreSQL и настроенная горячая реплика (streaming replication). Зачем использовать реплику для бэкапа?
Причины:
- 💡 На проде бэкап может замедлить отклик приложения.
- 🔁 Реплика — отличный способ разгрузить основной сервер.
- ⏱ Бэкап с
Как сделать:
Пояснения:
-
-
-
-
-
Важно:
Пользователь
А если добавить в cron, то получишь стабильный ночной бэкап без боли.
📲 Мы в MAX
#db
👉 @database_info
Сегодня покажу один из самых эффективных способов бэкапа PostgreSQL — с помощью
pg_basebackup + реплики.Сценарий: у нас есть продовый PostgreSQL и настроенная горячая реплика (streaming replication). Зачем использовать реплику для бэкапа?
Причины:
- 💡 На проде бэкап может замедлить отклик приложения.
- 🔁 Реплика — отличный способ разгрузить основной сервер.
- ⏱ Бэкап с
pg_basebackup возможен только на стопнутой БД или через репликацию.Как сделать:
pg_basebackup -h replica.host -U repl_user -D /backup/pg -F tar -z -P
Пояснения:
-
-h — адрес реплики-
-U — пользователь с правами репликации-
-D — куда класть бэкап-
-F tar -z — формат архива и сжатие-
-P — прогресс в консолиВажно:
Пользователь
repl_user должен быть прописан в pg_hba.conf и иметь роль REPLICATION.А если добавить в cron, то получишь стабильный ночной бэкап без боли.
#db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13
🚀 Сегодня покажу, как быстро диагностировать «тормоза» в PostgreSQL - без всяких внешних тулов и дополнительных логов. Только
Пользователи жалуются - "всё тормозит". Как понять, что именно? Открываем сессию в
📌 Что это нам даёт:
- Видим все активные (и зависшие) запросы.
- Сколько времени они уже выполняются (
- На чём конкретно «висят»: CPU, IO, Lock, Client и т.д. (
Пример:
→ Сразу ясно: кто-то держит блокировку на таблицу, и все остальные ждут.
🔥Чтобы найти виновника, можно запустить:
Этот запрос покажет, кто кого блокирует, и с каким запросом.
🙌 Это простая, но мощная техника диагностики. Помогала мне не раз в проде - особенно, когда времени мало, а багов много.
Ты пользуешься
📲 Мы в MAX
#db
👉 @database_info
pg_stat_activity и немного здравого смысла.Пользователи жалуются - "всё тормозит". Как понять, что именно? Открываем сессию в
psql от суперпользователя и запускаем:
SELECT pid, state, wait_event_type, wait_event, query, now() - query_start AS duration
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY duration DESC;
📌 Что это нам даёт:
- Видим все активные (и зависшие) запросы.
- Сколько времени они уже выполняются (
duration).- На чём конкретно «висят»: CPU, IO, Lock, Client и т.д. (
wait_event_type + `wait_event).Пример:
wait_event_type: Lock
wait_event: relation
→ Сразу ясно: кто-то держит блокировку на таблицу, и все остальные ждут.
🔥Чтобы найти виновника, можно запустить:
SELECT blocked_locks.pid AS blocked_pid,
blocking_locks.pid AS blocking_pid,
blocked_activity.query AS blocked_query,
blocking_activity.query AS blocking_query
FROM pg_locks blocked_locks
JOIN pg_locks blocking_locks ON blocked_locks.locktype = blocking_locks.locktype
AND blocked_locks.database IS NOT DISTINCT FROM blocking_locks.database
AND blocked_locks.relation IS NOT DISTINCT FROM blocking_locks.relation
AND blocked_locks.page IS NOT DISTINCT FROM blocking_locks.page
AND blocked_locks.tuple IS NOT DISTINCT FROM blocking_locks.tuple
AND blocked_locks.transactionid IS NOT DISTINCT FROM blocking_locks.transactionid
AND blocked_locks.classid IS NOT DISTINCT FROM blocking_locks.classid
AND blocked_locks.objid IS NOT DISTINCT FROM blocking_locks.objid
AND blocked_locks.objsubid IS NOT DISTINCT FROM blocking_locks.objsubid
AND blocked_locks.pid != blocking_locks.pid
JOIN pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
JOIN pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted;
Этот запрос покажет, кто кого блокирует, и с каким запросом.
🙌 Это простая, но мощная техника диагностики. Помогала мне не раз в проде - особенно, когда времени мало, а багов много.
Ты пользуешься
pg_stat_activity в проде? Или сразу лезешь в лог? Расскажи в комментах!#db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🔥2❤1🤡1
Как быстро найти “тяжёлые” запросы в PostgreSQL
Сегодня покажу простой способ найти самые ресурсоёмкие запросы, которые прямо сейчас выполняются в PostgreSQL. Это помогает, когда база начинает “тормозить”, а понять почему - сложно.
Используем pg_stat_activity и pg_stat_statements. Но сначала убедись, что pg_stat_statements включён:
Теперь сам запрос на поиск “тяжёлых” запросов:
А если интересует то, что прямо сейчас выполняется — тогда так:
Я часто сохраняю эти запросы в отдельный .sql-файл, чтобы запускать сразу при проблемах с производительностью. Полезно добавить в .psqlrc алиас или даже обернуть в скрипт.
Как вы ищете “тяжёлые” запросы в проде? Поделитесь в комментариях.
📲 Мы в MAX
#db
👉 @database_info
Сегодня покажу простой способ найти самые ресурсоёмкие запросы, которые прямо сейчас выполняются в PostgreSQL. Это помогает, когда база начинает “тормозить”, а понять почему - сложно.
Используем pg_stat_activity и pg_stat_statements. Но сначала убедись, что pg_stat_statements включён:
-- Проверка:
SELECT * FROM pg_extension WHERE extname = 'pg_stat_statements';
-- Включение (если не установлен):
CREATE EXTENSION pg_stat_statements;Теперь сам запрос на поиск “тяжёлых” запросов:
SELECT
query,
calls,
total_exec_time,
mean_exec_time,
rows
FROM
pg_stat_statements
ORDER BY
total_exec_time DESC
LIMIT 5;А если интересует то, что прямо сейчас выполняется — тогда так:
SELECT
pid,
now() - query_start AS duration,
state,
query
FROM
pg_stat_activity
WHERE
state != 'idle'
ORDER BY
duration DESC;Я часто сохраняю эти запросы в отдельный .sql-файл, чтобы запускать сразу при проблемах с производительностью. Полезно добавить в .psqlrc алиас или даже обернуть в скрипт.
Как вы ищете “тяжёлые” запросы в проде? Поделитесь в комментариях.
#db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤1
This media is not supported in your browser
VIEW IN TELEGRAM
Будьте осторожны при подсчете строк из outer join
Это происходит потому, что
Убедитесь, что
📲 Мы в MAX
#db
👉 @database_info
COUNT (*) => строки в группе; всегда хотя бы однаCOUNT ( inner_tab_col ) => строки из внутренней таблицы; ноль, если нет совпадений.Это происходит потому, что
COUNT ( col ) добавляет только ненулевые значения.Убедитесь, что
inner_tab_col является обязательным!#db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🧵 Сегодня я покажу вам простой, но мощный способ отладки сложных SQL-запросов
Когда у вас в проекте появляется монструозный запрос с десятками джойнов, подзапросов и оконных функций — ловить ошибки становится больно. Но есть подход, который реально спасает: инкрементальная отладка.
💡 Суть: разбиваем запрос на небольшие части и поочередно проверяем каждую
Вот как это делаю я:
1. Начинаю с ядра - самого внутреннего подзапроса или CTE. Проверяю, что он возвращает ожидаемые данные.
2. Добавляю следующий уровень логики - джойны, условия, группировки. Каждый раз выполняю и проверяю результат.
3. Для удобства использую
4. Сложные выражения и агрегаты выношу в отдельные CTE - это помогает быстрее изолировать проблему.
5. Если запрос очень тяжёлый - сохраняю промежуточные результаты в временные таблицы.
🔥 PostgreSQL позволяет использовать
📲 Мы в MAX
#db
👉 @database_info
Когда у вас в проекте появляется монструозный запрос с десятками джойнов, подзапросов и оконных функций — ловить ошибки становится больно. Но есть подход, который реально спасает: инкрементальная отладка.
💡 Суть: разбиваем запрос на небольшие части и поочередно проверяем каждую
Вот как это делаю я:
1. Начинаю с ядра - самого внутреннего подзапроса или CTE. Проверяю, что он возвращает ожидаемые данные.
2. Добавляю следующий уровень логики - джойны, условия, группировки. Каждый раз выполняю и проверяю результат.
3. Для удобства использую
WITH (CTE) - это даёт имена промежуточным результатам и делает запрос читабельным.4. Сложные выражения и агрегаты выношу в отдельные CTE - это помогает быстрее изолировать проблему.
5. Если запрос очень тяжёлый - сохраняю промежуточные результаты в временные таблицы.
🔥 PostgreSQL позволяет использовать
EXPLAIN (ANALYZE, BUFFERS) для профилирования на каждом этапе. Очень помогает найти, где тормозит.#db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥5
⚠️ Антипаттерн: использовать NULL без оглядки
На первый взгляд
🔸
🔸 Агрегации ведут себя странно.
🔸 Индексы и
🔸
💡 Как избежать проблем:
1. Всегда осознанно работай с
2. По возможности избегай
3. Добавляй проверки в коде:
4. Понимай, как твоя СУБД работает с
🎯 Вывод:
Сохрани, чтобы не отловить баг на проде 🐛
📲 Мы в MAX
#db
👉 @database_info
На первый взгляд
NULL — это просто “нет значения”. Но в реальности — это тихий саботаж:🔸
NULL != NULL. Да-да, сравнение NULL = NULL даст false или unknown. Это ломает привычную логику и может убить фильтры.🔸 Агрегации ведут себя странно.
COUNT(column) не считает NULL'ы. AVG, SUM — тоже их игнорируют. Итог: неверная статистика.🔸 Индексы и
WHERE column IS NULL. Не все СУБД эффективно используют индексы при таких запросах. Можно словить тормоза.🔸
NOT IN + NULL = 💥. Запрос WHERE id NOT IN (subquery) может вернуть пустой результат, если в подзапросе есть хотя бы один NULL.💡 Как избежать проблем:
1. Всегда осознанно работай с
NULL — используй IS NULL и IS NOT NULL, не = и !=.2. По возможности избегай
NULL в колонках, где это не нужно. Лучше использовать значения по умолчанию.3. Добавляй проверки в коде:
COALESCE, IFNULL, NVL и аналоги.4. Понимай, как твоя СУБД работает с
NULL в индексах и фильтрах.🎯 Вывод:
NULL — не пустота, а “неизвестность”. Обращайся с ним осторожно, иначе баги будут неявными и неприятными.Сохрани, чтобы не отловить баг на проде 🐛
#db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍3
Транзакции в SQLite: просто, но со своими нюансами
SQLite - это встраиваемая база данных, и она немного отличается от привычных серверных СУБД (PostgreSQL, MySQL) в части работы с транзакциями. Но транзакции там есть, и работают по принципу ACID - атомарность, согласованность, изолированность и долговечность.
Разберёмся по полочкам:
🔹 Как начинается и заканчивается транзакция?
Или, в случае ошибки:
Можно использовать синонимы:
-
-
-
Они отличаются уровнем блокировок.
🔹 Типы транзакций
1. DEFERRED (по умолчанию)
🔒 Блокировки ставятся только при первом доступе к таблице (на чтение/запись).
2. IMMEDIATE
🔒 Сразу ставит блокировку на запись (write-lock). Полезно, если точно знаешь, что будешь писать — исключишь гонки.
3. EXCLUSIVE
🔒 Блокирует БД полностью. Даже другие чтения не пройдут.
🔹 Особенности SQLite
- Одна запись за раз: SQLite поддерживает одновременные чтения, но только одну запись одновременно. Остальные получат "database is locked".
- Авто-коммиты: если явно не начать транзакцию - SQLite будет делать коммит после каждого запроса.
- Журналирование: SQLite использует WAL (write-ahead log) или rollback journal - в зависимости от настроек. WAL - более производителен для параллельного чтения.
💡 Советы
- При пакетной вставке всегда оборачивай в транзакцию:
→ Это в разы быстрее, чем отдельные
- Если получаешь ошибку
- Не оставил ли ты открытые транзакции
- Не работают ли несколько процессов с БД одновременно без координации
Сохрани, чтобы не потерять!
📲 Мы в MAX
#db
👉 @database_info
SQLite - это встраиваемая база данных, и она немного отличается от привычных серверных СУБД (PostgreSQL, MySQL) в части работы с транзакциями. Но транзакции там есть, и работают по принципу ACID - атомарность, согласованность, изолированность и долговечность.
Разберёмся по полочкам:
🔹 Как начинается и заканчивается транзакция?
BEGIN TRANSACTION;
-- какие-то запросы
COMMIT;
Или, в случае ошибки:
ROLLBACK;
Можно использовать синонимы:
-
BEGIN = BEGIN DEFERRED-
BEGIN IMMEDIATE-
BEGIN EXCLUSIVEОни отличаются уровнем блокировок.
🔹 Типы транзакций
1. DEFERRED (по умолчанию)
🔒 Блокировки ставятся только при первом доступе к таблице (на чтение/запись).
2. IMMEDIATE
🔒 Сразу ставит блокировку на запись (write-lock). Полезно, если точно знаешь, что будешь писать — исключишь гонки.
3. EXCLUSIVE
🔒 Блокирует БД полностью. Даже другие чтения не пройдут.
🔹 Особенности SQLite
- Одна запись за раз: SQLite поддерживает одновременные чтения, но только одну запись одновременно. Остальные получат "database is locked".
- Авто-коммиты: если явно не начать транзакцию - SQLite будет делать коммит после каждого запроса.
- Журналирование: SQLite использует WAL (write-ahead log) или rollback journal - в зависимости от настроек. WAL - более производителен для параллельного чтения.
💡 Советы
- При пакетной вставке всегда оборачивай в транзакцию:
BEGIN;
INSERT INTO users VALUES (...);
INSERT INTO users VALUES (...);
...
COMMIT;
→ Это в разы быстрее, чем отдельные
INSERT с автокоммитом.- Если получаешь ошибку
database is locked, проверь:- Не оставил ли ты открытые транзакции
- Не работают ли несколько процессов с БД одновременно без координации
Сохрани, чтобы не потерять!
#db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3❤1
Мероприятие: PG BootCamp Russia — официальное российское комьюнити-мероприятие PostgreSQL
Когда: весна 2026 г. (дата уточняется)
Где: г. Москва
Больше о мероприятиях PG BootCamp
В отличие от коммерческих конференций, предметом докладов выступает «ванильная» версия этой СУБД. Темы выступлений, связанные с коммерческими продуктами, не принимаются. Доклады (их обычно до 16 в два трека) делятся по темам разработки и эксплуатации. Формат предполагает камерную атмосферу, максимальную практическую пользу и содержательное профессиональное общение.
Открыт прием заявок на выступления:
🔹Исследование внутренней архитектуры PostgreSQL
🔹 Оптимизация производительности в высоконагруженных системах
🔹Анализ сложных задач и методов их решения
🔹 Инструменты и методологии для DBA
🔹 R&D-исследования, связанные с Postgres
Если у вас есть материалы, которым вы хотите поделиться с сообществом, — пожалуйста, присылайте тезисы. Это возможность не только представить свою работу, но и получить содержательную обратную связь от ведущих специалистов.
🎙Подать заявку на выступление
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🚨 SELECT * - скрытый враг в проде
На dev-сервере всё шустро. В проде — беда: запросы висят, база потеет. И вроде бы всё ок... пока не заглянешь в SQL:
На первый взгляд — удобно. Но:
🔻 Проблемы “SELECT *”:
– Тянет все колонки, даже ненужные. А их может быть 30+.
– Увеличивает нагрузку на сеть и память приложения.
– Ломает кэш — ведь даже малейшие изменения в колонках меняют структуру результата.
– Убивает индекс-only scan: Postgres не может использовать покрывающий индекс, если явно не указаны поля.
✅ Как надо:
🎯 Выбирай только нужные поля:
💡 Хочешь “быстро протестить” в dev-е? Ок. Но не пускай такое в прод. Автоматизируй линтинг SQL, если надо.
Вывод:
Сохрани, чтобы не словить боль в проде.
А у тебя где последний раз встречалось
📲Мы в MAX
#db
👉 @database_info
На dev-сервере всё шустро. В проде — беда: запросы висят, база потеет. И вроде бы всё ок... пока не заглянешь в SQL:
SELECT * FROM users WHERE status = 'active';
На первый взгляд — удобно. Но:
🔻 Проблемы “SELECT *”:
– Тянет все колонки, даже ненужные. А их может быть 30+.
– Увеличивает нагрузку на сеть и память приложения.
– Ломает кэш — ведь даже малейшие изменения в колонках меняют структуру результата.
– Убивает индекс-only scan: Postgres не может использовать покрывающий индекс, если явно не указаны поля.
✅ Как надо:
🎯 Выбирай только нужные поля:
SELECT id, name, email FROM users WHERE status = 'active';
💡 Хочешь “быстро протестить” в dev-е? Ок. Но не пускай такое в прод. Автоматизируй линтинг SQL, если надо.
Вывод:
SELECT * — это не “удобно”, это дорого. И ты за него уже платишь.Сохрани, чтобы не словить боль в проде.
А у тебя где последний раз встречалось
SELECT *?📲Мы в MAX
#db
👉 @database_info
👍8😁1
Антипаттерн: NULL в WHERE — и ты в ловушке
Когда в таблице есть
Ты ожидаешь, что выберутся все, кто не 30.
Но если
Почему? Потому что
А SQL возвращает строки только там, где
Как избежать?
1. Будь явно осторожен с NULL:
2. Логика на уровне схемы:
– Если поле нужно всегда — делай
– Если допускаешь
3. Не верь глазам своим:
Даже
Вывод:
Это "мы не знаем". И SQL ведёт себя с ним очень осторожно.
Сохрани, чтобы не словить грабли.
📲Мы в MAX
#db
👉 @database_info
Когда в таблице есть
NULL, а в WHERE ты пишешь что-то вроде:
SELECT * FROM users WHERE age != 30;
Ты ожидаешь, что выберутся все, кто не 30.
Но если
age IS NULL — такие строки пропадут из выборки!Почему? Потому что
NULL != 30 не TRUE, это UNKNOWN. А SQL возвращает строки только там, где
WHERE → TRUE.Как избежать?
1. Будь явно осторожен с NULL:
SELECT * FROM users
WHERE age != 30 OR age IS NULL;
2. Логика на уровне схемы:
– Если поле нужно всегда — делай
NOT NULL. – Если допускаешь
NULL, продумывай поведение выборок.3. Не верь глазам своим:
Даже
count(*) и count(column) ведут себя по-разному из-за NULL.Вывод:
NULL — это не ноль, не пустая строка и не "ничего". Это "мы не знаем". И SQL ведёт себя с ним очень осторожно.
Сохрани, чтобы не словить грабли.
📲Мы в MAX
#db
👉 @database_info
👍6❤1👎1