Go: PostgreSQL

12 вопросов

1 Чем PostgreSQL отличается от MySQL с точки зрения Go?

Postgres: плейсхолдеры $1, $2, богатые типы (JSONB, array, custom), строгая консистентность, CTE, оконные функции, расширения. MySQL: ?, проще репликация "из коробки". В Go для Postgres часто используют pgx (нативный протокол, быстрее database/sql); для MySQL - go-sql-driver/mysql. Выбор по требованиям к данным и экосистеме.

Открыть отдельно →
2 Что такое VACUUM в PostgreSQL? Зачем нужен?

VACUUM освобождает место от мертвых строк (удаленных или старых версий при MVCC). Обычно не возвращает место ОС, но помечает для переиспользования. VACUUM FULL переписывает таблицу и возвращает место, но блокирует таблицу. autovacuum запускается по расписанию. Без вакуума растет bloat и падает производительность. В Go приложение не вызывает VACUUM напрямую - это задача администрирования.

VACUUM ANALYZE users;
VACUUM FULL orders;  -- только при необходимости, блокировка
Открыть отдельно →
3 Как работать с JSONB в Postgres из Go?

JSONB хранится в бинарном виде, поддерживает индексы (GIN), операторы @>, ?, jsonb_path_query. В Go сканируют в []byte и json.Unmarshal или в тип с datatype (pgx). Запросы с условиями по полям JSON: WHERE data @> $1, передают JSON. Для сложной логики используют сырой SQL с функциями jsonb_*.

var data []byte
err := db.QueryRowContext(ctx, "SELECT data FROM events WHERE data @> $1", "{\"type\":\"click\"}").Scan(&data)
Открыть отдельно →
4 Партиционирование таблиц в PostgreSQL. Зачем в Go-приложении?

Партиционирование разбивает таблицу на части по диапазону/списку/хешу. Запросы с условием по ключу партиционирования обращаются только к нужным партициям (partition pruning). В Go пишут запросы как к одной таблице; драйвер и БД выбирают партиции. Упрощает архивацию и удаление старых данных, ускоряет запросы по ключу партиции.

CREATE TABLE orders (id BIGSERIAL, user_id BIGINT, ...) PARTITION BY RANGE (created_at);
CREATE TABLE orders_2024 PARTITION OF orders FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');
Открыть отдельно →
5 Что такое pg_stat_statements? Как использовать для оптимизации?

Расширение pg_stat_statements собирает статистику по выполненным запросам: нормализованный текст, число вызовов, суммарное/среднее время, строки. Включают: shared_preload_libraries = 'pg_stat_statements', CREATE EXTENSION pg_stat_statements. Запросы к представлению показывают самые тяжелые запросы. В Go можно логировать медленные запросы (pgx с логгером) и сопоставлять с pg_stat_statements.

SELECT query, calls, total_exec_time, mean_exec_time FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;
Открыть отдельно →
6 Что такое PgBouncer? Как настроить пул в Go?

PgBouncer - пулер соединений к Postgres. Приложение подключается к PgBouncer; PgBouncer держит меньше реальных соединений к БД. Режимы: session (один клиент - одно соединение на сессию), transaction (соединение возвращается после каждой транзакции), statement. В Go подключаются к хосту/порту PgBouncer; настройки пула (MaxOpenConns, MaxIdleConns) задают под размер пула PgBouncer и лимиты БД.

db.SetMaxOpenConns(50)
db.SetMaxIdleConns(10)
Открыть отдельно →
7 Advisory locks в Postgres. Пример из Go.

Advisory lock - блокировка на уровне приложения по целому числу (или паре). Не привязана к строке таблицы. Используют для координации воркеров: один воркер на задачу по id. pg_advisory_lock(id) блокирует до освобождения; pg_try_advisory_lock не блокирует, возвращает true/false. В Go вызывают SELECT pg_advisory_lock($1), затем работу, pg_advisory_unlock (или закрытие соединения снимает lock в session mode).

_, err := db.ExecContext(ctx, "SELECT pg_advisory_lock($1)", jobID)
defer db.ExecContext(ctx, "SELECT pg_advisory_unlock($1)", jobID)
Открыть отдельно →
8 Что такое CREATE INDEX CONCURRENTLY?

Построение индекса без эксклюзивной блокировки таблицы на запись. INSERT/UPDATE/DELETE продолжают работать. Нельзя выполнять внутри транзакции. Дольше обычного CREATE INDEX. При сбое индекс может остаться INVALID - тогда DROP и создать заново. В миграциях (goose, golang-migrate) для прода используют CONCURRENTLY, чтобы избежать даунтайма.

CREATE INDEX CONCURRENTLY idx_orders_created ON orders(created_at);
Открыть отдельно →
9 Что такое TOAST в PostgreSQL?

TOAST (The Oversized-Attribute Storage Technique) - большие значения (> 2 KB) хранятся в отдельной TOAST-таблице; в строке остается ссылка. Поля TEXT, JSONB, большие bytea сжимаются и/или выносятся. Сканирование основной таблицы быстрее, когда большие столбцы не запрашиваются. В Go при чтении только нужных столбцов лишние TOAST-атрибуты не подгружаются.

Открыть отдельно →
10 Полнотекстовый поиск в Postgres из Go.

Типы tsvector и tsquery, функции to_tsvector, to_tsquery, plainto_tsquery; оператор @@. GIN-индекс по tsvector ускоряет поиск. В Go формируют запрос с условием: WHERE to_tsvector('russian', body) @@ plainto_tsquery('russian', $1), передают строку поиска. Конфигурация языка задает стемминг и стоп-слова.

rows, err := db.QueryContext(ctx,
    "SELECT * FROM articles WHERE to_tsvector('russian', body) @@ plainto_tsquery('russian', $1)", query)
Открыть отдельно →
11 Sequences, SERIAL и IDENTITY в Postgres. Использование в Go.

SERIAL/BIGSERIAL - удобная запись для столбца с default nextval(sequence). IDENTITY (SQL standard) - то же по смыслу, явный синтаксис. При вставке без значения подставляется nextval. В Go при INSERT возврат сгенерированного id: RETURNING id в запросе и Scan в переменную, или QueryRow после Insert с RETURNING. Пропуски в последовательности возможны (откат транзакции, сбой).

var id int64
err := db.QueryRowContext(ctx, "INSERT INTO users (name) VALUES ($1) RETURNING id", name).Scan(&id)
Открыть отдельно →
12 Как находить и логировать медленные запросы в Go и Postgres?

В Postgres: log_min_duration_statement, pg_stat_statements. В Go: драйвер с логгером (pgx - pgx.Tracer, database/sql - обертка с логированием времени и запроса). Middleware или обертка над *sql.DB логирует каждый запрос и длительность. При превышении порога - логировать с уровнем warn и отправлять в систему мониторинга. Трейсинг (OpenTelemetry) привязывает запросы к span.

// обертка: перед Exec/Query засечь время, после - залогировать и запрос, и duration
Открыть отдельно →
🧠Квиз 🏆Лидеры 🎯Собесед. 📖Вопросы 📚База зн.