1 - Индексация:
Проверьте шаблоны запросов вашего приложения и создайте подходящие индексы.
2 - Материализованные представления:
Предварительно вычислите результаты сложных запросов и сохраните их для быстрого доступа.
3 - Денормализация:
Уменьшите количество сложных соединений (join), чтобы улучшить производительность запросов.
4 - Вертикальное масштабирование:
Увеличьте мощность вашего сервера базы данных, добавив больше ЦП, оперативной памяти или хранилища.
5 - Кэширование:
Сохраните часто запрашиваемые данные в более быстром слое хранения, чтобы снизить нагрузку на базу данных.
6 - Репликация:
Создайте реплики вашей основной базы данных на разных серверах для масштабирования чтений.
7 - Шардинг:
Разделите таблицы базы данных на более мелкие части и распределите их по серверам. Используется для масштабирования как записей, так и чтений.
#db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
📊 Зачем 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
🔥3👍2
Сегодня расскажу вам про одну часто недооценённую, но крайне полезную 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
👍3❤2🔥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
👍4🔥4
Этот файл был удален пользователем
Встречаемся в Москве с теми, кто отвечает за хранение данных бизнеса. Обсудим, как решить задачу со звездочкой — уберечь массивы файлов и резервных копий от атак шифровальщиков, утечки или случайной перезаписи.
На бизнес-ужине эксперты провайдера ИТ-инфраструктуры Selectel расскажут:
➕ как устроено хранилище S3 под капотом;
➕ какие угрозы данным существуют и как выстроить многослойную систему защиты;
➕ почему защита данных — это не статья расходов, а управление рисками.
Будет актуально руководителям в ИТ-компаниях, старшим архитекторам, системным администраторам. Поговорим и про стратегию хранения, и про реализацию.
📆 24 сентября (чт), 18:30
📍 Москва, м. Динамо
⏩ Участие бесплатное, дождитесь подтверждения заявки. Смотрите полную программу и регистрируйтесь: https://slc.tl/pophe
Реклама. АО "Селектел". erid:2W5zFG59449
Встречаемся в Москве с теми, кто отвечает за хранение данных бизнеса. Обсудим, как решить задачу со звездочкой — уберечь массивы файлов и резервных копий от атак шифровальщиков, утечки или случайной перезаписи.
На бизнес-ужине эксперты провайдера ИТ-инфраструктуры Selectel расскажут:
Будет актуально руководителям в ИТ-компаниях, старшим архитекторам, системным администраторам. Поговорим и про стратегию хранения, и про реализацию.
Реклама. АО "Селектел". erid:2W5zFG59449
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🎯 Сегодня покажу простой способ ускорить запросы в 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
👍4
🧩 Как сделать 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
🔥6👍3
🚀 Сегодня покажу, как быстро диагностировать «тормоза» в 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
👍4🔥2
Как быстро найти “тяжёлые” запросы в 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
👍2🔥2
⚡️ Совет по работе с базами данных 💡
Уникальные индексы с исключением определенных строк
Создание уникальных индексов в некоторых случаях невозможно из-за дублирования значений - например, в строках, помеченных как «мягко удаленные» (soft-deleted). Исключив такие строки из индекса, можно корректно настроить ограничение уникальности. В MySQL частичные уникальные индексы (unique partial indexes) требуют эмуляции.
В современных базах данных часто используется паттерн Soft Delete, когда данные не удаляются физически, а помечаются флагом
Если вы хотите, чтобы поле
Нюанс для MySQL:
В отличие от PostgreSQL или SQL Server, MySQL не поддерживает синтаксис
• Виртуальные колонки (Generated Columns): создается колонка, которая принимает значение только если запись активна, и на нее вешается уникальный индекс.
• Составные индексы: включение флага удаления или временной метки в сам индекс.
📲 Мы в MAX
#db
👉 @database_info
Уникальные индексы с исключением определенных строк
Создание уникальных индексов в некоторых случаях невозможно из-за дублирования значений - например, в строках, помеченных как «мягко удаленные» (soft-deleted). Исключив такие строки из индекса, можно корректно настроить ограничение уникальности. В MySQL частичные уникальные индексы (unique partial indexes) требуют эмуляции.
В современных базах данных часто используется паттерн Soft Delete, когда данные не удаляются физически, а помечаются флагом
is_deleted = true.Если вы хотите, чтобы поле
email было уникальным только для активных пользователей, обычный уникальный индекс выдаст ошибку при попытке регистрации нового пользователя с почтой, которая уже есть в «корзине». Использование частичного индекса решает эту проблему, позволяя игнорировать помеченные на удаление записи.Нюанс для MySQL:
В отличие от PostgreSQL или SQL Server, MySQL не поддерживает синтаксис
WHERE внутри команды CREATE INDEX. Чтобы добиться такого же поведения, разработчики обычно используют:• Виртуальные колонки (Generated Columns): создается колонка, которая принимает значение только если запись активна, и на нее вешается уникальный индекс.
• Составные индексы: включение флага удаления или временной метки в сам индекс.
#db
👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🚀 Сегодня я покажу вам один из моих любимых хаков для PostgreSQL – генерация серий дат без циклов и хранимок. Это идеальный способ быстро собрать таймлайн для аналитики или отчётов.
Сценарий: вам нужно построить список всех дат за последний месяц — например, чтобы потом сделать LEFT JOIN к таблице с событиями и увидеть, где были пропуски.
Вот как это делается с помощью
💡 Результат — 31 строка с датами от 30 дней назад до сегодняшнего дня.
Теперь добавим, например, LEFT JOIN к таблице
📊 Отлично подходит для дашбордов, когда нужно увидеть, где были дни без событий.
Пользуетесь ли вы
📲 Мы в MAX
#db
👉 @database_info
Сценарий: вам нужно построить список всех дат за последний месяц — например, чтобы потом сделать LEFT JOIN к таблице с событиями и увидеть, где были пропуски.
Вот как это делается с помощью
generate_series:
SELECT generate_series(
date_trunc('day', current_date) - interval '30 days',
date_trunc('day', current_date),
interval '1 day'
) AS day;
💡 Результат — 31 строка с датами от 30 дней назад до сегодняшнего дня.
Теперь добавим, например, LEFT JOIN к таблице
events, чтобы увидеть активность по дням:
SELECT
d.day,
COUNT(e.id) AS events_count
FROM
generate_series(
date_trunc('day', current_date) - interval '30 days',
date_trunc('day', current_date),
interval '1 day'
) AS d(day)
LEFT JOIN events e ON date_trunc('day', e.created_at) = d.day
GROUP BY d.day
ORDER BY d.day;
📊 Отлично подходит для дашбордов, когда нужно увидеть, где были дни без событий.
Пользуетесь ли вы
generate_series? А может быть, используете что-то подобное в других СУБД? Делитесь в комментариях👇📲 Мы в MAX
#db
👉 @database_info
🔥3👍1
Сегодня я хочу рассказать вам про одну часто недооцененную фишку в PostgreSQL - partial indexes (частичные индексы).
Обычно мы создаём индексы на всю таблицу, но что если нам нужно ускорить только небольшую часть данных? Например, часто выбираются только активные пользователи (
Что это даёт:
- Индекс меньше по размеру → быстрее поиск и обновление.
- Используется только тогда, когда запрос соответствует условию
- Меньше нагрузка на диск при обновлениях таблицы.
🛠 Где это реально помогает:
- Таблицы с миллионами записей, где активно работают только с частью строк.
- Сценарии "горячих" и "холодных" данных.
Рекомендую попробовать partial indexes там, где обычные индексы слишком тяжелы или тормозят обновления!
📲 Мы в MAX
#db
👉 @database_info
Обычно мы создаём индексы на всю таблицу, но что если нам нужно ускорить только небольшую часть данных? Например, часто выбираются только активные пользователи (
status = 'active'). Вместо полного индекса можно создать индекс только для нужного поднабора данных:
CREATE INDEX idx_active_users
ON users (last_login)
WHERE status = 'active';
Что это даёт:
- Индекс меньше по размеру → быстрее поиск и обновление.
- Используется только тогда, когда запрос соответствует условию
status = 'active'.- Меньше нагрузка на диск при обновлениях таблицы.
🛠 Где это реально помогает:
- Таблицы с миллионами записей, где активно работают только с частью строк.
- Сценарии "горячих" и "холодных" данных.
Рекомендую попробовать partial indexes там, где обычные индексы слишком тяжелы или тормозят обновления!
📲 Мы в MAX
#db
👉 @database_info
👍3🔥3
❤1🔥1