Аналитика Telegram-канала
Подписчики
288
Охват за 24 ч
68
ER за 24 ч
23,61%
Публикаций
137
Динамика подписчиков
7 дней
30 дней
6 месяцев
Больше
27 окт30 окт2 ноя
Показаны последние 7 дней
Больше истории →
Охваты публикаций
27.1028.1029.1030.1001.1101.11
21
Средний охват
65
Лучший результат
Показаны 6 публикаций — последние в канале
Больше статистики →
Упоминания
6
Упоминаний
0
Из них репостов
5
Каналов
0
Упоминаний
0
Из них репостов
0
Каналов
Последние посты
3 из 137 2 из 137
🐘 Как устроено подключение к PostgreSQL: что происходит внутри БД?
Продолжаем изучать PostgreSQL под капотом.
В прошлом посте разобрали путь SQL-запроса от приложения до выполнения. Теперь посмотрим, что происходит ещё раньше — как устанавливается соединение и кто обслуживает клиента.
Коротко:
PostgreSQL использует архитектуру process-per-connection. Для каждого прямого клиентского подключения создаётся отдельный backend process, который обслуживает SQL-запросы на протяжении всей сессии.
1. Установка соединения
Допустим, приложение подключается к PostgreSQL:
app → postgres:5432
Сначала устанавливается TCP-соединение или используется Unix socket для локального подключения.
При необходимости согласовывается TLS.
Главный процесс PostgreSQL — postmaster — принимает новое подключение и создаёт отдельный backend process.
Получается:
Client A → Backend PID 1001
Client B → Backend PID 1002
Client C → Backend PID 1003
Важный момент: PostgreSQL создаёт отдельный процесс на соединение, а не на каждый SQL-запрос.
2. Authentication — проверка доступа
Клиент передаёт параметры подключения, включая имя пользователя и базу данных.
PostgreSQL обращается к:
pg_hba.conf
Этот файл определяет, какие подключения разрешены и какой метод аутентификации использовать.
Например:
host all app_user 10.0.0.0/8 scram-sha-256
В данном случае для подходящего подключения используется SCRAM-SHA-256.
PostgreSQL поддерживает и другие методы: сертификаты, LDAP, GSSAPI и т.д.
После успешной аутентификации и необходимых проверок доступа сессия становится готовой к работе.
3. Session — что хранится в соединении
После подключения создаётся контекст сессии.
С ним связаны:
• текущая роль и параметры сессии
• search_path
• временные таблицы
• prepared statements
• состояние транзакции
• session-level advisory locks
Например, приложение выполняет:
SET search_path TO app;
Эта настройка будет действовать в рамках соответствующей сессии.
Именно поэтому повторное использование соединений через connection pool требует аккуратного управления session state.
4. Backend Process — кто выполняет SQL
Теперь клиент отправляет:
SELECT * FROM users WHERE id = 42;
Запрос получает backend process, закреплённый за соединением.
Он выполняет:
Parse → Plan → Execute → Result
При этом backend взаимодействует с общими структурами PostgreSQL:
Shared Buffers
WAL Buffers
Lock Manager
Хотя процессы отдельные, они используют shared memory и механизмы синхронизации.
После выполнения запроса backend не исчезает, а ожидает следующую команду от клиента.
5. Где возможен production-инцидент?
Представим, что приложение перезапустили и одновременно поднялись 20 replicas.
Каждая создаёт пул:
pool size = 20
Получаем потенциально:
20 × 20 = 400 connections
Но PostgreSQL ограничивает количество подключений через:
max_connections
Если доступные слоты закончились, новые клиенты начнут получать ошибки подключения.
А большое количество backend processes дополнительно расходует память и ресурсы CPU.
Именно поэтому больше соединений не всегда означает больше производительности.
Для ограничения количества подключений часто используют PgBouncer.
6. Как диагностировать
Посмотреть соединения можно через:
pg_stat_activity
Например:
SELECT pid, usename, application_name, state, wait_event_type, wait_event FROM pg_stat_activity;
Особенно интересны состояния:
• active — выполняется запрос
• idle — соединение ожидает команду
• idle in transaction — транзакция открыта, но запрос сейчас не выполняется
Последнее состояние особенно опасно: длительные транзакции могут удерживать блокировки и мешать очистке старых версий строк через VACUUM.
📌 Вывод:
подключение к PostgreSQL — это не просто открытый TCP-сокет.
Connection → Authentication → Backend Process → Session → SQL
Каждое прямое соединение получает собственный backend process и контекст сессии.
Поэтому при проблемах с PostgreSQL важно анализировать не только медленные SQL-запросы, но и количество соединений, состояние сессий и поведение connection pool.
#postgresql #database #architecture #production
ещё
5
1
1 ноя, 21:05
🗄 Что происходит, когда приложение отправляет SQL-запрос?
Коллеги, Kubernetes мы уже достаточно подробно разобрали, поэтому предлагаю перейти к следующей большой теме — внутреннему устройству баз данных.
Также важно понять, нравятся ли вам посты про БД или нет. Прошу дать обратную связь :)
Начнём с PostgreSQL.
Разберём полный путь SQL-запроса, а в следующих постах будем постепенно углубляться в отдельные механизмы: индексы, планировщик, MVCC, WAL, блокировки и работу с памятью.
Коротко:
когда приложение отправляет SQL-запрос, PostgreSQL не начинает сразу читать данные с диска. Сначала запрос проходит несколько этапов: разбор, проверку, оптимизацию, выполнение и только затем возвращает результат.
1. Connection — подключение к БД
Допустим, приложение выполняет:
SELECT * FROM users WHERE id = 42;
Сначала необходимо соединение с PostgreSQL.
Обычно приложение использует connection pool, чтобы не создавать новое TCP-соединение для каждого запроса.
Упрощённо:
Application → TCP :5432 → PostgreSQL
В классической архитектуре PostgreSQL подключение обслуживает отдельный backend process.
Он получает SQL-запрос и начинает его обработку.
2. Parser — разбор SQL
PostgreSQL получает текст запроса и проверяет его синтаксис.
Например:
SELECT * FORM users;
Здесь ошибка, потому что написано FORM вместо FROM.
Если синтаксис корректный, Parser формирует дерево разбора.
Далее выполняется семантический анализ:
• существует ли таблица
• существуют ли указанные столбцы
• корректны ли типы данных
• есть ли необходимые права доступа
Также PostgreSQL может преобразовать запрос через Rewrite System, например при обращении к View.
3. Planner — выбор плана выполнения
Теперь PostgreSQL должен решить, как именно получить данные.
Допустим, таблица users содержит миллион строк.
Есть несколько вариантов:
Seq Scan → прочитать таблицу
Index Scan → использовать индекс
Planner оценивает возможные планы с учётом статистики, количества строк, стоимости I/O и CPU.
Выбирается план с наименьшей оценочной стоимостью.
Причём наличие индекса не гарантирует его использование!
Иногда последовательное чтение таблицы оказывается выгоднее.
Для подготовленных запросов план может использоваться повторно.
4. Executor — выполнение запроса
После выбора плана начинается выполнение.
Например, Planner выбрал:
Index Scan using users_pkey
Executor обращается к индексу, находит нужную запись и получает данные.
Но это не обязательно означает чтение с физического диска.
PostgreSQL использует:
Shared Buffers
Если нужная страница уже находится в памяти:
Cache Hit → читаем из RAM
Если её нет:
Cache Miss → чтение через ОС
При этом данные могут оказаться в файловом кэше Linux, поэтому физический I/O тоже не гарантирован.
Здесь же PostgreSQL учитывает MVCC, чтобы определить, какие версии строк видны текущей транзакции.
5. Возврат результата
Executor формирует строки результата, PostgreSQL передаёт их клиенту через сетевой протокол.
Получаем:
PostgreSQL → Application → Client
На этом обработка SELECT завершается.
Для INSERT, UPDATE и DELETE путь сложнее: появляются изменения страниц, WAL, блокировки и другие механизмы.
Где может возникнуть production-инцидент?
Представим, запрос раньше выполнялся за:
15 ms
А после увеличения таблицы стал выполняться за:
4 seconds
Причина может быть не в CPU или диске.
Например, Planner из-за неактуальной статистики неверно оценил количество строк и выбрал неэффективный план.
Вместо:
Index Scan
получили:
Seq Scan
Теперь PostgreSQL обрабатывает гораздо больше данных.
Для диагностики используем:
EXPLAIN (ANALYZE, BUFFERS)
Он показывает фактический план выполнения, время операций и статистику обращения к буферам. Важно: ANALYZE действительно выполняет запрос.
📌 Вывод:
выполнение SQL — это целая цепочка:
Connection → Parser → Analyzer / Rewriter → Planner → Executor → Buffers / Storage → Result
Медленный запрос не всегда означает медленный диск. Проблема может находиться в соединениях, планировании, чтении данных, блокировках или передаче результата.
#postgresql #database #architecture #production
ещё
65
3
11 1 ноя, 00:03
💾 Как устроена файловая система Docker-контейнера?
Продолжаем изучать контейнеры под капотом. Теперь посмотрим, откуда внутри контейнера берутся файлы, куда записываются изменения и почему запись нескольких байт иногда приводит к копированию целого файла.
Коротко:
Docker использует слои образа и отдельный writable layer для каждого контейнера.
В распространённой реализации на базе OverlayFS работает механизм Copy-on-Write (CoW) — копирование при записи.
1. Из чего состоит Docker Image
Допустим, собираем образ:
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y nginx
COPY nginx.conf /etc/nginx/nginx.conf
При сборке формируются слои файловой системы.
Упрощённо:
Layer 1 → Ubuntu
Layer 2 → Nginx
Layer 3 → nginx.conf
Эти слои неизменяемые — read-only.
Когда Docker запускает контейнер, поверх них создаётся дополнительный слой:
Writable Layer (RW)
Получается:
Writable Layer ← изменения
Image Layer 3 (RO)
Image Layer 2 (RO)
Image Layer 1 (RO)
Контейнер видит всё как одну файловую систему.
2. Как работает OverlayFS
В Linux для этого часто используется OverlayFS.
У него есть три важных понятия:
• lowerdir — нижние read-only слои образа
• upperdir — верхний writable layer контейнера
• merged — объединённое представление файловой системы
Когда приложение читает:
/etc/nginx/nginx.conf
OverlayFS ищет файл в верхнем слое, а если его там нет — в нижних.
Приложение при этом не знает, в каком именно слое лежит файл.
3. Что такое Copy-on-Write
Теперь самое интересное.
Допустим, в образе есть файл размером:
1 GB
Приложение открыло его и хочет изменить всего несколько байт.
Но нижние слои образа изменять нельзя.
Поэтому OverlayFS выполняет:
lowerdir → copy_up → upperdir
То есть при первой записи файл копируется в writable layer, после чего изменяется уже его копия.
Важный нюанс: классический OverlayFS выполняет copy_up на уровне файла, а не отдельных блоков.
Поэтому изменение нескольких байт в большом файле может привести к копированию всего файла.
Последующие записи выполняются уже в верхнем слое без повторного полного copy_up.
4. Где здесь production-инцидент?
Представим приложение, в образ которого положили большой файл данных.
После запуска оно изменяет небольшой участок этого файла.
Внезапно наблюдаем:
• увеличение дискового I/O
• рост занятого пространства на ноде
• задержки при первой записи
• увеличение writable layer контейнера
Причина может быть не в объёме новых данных, а в copy_up большого файла из image layer.
Особенно неприятно, если несколько контейнеров одновременно начинают изменять такие файлы.
Каждый контейнер имеет собственный writable layer, поэтому копирование выполняется отдельно.
5. Чем Volume отличается от writable layer
Допустим, приложение пишет данные в:
/var/lib/postgresql/data
Без отдельного mount данные попадут в writable layer контейнера.
При удалении контейнера этот слой исчезнет.
Теперь подключаем Docker Volume:
postgres-data:/var/lib/postgresql/data
Данные записываются в подключённый volume, минуя writable layer для этого пути.
Получаем:
Writable Layer:
• принадлежит конкретному контейнеру
• удаляется вместе с ним
• использует механизм слоёв файловой системы
Volume:
• существует независимо от контейнера
• сохраняет данные после его удаления
• подходит для постоянных и интенсивно изменяемых данных
Важно: обычный docker restart не удаляет writable layer. Данные исчезают именно при удалении и пересоздании контейнера без переноса данных.
📌 Вывод:
контейнер не получает полноценную копию Docker Image при запуске.
Он использует общие read-only слои и собственный writable layer.
Read → чтение из существующих слоёв
Write → copy_up при необходимости
Volume → запись вне writable layer
Поэтому для баз данных, persistent storage и write-intensive workloads обычно лучше использовать volumes, а не хранить изменяемые данные непосредственно в файловой системе контейнера.
И если приложение неожиданно создаёт большую дисковую нагрузку при небольших изменениях файлов — стоит проверить, не столкнулись ли вы с Copy-on-Write.
#docker #linux #containers #architecture #production
ещё
18
6
3 30 окт, 21:09
Продолжить анализ этого канала
- История роста подписчиков
- Просмотры и охваты публикаций
- Кто упоминает канал и кого упоминает он
- Похожие каналы и подбор для рекламы
Открыть аналитику
Демо до 14 дней