This media is not supported in your browser
VIEW IN TELEGRAM
Давайте разберём логическую репликацию Postgres с помощью
Небольшое предупреждение: это демонстрационный пример. В продакшене лучше использовать более надёжный CDC-инструмент, который умеет обрабатывать повторы, обеспечивать устойчивость, эволюцию схем и т.д. Но
Теперь к коду:
Чтобы запустить Docker Postgres, готовый к логической репликации, сделайте следующее:
Запустите Postgres в Docker-контейнере с
Подключитесь к базе (пароль —
В другом терминале запустите
В терминале, где вы подключены к
Вы увидите изменения в реальном времени:
Это, по сути, то, как работают все CDC-инструменты «под капотом»: они подключаются к логическому репликационному слоту и декодируют изменения WAL в поток событий. Конечно, для продакшен-пайплайнов нужно больше — обработка ошибок, повторы, устойчивость, эволюция схем и т.д., но это и есть основной механизм.
👉 @SQLPortal
pg_recvlogical. Это CLI-инструмент, который поставляется вместе с Postgres и позволяет стримить изменения логической репликации в stdout. Это простой способ понять, какие данные доступны через логическую репликацию, чтобы вы могли строить свои собственные пайплайны «прямо из Postgres».Небольшое предупреждение: это демонстрационный пример. В продакшене лучше использовать более надёжный CDC-инструмент, который умеет обрабатывать повторы, обеспечивать устойчивость, эволюцию схем и т.д. Но
pg_recvlogical отлично подходит, чтобы увидеть сам механизм в действии. Также мы собираемся выполнить команды, которые изменяют базу данных. Убедитесь, что у вас тестовая база, а не продакшен, или используйте Docker, чтобы поднять временный экземпляр Postgres.Теперь к коду:
Чтобы запустить Docker Postgres, готовый к логической репликации, сделайте следующее:
Запустите Postgres в Docker-контейнере с
wal_level=logical:docker run --name pg-test -e POSTGRES_PASSWORD=secret -d -p 5432:5432 postgres:latest -c wal_level=logical
Подключитесь к базе (пароль —
secret) и создайте базу, таблицу и репликационные слоты:docker exec -it pg-test createdb -U postgres mydb
docker exec -it pg-test psql -U postgres mydb
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
product TEXT,
quantity INTEGER
);
SELECT pg_create_logical_replication_slot('my_slot', 'test_decoding');
В другом терминале запустите
pg_recvlogical, чтобы стримить изменения:docker exec -it pg-test pg_recvlogical -U postgres -d mydb --slot=my_slot --start -f -
В терминале, где вы подключены к
mydb, вставляйте и обновляйте данные. Выполняйте каждую команду по очереди и наблюдайте поток изменений от pg_recvlogical:INSERT INTO orders (id, product, quantity) VALUES (1, 'Widget', 3);
UPDATE orders SET quantity = 4 WHERE id = 1;
DELETE FROM orders WHERE id = 1;
Вы увидите изменения в реальном времени:
BEGIN 742
table public.orders: INSERT: id[integer]:1 product[text]:'Widget' quantity[integer]:3
table public.orders: UPDATE: id[integer]:1 product[text]:'Widget' quantity[integer]:4
table public.orders: DELETE: id[integer]:1
COMMIT 742
Это, по сути, то, как работают все CDC-инструменты «под капотом»: они подключаются к логическому репликационному слоту и декодируют изменения WAL в поток событий. Конечно, для продакшен-пайплайнов нужно больше — обработка ошибок, повторы, устойчивость, эволюция схем и т.д., но это и есть основной механизм.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
This media is not supported in your browser
VIEW IN TELEGRAM
MICROSOFT отказала одному парню — и он на вайб-кодинге собрал свой Excel
Подался в Microsoft, получил отказ и решил с нуля написать терминальный просмотрщик таблиц.
Что он умеет:
- открывает и рендерит .xlsx и .csv прямо в терминале
- навигация полностью с клавиатуры — мышка не нужна
- работает с большими датасетами без лагов, которыми страдает современный Excel
- форматирование ячеек, формулы и сортировка работают прямо в CLI
- достаточно лёгкий, чтобы мгновенно запускаться почти на любой машине
Без ribbon-интерфейса, без раздутого UI, без веб-версии, которая грузится по 10 секунд.
Просто быстрый инструмент для таблиц, который выглядит как софт из 80-х, но работает лучше большинства современных альтернатив.
Первый пользователь — аналитик данных — сказал, что уже много лет считает переход на ribbon-интерфейс ударом по хардкорным пользователям.
Другой пользователь написал, что проект напоминает Lotus 1-2-3 времён DOS, только намного современнее.
Современный Excel — это 90% функций, которыми никто не пользуется, и 10% того, что действительно нужно опытным пользователям.
Этот парень выкинул всё лишнее, оставил только эти 10% и сделал всё быстрым.
https://github.com/bgreenwell/xleak
👉 @SQLPortal
Подался в Microsoft, получил отказ и решил с нуля написать терминальный просмотрщик таблиц.
Что он умеет:
- открывает и рендерит .xlsx и .csv прямо в терминале
- навигация полностью с клавиатуры — мышка не нужна
- работает с большими датасетами без лагов, которыми страдает современный Excel
- форматирование ячеек, формулы и сортировка работают прямо в CLI
- достаточно лёгкий, чтобы мгновенно запускаться почти на любой машине
Без ribbon-интерфейса, без раздутого UI, без веб-версии, которая грузится по 10 секунд.
Просто быстрый инструмент для таблиц, который выглядит как софт из 80-х, но работает лучше большинства современных альтернатив.
Первый пользователь — аналитик данных — сказал, что уже много лет считает переход на ribbon-интерфейс ударом по хардкорным пользователям.
Другой пользователь написал, что проект напоминает Lotus 1-2-3 времён DOS, только намного современнее.
Современный Excel — это 90% функций, которыми никто не пользуется, и 10% того, что действительно нужно опытным пользователям.
Этот парень выкинул всё лишнее, оставил только эти 10% и сделал всё быстрым.
https://github.com/bgreenwell/xleak
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6😁4
Подготовленные выражения и подготовленные транзакции — это полностью разные возможности Postgres, которые совпадают только по слову в названии. Одна используется широко и безопасна. Вторая применяется редко и при неправильном управлении может приводить к простоям.
Начнём с безопасной:
Подготовленные выражения (PREPARE / EXECUTE)
Часто используемая функция оптимизации запросов. SQL-выражение парсится один раз, затем выполняется много раз с разными параметрами. Снижает накладные расходы на парсинг и иногда на построение плана выполнения:
• Живёт в пределах одной сессии
• Хранится в памяти, без долговременного хранения
• Безопасно при «забывании»
• Видно через
• Используется большинством фреймворков, ORM и библиотек для оптимизации
Подготовленные транзакции (PREPARE TRANSACTION / COMMIT PREPARED)
Большинству приложений эта функциональность не нужна.
Это реализация Postgres двухфазного коммита (2PC / XA транзакции). Транзакция переводится в состояние «готова к фиксации», фиксируется на диске, а завершение выполняется позже из другой сессии:
• Переживает падения и перезапуски (запись в
• Держит блокировки до завершения
• Удерживает идентификатор транзакции (XID) и блокирует автоочистку (vacuum)
• Блокирует DDL-операции над затронутыми таблицами
• Требует
👉 @SQLPortal
Начнём с безопасной:
Подготовленные выражения (PREPARE / EXECUTE)
Часто используемая функция оптимизации запросов. SQL-выражение парсится один раз, затем выполняется много раз с разными параметрами. Снижает накладные расходы на парсинг и иногда на построение плана выполнения:
PREPARE find_user (text) AS
SELECT * FROM users WHERE email = $1;
EXECUTE find_user('alice@example.com');
EXECUTE find_user('bob@example.com');
DEALLOCATE find_user;
• Живёт в пределах одной сессии
• Хранится в памяти, без долговременного хранения
• Безопасно при «забывании»
• Видно через
SELECT * FROM pg_prepared_statements• Используется большинством фреймворков, ORM и библиотек для оптимизации
Подготовленные транзакции (PREPARE TRANSACTION / COMMIT PREPARED)
Большинству приложений эта функциональность не нужна.
Это реализация Postgres двухфазного коммита (2PC / XA транзакции). Транзакция переводится в состояние «готова к фиксации», фиксируется на диске, а завершение выполняется позже из другой сессии:
BEGIN;
INSERT INTO orders VALUES (1, 'widget', 99);
PREPARE TRANSACTION 'order_txn_abc123';
-- позже
COMMIT PREPARED 'order_txn_abc123';
-- или: ROLLBACK PREPARED 'order_txn_abc123';
• Переживает падения и перезапуски (запись в
pg_twophase/ внутри каталога)• Держит блокировки до завершения
• Удерживает идентификатор транзакции (XID) и блокирует автоочистку (vacuum)
• Блокирует DDL-операции над затронутыми таблицами
• Требует
max_prepared_transactions > 0 (по умолчанию 0 — одно из удачных значений по умолчанию)Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
SQL-Translator – визуализируй SQL-запросы мгновенно!
Устал разбираться в сложных SQL-запросах? SQL-Translator преобразует их в наглядные схемы, помогая быстро понять структуру данных
🟡 Парсит SQL-запросы и визуализирует их
🟡 Поддерживает PostgreSQL, MySQL, SQLite и другие БД
🟡 Работает прямо в браузере – ничего устанавливать не нужно!
Попробуй сам: здесь
👉 @SQLPortal
Устал разбираться в сложных SQL-запросах? SQL-Translator преобразует их в наглядные схемы, помогая быстро понять структуру данных
Попробуй сам: здесь
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Ожидается, что в Postgres 19 появится новое contrib-расширение
Начиная с 1998 года, Postgres использует статистику на уровне колонок для генерации планов запросов. Алгоритм много раз улучшался и давно считается хорошо проверенной системой выбора плана выполнения на основе статистики. Статистика, которую генерирует
Когда вы запускаете
Ниже примеры из таблицы с HTTP-запросами. Такой набор данных удобно использовать, потому что там есть разные типы значений, которые легко представить.
Список наиболее частых значений (MCV)
Если самая сложная задача в компьютерных науках — кеширование и нейминг, то список most common values заслуживает место в зале славы за удачное название.
Это самые частые значения колонки и их точные частоты:
Здесь это HTTP-методы, поэтому набор значений ограничен спецификацией.
Для
70% от 200 000 строк = ожидаемые 140 000 строк.
Гистограмма с бакетами равной высоты
Для значений, которых нет в MCV-списке, Postgres строит equi-height buckets. Ниже пример гистограммы для колонки со временем ответа в миллисекундах:
Каждый бакет содержит примерно одинаковое количество строк. Бакет
За счёт фиксированного количества строк диапазон значений становится переменным. Это упрощает оценку количества строк.
NULL-гистограммы
Когда все уникальные значения помещаются в MCV-список,
У колонки
Зачем нужны две структуры?
MCV даёт точные частоты для популярных значений, а гистограмма покрывает длинный хвост — редкие или непрерывные значения через приближение. Вместе они описывают полное распределение без хранения каждого значения отдельно.
Чтобы посмотреть статистику для таблицы и колонки:
👉 @SQLPortal
pg_plan_advice, которое добавит пользовательские хинты. Но как Postgres столько лет обходился без хинтов, пока другие СУБД на них опирались? Статистика.Начиная с 1998 года, Postgres использует статистику на уровне колонок для генерации планов запросов. Алгоритм много раз улучшался и давно считается хорошо проверенной системой выбора плана выполнения на основе статистики. Статистика, которую генерирует
ANALYZE, — основа планировщика запросов.Когда вы запускаете
ANALYZE, Postgres строит две структуры для каждой колонки. Именно на них в первую очередь смотрит планировщик, когда оценивает, сколько строк вернёт WHERE.Ниже примеры из таблицы с HTTP-запросами. Такой набор данных удобно использовать, потому что там есть разные типы значений, которые легко представить.
Список наиболее частых значений (MCV)
Если самая сложная задача в компьютерных науках — кеширование и нейминг, то список most common values заслуживает место в зале славы за удачное название.
Это самые частые значения колонки и их точные частоты:
most_common_vals = {GET, POST, PUT, DELETE, PATCH}
most_common_freqs = {0.70, 0.20, 0.05, 0.03, 0.02}Здесь это HTTP-методы, поэтому набор значений ограничен спецификацией.
Для
WHERE method = 'GET' планировщик напрямую читает 0.70 из списка.70% от 200 000 строк = ожидаемые 140 000 строк.
Гистограмма с бакетами равной высоты
Для значений, которых нет в MCV-списке, Postgres строит equi-height buckets. Ниже пример гистограммы для колонки со временем ответа в миллисекундах:
histogram_bounds = {83, 88, 95, 107, 132, 166, 195, 245, 362, 554, 965, 2622164}Каждый бакет содержит примерно одинаковое количество строк. Бакет
[245, 362] содержит примерно столько же строк, сколько [83, 88].За счёт фиксированного количества строк диапазон значений становится переменным. Это упрощает оценку количества строк.
NULL-гистограммы
Когда все уникальные значения помещаются в MCV-список,
histogram_bounds становится NULL.У колонки
method всего 5 значений. Все они попадают в MCV-список, поэтому гистограмма не нужна.Зачем нужны две структуры?
MCV даёт точные частоты для популярных значений, а гистограмма покрывает длинный хвост — редкие или непрерывные значения через приближение. Вместе они описывают полное распределение без хранения каждого значения отдельно.
Чтобы посмотреть статистику для таблицы и колонки:
SELECT
most_common_vals,
most_common_freqs,
histogram_bounds
FROM pg_stats
WHERE tablename = 'requests'
AND attname = 'status_code';
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
This media is not supported in your browser
VIEW IN TELEGRAM
Кто-то только что выложил в open-source проект, который тихо ломает стандартную модель ценообразования в ИИ.
800 млн токенов инференса в месяц бесплатно.
14 провайдеров. Один унифицированный эндпоинт. Локальный запуск.
Без подписок. Без платных стен. Без “обновите тариф, чтобы продолжить”. Без привязки к одному вендору и экосистеме.
Это не просто более дешёвый доступ к ИИ — это штука, которая заставляет пересмотреть сам факт того, почему базовый инференс вообще был платным.
Это выглядит не как релиз продукта, а как сдвиг в том, как должен работать доступ к ИИ.
Репозиторий👇
https://github.com/tashfeenahmed/freellmapi
👉 @SQLPortal
800 млн токенов инференса в месяц бесплатно.
14 провайдеров. Один унифицированный эндпоинт. Локальный запуск.
Без подписок. Без платных стен. Без “обновите тариф, чтобы продолжить”. Без привязки к одному вендору и экосистеме.
Это не просто более дешёвый доступ к ИИ — это штука, которая заставляет пересмотреть сам факт того, почему базовый инференс вообще был платным.
Это выглядит не как релиз продукта, а как сдвиг в том, как должен работать доступ к ИИ.
Репозиторий👇
https://github.com/tashfeenahmed/freellmapi
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
SQL-запросы могут сортировать по невыбранным колонкам, например:
Но так было не всегда!
Markus Winand проводит обзор истории
- позиционные ссылки (positional references)
- невыбранные колонки (non-selected columns)
- выражения (expressions)
- порядок сортировки NULL-значений (null ordering)
- подзапросы (subqueries)
👉 @SQLPortal
SELECT a FROM t ORDER BY b
Но так было не всегда!
Markus Winand проводит обзор истории
ORDER BY, рассматривая:- позиционные ссылки (positional references)
- невыбранные колонки (non-selected columns)
- выражения (expressions)
- порядок сортировки NULL-значений (null ordering)
- подзапросы (subqueries)
Please open Telegram to view this post
VIEW IN TELEGRAM
Modern-Sql
SQL’s ORDER BY Has Come a Long Way
ORDER BY Evolution
This media is not supported in your browser
VIEW IN TELEGRAM
Парень создал систему поиска работы при помощи Claude Code
Которая в итоге отправила более 700 заявок и помогла найти работу.🥳
Он выложил её в опенсорс.
Она автоматизирует весь процесс: сканирует страницы вакансий разных компаний, адаптирует твоё резюме под каждую конкретную позицию и даже автоматически заполняет формы за тебя. В репозитории есть:
- 14 различных режимов (оценка, парсинг, PDF и т.д.)
- терминальный dashboard на Go
- генерация PDF-резюме, оптимизированного под ATS, через Playwright
- уже более 45 настроенных компаний (Anthropic, OpenAI, ElevenLabs, Stripe…)
👉 @SQLPortal
Которая в итоге отправила более 700 заявок и помогла найти работу.
Он выложил её в опенсорс.
Она автоматизирует весь процесс: сканирует страницы вакансий разных компаний, адаптирует твоё резюме под каждую конкретную позицию и даже автоматически заполняет формы за тебя. В репозитории есть:
- 14 различных режимов (оценка, парсинг, PDF и т.д.)
- терминальный dashboard на Go
- генерация PDF-резюме, оптимизированного под ATS, через Playwright
- уже более 45 настроенных компаний (Anthropic, OpenAI, ElevenLabs, Stripe…)
Please open Telegram to view this post
VIEW IN TELEGRAM
Real-Time Dashboard для PostgreSQL прямо в терминале: https://github.com/mujib77/cosmo
Просматривай внутреннее состояние базы данных напрямую: запросы, записи, блокировки и подключения — всё в одном интерфейсе.
TUI для терминала, написанный на Go, который подключается к Postgres и показывает, что происходит внутри базы данных. Он читает системные представления вроде
Интерфейс разделён на четыре панели:
- Overview
- Queries
- WAL/MVCC
- Locks
👉 @SQLPortal
Просматривай внутреннее состояние базы данных напрямую: запросы, записи, блокировки и подключения — всё в одном интерфейсе.
TUI для терминала, написанный на Go, который подключается к Postgres и показывает, что происходит внутри базы данных. Он читает системные представления вроде
pg_stat_activity и pg_stat_database, обновляя данные каждые две секунды, и в реальном времени отображает метрики: активные запросы, cache hit rate, скорость записи WAL и ожидания блокировок.Интерфейс разделён на четыре панели:
- Overview
- Queries
- WAL/MVCC
- Locks
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Если тебе нужно просто проверить, существует ли конкретный элемент в данных, не используй COUNT() с фильтром (WHERE).
SELECT COUNT(*)
FROM users
WHERE email = 'http://thisemaildontexist.com'
Проблема в том, что COUNT(*) отвечает на другой вопрос. Он считает количество строк, которые удовлетворяют условию, хотя тебе нужно только знать, есть ли хотя бы одна.
Когда ты используешь COUNT(*), база данных может просканировать все подходящие строки, чтобы вычислить итог. Даже если она найдёт одно совпадение сразу, она может продолжить работу, потому что ты запросил полный подсчёт. Это лишняя работа при больших таблицах.
Вместо COUNT() используй EXISTS
SELECT EXISTS (
SELECT 1
FROM users
WHERE email = 'http://thisemaildontexist.com'
);
С EXISTS база данных остановится, как только найдёт первое совпадение. Без подсчёта. Без лишнего сканирования.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Чистые данные — это основа надёжной аналитики. SQL упрощает работу с NULL-значениями, дубликатами, выбросами, проблемами форматирования, опечатками и несогласованными значениями перед анализом или построением отчётности. Хорошие навыки очистки данных повышают точность и качество принимаемых решений. #SQL #DataAnalytics #DataCleaning #DataScience
👉 @SQLPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3❤2
Почему запрос
не возвращает Charlie, если у него
Потому что в SQL
Когда SQL проверяет условие:
для строки, где:
результат будет не
То есть SQL не может определить, равно ли неизвестное значение
Получается:
А в секцию
Поэтому Charlie не попадает в результат.
Если нужно включить строки с
или в некоторых СУБД (например, PostgreSQL):
Этот оператор рассматривает
👉 @SQLPortal
SELECT * FROM users WHERE status <> 'banned';
не возвращает Charlie, если у него
status = NULL?Потому что в SQL
NULL означает «неизвестное значение», а не пустую строку и не какое-то конкретное значение.Когда SQL проверяет условие:
status <> 'banned'
для строки, где:
status = NULL
результат будет не
TRUE и не FALSE, а UNKNOWN.То есть SQL не может определить, равно ли неизвестное значение
'banned' или нет.Получается:
NULL = 'banned' → UNKNOWN
NULL <> 'banned' → UNKNOWN
А в секцию
WHERE попадают только строки, для которых условие вернуло TRUE.TRUE → строка возвращается
FALSE → строка отбрасывается
UNKNOWN → строка тоже отбрасывается
Поэтому Charlie не попадает в результат.
Если нужно включить строки с
NULL, условие следует написать так:SELECT *
FROM users
WHERE status <> 'banned'
OR status IS NULL;
или в некоторых СУБД (например, PostgreSQL):
SELECT *
FROM users
WHERE status IS DISTINCT FROM 'banned';
Этот оператор рассматривает
NULL как отдельное значение и вернёт все строки, где статус не 'banned', включая NULL.Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Для выполнения ad-hoc-поиска по данным в форматах JSON и XML можно создавать поисковые индексы.
В Oracle AI Database 26ai синтаксис был упрощён до:
Это позволяет выполнять полнотекстовый поиск по значениям XML- и JSON-документов с использованием индекса, что значительно ускоряет запросы по неструктурированным данным.
👉 @SQLPortal
В Oracle AI Database 26ai синтаксис был упрощён до:
CREATE SEARCH INDEX ... ON ... ( { xmlcol | jsoncol } )Это позволяет выполнять полнотекстовый поиск по значениям XML- и JSON-документов с использованием индекса, что значительно ускоряет запросы по неструктурированным данным.
Please open Telegram to view this post
VIEW IN TELEGRAM
Кодируйте строки на основе их произношения в Oracle AI Database 26ai с помощью:
Это позволяет сопоставлять строки с разным написанием, которые звучат одинаково.
Например:
👉 @SQLPortal
PHONIC_ENCODE ( DOUBLE_METAPHONE[_ALT], <str> )
Это позволяет сопоставлять строки с разным написанием, которые звучат одинаково.
Например:
"Knight" и "Night".Please open Telegram to view this post
VIEW IN TELEGRAM
«Потому что на этом этапе мы не обязательно знаем это наверняка» — комментарий из коммита 2004 года, который до сих пор присутствует в PostgreSQL.
Скриншот вверху взят из файла
В статье рассматривался вопрос:
Ответ оказался примерно таким: около 300 выборок на каждый бакет (bin) гистограммы равной высоты (equi-height histogram).
Почему именно столько?
Статья показывает, что необходимый объём выборки растёт линейно с количеством бакетов, но лишь логарифмически с размером таблицы в большинстве практических случаев. Поэтому после нескольких сотен выборок на бакет отдача от увеличения выборки становится всё меньше.
Например, значение
Это означает, что PostgreSQL стремится собрать выборку размером:
чтобы:
- построить гистограмму равной высоты из 100 бакетов;
- сохранить 100 наиболее часто встречающихся значений (Most Common Values, MCV).
Зачем столько усилий ради неиндексированных данных?
Потому что в 1998 году индексы были значительно дороже, чем сегодня:
- занимали ценное дисковое пространство;
- потребляли ограниченные IOPS при записи и построении;
- были дорогими в сопровождении;
- полные сканирования таблиц выполнялись медленно и блокировали работу.
В то время производительность дисков измерялась в RPM (оборотах в минуту). Говорить об IOPS было сложнее, поскольку случайный доступ к данным требовал ожидания поворота диска до нужного сектора, а физическое расположение данных заранее было неизвестно.
Тесты из статьи выполнялись на системе со следующими характеристиками:
- процессор Pentium 200 МГц;
- 64 МБ оперативной памяти;
- SCSI-диск 7200 RPM.
Пользователи PostgreSQL продолжают получать выгоду от этой работы даже сегодня.
Да, индексы по-прежнему не бесплатны, и их может быть слишком много, но их стоимость уже далеко не такая, как в конце 90-х. Аналогично и работа с неиндексированными данными стала намного менее затратной.
Компромисс между точностью и производительностью
Авторы статьи также отмечают, что задача является:
Поэтому:
Число 300 представляет собой компромисс между точностью и скоростью работы:
- меньшее значение дало бы менее точную статистику и могло привести к ошибочным решениям планировщика запросов;
- большее значение улучшило бы точность, но замедлило бы работу
А в те времена
Что контролирует
Параметр
- Most Common Values (MCV);
- Equi-height Histogram.
Например:
По умолчанию этот параметр задаётся на уровне базы данных, но его можно переопределить для отдельного столбца:
Для крупных баз данных обычно находится хотя бы один столбец, для которого имеет смысл увеличить значение статистики локально. Не стоит повышать глобальное значение по умолчанию только из-за одного столбца, которому требуется более детальная статистика.
Сегодня влияние настроек статистики меньше, чем раньше, но для отдельных столбцов они всё ещё могут улучшить планы запросов.
👉 @SQLPortal
Скриншот вверху взят из файла
analyze.c в исходном коде PostgreSQL. Число 300 — это жёстко заданное значение в коде ANALYZE. Его происхождение связано с научной работой "Random Sampling for Histogram Construction: How Much Is Enough?", опубликованной в 1998 году, когда объёмы данных были значительно меньше, а оборудование — намного медленнее.В статье рассматривался вопрос:
сколько строк нужно выбрать в выборку, чтобы построить статистику, достаточно точную для оптимизации запросов к неиндексированным данным?
Ответ оказался примерно таким: около 300 выборок на каждый бакет (bin) гистограммы равной высоты (equi-height histogram).
Почему именно столько?
Статья показывает, что необходимый объём выборки растёт линейно с количеством бакетов, но лишь логарифмически с размером таблицы в большинстве практических случаев. Поэтому после нескольких сотен выборок на бакет отдача от увеличения выборки становится всё меньше.
Например, значение
statistics_target по умолчанию равно 100.Это означает, что PostgreSQL стремится собрать выборку размером:
300 × 100 = 30 000 значений
чтобы:
- построить гистограмму равной высоты из 100 бакетов;
- сохранить 100 наиболее часто встречающихся значений (Most Common Values, MCV).
Зачем столько усилий ради неиндексированных данных?
Потому что в 1998 году индексы были значительно дороже, чем сегодня:
- занимали ценное дисковое пространство;
- потребляли ограниченные IOPS при записи и построении;
- были дорогими в сопровождении;
- полные сканирования таблиц выполнялись медленно и блокировали работу.
В то время производительность дисков измерялась в RPM (оборотах в минуту). Говорить об IOPS было сложнее, поскольку случайный доступ к данным требовал ожидания поворота диска до нужного сектора, а физическое расположение данных заранее было неизвестно.
Тесты из статьи выполнялись на системе со следующими характеристиками:
- процессор Pentium 200 МГц;
- 64 МБ оперативной памяти;
- SCSI-диск 7200 RPM.
Пользователи PostgreSQL продолжают получать выгоду от этой работы даже сегодня.
Да, индексы по-прежнему не бесплатны, и их может быть слишком много, но их стоимость уже далеко не такая, как в конце 90-х. Аналогично и работа с неиндексированными данными стала намного менее затратной.
Компромисс между точностью и производительностью
Авторы статьи также отмечают, что задача является:
«доказуемо сложной, поскольку существует предел достижимой точности оценки в худшем случае».
Поэтому:
«мы разработали простой метод оценки, который, по нашему мнению, является оптимальным».
Число 300 представляет собой компромисс между точностью и скоростью работы:
- меньшее значение дало бы менее точную статистику и могло привести к ошибочным решениям планировщика запросов;
- большее значение улучшило бы точность, но замедлило бы работу
ANALYZE.А в те времена
ANALYZE и без того работал значительно медленнее.Что контролирует
statistics_target?Параметр
statistics_target определяет количество значений, сохраняемых для:- Most Common Values (MCV);
- Equi-height Histogram.
Например:
statistics_target = 100 → 30 000 выборок, 100 MCV, 100 бакетов
statistics_target = 500 → 150 000 выборок, 500 MCV, 500 бакетов
statistics_target = 1000 → 300 000 выборок, 1000 MCV, 1000 бакетов
По умолчанию этот параметр задаётся на уровне базы данных, но его можно переопределить для отдельного столбца:
-- Настройка для конкретного столбца
ALTER TABLE requests
ALTER COLUMN status_code
SET STATISTICS 500;
ANALYZE requests;
Для крупных баз данных обычно находится хотя бы один столбец, для которого имеет смысл увеличить значение статистики локально. Не стоит повышать глобальное значение по умолчанию только из-за одного столбца, которому требуется более детальная статистика.
Сегодня влияние настроек статистики меньше, чем раньше, но для отдельных столбцов они всё ещё могут улучшить планы запросов.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
Lost update возникает, когда один пользователь незаметно перезаписывает изменения другого пользователя.
Vlad Mihalcea подробно разбирает:
• Как возникают lost update
• Как предотвращать их с помощью pessimistic locking (stateful) и optimistic locking (stateless)
Сейчас большинство приложений stateless, поэтому чаще используют optimistic locking.
👉 @SQLPortal
Vlad Mihalcea подробно разбирает:
• Как возникают lost update
• Как предотвращать их с помощью pessimistic locking (stateful) и optimistic locking (stateless)
Сейчас большинство приложений stateless, поэтому чаще используют optimistic locking.
Please open Telegram to view this post
VIEW IN TELEGRAM
Vlad Mihalcea
Optimistic vs. Pessimistic Locking - Vlad Mihalcea
Learn the difference between optimistic and pessimistic locking and when you should employ one or the other to prevent conflicts.
Решение многих распространённых проблем с производительностью БД вообще не требует изменений в самой базе данных. База — это лишь один компонент гораздо более крупной системы. В эту систему входят поведение пользователей, UX-дизайн, изменения в коде и меняющиеся сценарии использования.
Ниже — несколько проблем, которые выглядят как проблемы производительности БД, но на деле ими не являются.
Пользователи бесконечно жмут refresh
Когда пользователю кажется, что страница не загружается, он по привычке нажимает reload. Каждый reload повторно запускает те же самые запросы. Мы часто видели это на тяжёлых отчётах. Когда нагрузка была низкой, отчёты генерировались мгновенно, но с ростом числа клиентов, объёма данных и сложности логики запросы стали выполняться дольше, плюс выросло время форматирования на уровне приложения. Если кнопка “run report” никак не показывает, что процесс уже идёт, пользователи начинают нажимать её повторно или обновлять страницу.
Признаки:
• резкий рост
• всплеск запросов, привязанный к загрузке конкретной страницы
• пользователи жалуются, что страница «сломалась» или «грузится вечность»
Решение: состояния загрузки с отключением кнопок, optimistic UI, асинхронная генерация отчётов или хотя бы обычный spinner, который показывает, что приложение работает. Ни одна база не выдержит фронтенд, позволяющий пользователям DDOS’ить её вручную.
N+1 запросы из ORM
Вы загружаете список из 100 заказов. ORM затем выполняет ещё 100 отдельных запросов, чтобы получить клиента для каждого заказа. Потом дашборд показывает общее число заказов для каждого клиента — и это ещё 100 запросов. В итоге вместо одного запроса страница делает 201.
Каждый запрос занимает 2 мс. Но 201 × 2 мс = 404 мс последовательных round-trip’ов к БД на каждый запрос страницы. При 500 запросах в минуту одна страница генерирует тысячи лишних запросов в минуту, которых вообще не должно существовать.
Решение: свести всё к одному запросу через
Агрессивный polling чаще, чем меняются данные
Разработчик добавляет
Решение: WebSocket, Server-Sent Events или хотя бы существенно увеличить интервал polling’а.
Запрос медленный только для нового сценария использования
Отчётный запрос выполняется 800 мс. Финансовый отдел запускает его один раз в день в 9 утра. Потом кто-то добавляет этот запрос в widget на дашборде с автообновлением каждые 30 секунд. Теперь каждый пользователь с открытым дашбордом запускает запрос на 800 мс по 120 раз в час.
Сам запрос медленнее не стал. Изменился паттерн доступа.
Решение: кешировать отчёт через materialized view, вынести его в background job или убрать auto-refresh.
Иногда проблема — в дизайне системы, а не в БД
Не раз бывало так, что запись экрана пользователя с воспроизведением ошибки помогала понять проблему быстрее, чем любые логи.
👉 @SQLPortal
Ниже — несколько проблем, которые выглядят как проблемы производительности БД, но на деле ими не являются.
Пользователи бесконечно жмут refresh
Когда пользователю кажется, что страница не загружается, он по привычке нажимает reload. Каждый reload повторно запускает те же самые запросы. Мы часто видели это на тяжёлых отчётах. Когда нагрузка была низкой, отчёты генерировались мгновенно, но с ростом числа клиентов, объёма данных и сложности логики запросы стали выполняться дольше, плюс выросло время форматирования на уровне приложения. Если кнопка “run report” никак не показывает, что процесс уже идёт, пользователи начинают нажимать её повторно или обновлять страницу.
Признаки:
• резкий рост
calls в pg_stat_statements без соответствующего роста трафика• всплеск запросов, привязанный к загрузке конкретной страницы
• пользователи жалуются, что страница «сломалась» или «грузится вечность»
Решение: состояния загрузки с отключением кнопок, optimistic UI, асинхронная генерация отчётов или хотя бы обычный spinner, который показывает, что приложение работает. Ни одна база не выдержит фронтенд, позволяющий пользователям DDOS’ить её вручную.
N+1 запросы из ORM
Вы загружаете список из 100 заказов. ORM затем выполняет ещё 100 отдельных запросов, чтобы получить клиента для каждого заказа. Потом дашборд показывает общее число заказов для каждого клиента — и это ещё 100 запросов. В итоге вместо одного запроса страница делает 201.
Каждый запрос занимает 2 мс. Но 201 × 2 мс = 404 мс последовательных round-trip’ов к БД на каждый запрос страницы. При 500 запросах в минуту одна страница генерирует тысячи лишних запросов в минуту, которых вообще не должно существовать.
Решение: свести всё к одному запросу через
JOIN и агрегации. Некоторые ORM умеют это автоматически, в других придётся писать SQL вручную.Агрессивный polling чаще, чем меняются данные
Разработчик добавляет
setInterval(() => fetchStatus(), 1000) для проверки статуса заказа. Код уезжает в production. 50 000 активных сессий, опрашивающих сервер раз в секунду = 50 000 запросов в секунду к таблице, которая обновляется всего раз в минуту.Решение: WebSocket, Server-Sent Events или хотя бы существенно увеличить интервал polling’а.
Запрос медленный только для нового сценария использования
Отчётный запрос выполняется 800 мс. Финансовый отдел запускает его один раз в день в 9 утра. Потом кто-то добавляет этот запрос в widget на дашборде с автообновлением каждые 30 секунд. Теперь каждый пользователь с открытым дашбордом запускает запрос на 800 мс по 120 раз в час.
Сам запрос медленнее не стал. Изменился паттерн доступа.
Решение: кешировать отчёт через materialized view, вынести его в background job или убрать auto-refresh.
Иногда проблема — в дизайне системы, а не в БД
Не раз бывало так, что запись экрана пользователя с воспроизведением ошибки помогала понять проблему быстрее, чем любые логи.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3👍3
9 HTTP-методов
1. GET
Читает данные без их изменения.
Безопасный и идемпотентный — повторный запрос возвращает тот же результат.
2. POST
Создаёт новые ресурсы.
Не является идемпотентным — повтор может привести к дублированию данных.
3.PUT
Создаёт или полностью заменяет ресурс по известному URL.
Идемпотентный, идеально подходит для полных обновлений.
4. PATCH
Используется для частичных обновлений, изменяя только нужные поля.
5. DELETE
Удаляет ресурс.
Идемпотентный — повторное удаление не приведёт к ошибке. Часто используется в API для удаления записей.
6. HEAD
Как GET, но возвращает только заголовки (без тела ответа).
Удобен для проверки наличия ресурса или метаданных.
7. OPTIONS
Показывает доступные HTTP-методы для ресурса.
Часто используется для CORS preflight-проверок.
8. CONNECT
Устанавливает туннель — в основном используется для HTTPS через прокси.
9. TRACE
Возвращает полученный сервером запрос "как есть".
Нужен в основном для отладки
👉 @SQLPortal
1. GET
Читает данные без их изменения.
Безопасный и идемпотентный — повторный запрос возвращает тот же результат.
2. POST
Создаёт новые ресурсы.
Не является идемпотентным — повтор может привести к дублированию данных.
3.PUT
Создаёт или полностью заменяет ресурс по известному URL.
Идемпотентный, идеально подходит для полных обновлений.
4. PATCH
Используется для частичных обновлений, изменяя только нужные поля.
5. DELETE
Удаляет ресурс.
Идемпотентный — повторное удаление не приведёт к ошибке. Часто используется в API для удаления записей.
6. HEAD
Как GET, но возвращает только заголовки (без тела ответа).
Удобен для проверки наличия ресурса или метаданных.
7. OPTIONS
Показывает доступные HTTP-методы для ресурса.
Часто используется для CORS preflight-проверок.
8. CONNECT
Устанавливает туннель — в основном используется для HTTPS через прокси.
9. TRACE
Возвращает полученный сервером запрос "как есть".
Нужен в основном для отладки
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Supabase выпустила бесплатный курс по изучению PostgreSQL с нуля и пошагово.
✓ Более 5 часов контента в 39 видео
✓ Запросы,
✓ Всё объясняется постепенно и на практических примерах
https://databaseschool.com/series/intro-to-postgres/videos/203
👉 @SQLPortal
✓ Более 5 часов контента в 39 видео
✓ Запросы,
JOIN, JSON, индексы и создание таблиц✓ Всё объясняется постепенно и на практических примерах
https://databaseschool.com/series/intro-to-postgres/videos/203
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2