Работа с PostgreSQL в языке Go

Оглавление

  1. Синтаксис и категории команд SQL
  2. Ограничения (Constraints)
  3. Подходы к работе с БД в Go
  4. Драйверы для PostgreSQL
  5. Запуск и миграции
  6. Практическая работа с pgx
  7. Методы написания SQL-запросов

1. Синтаксис и категории команд SQL

SQL (Structured Query Language) подразделяется на несколько функциональных групп:

DDL (Data Definition Language) — Определение данных

Команды для управления структурой (схемой) объектов базы данных.

  • CREATE: Создание новых объектов (таблиц, представлений, индексов).
  • ALTER: Модификация существующих объектов (изменение типов столбцов, добавление полей).
  • DROP: Удаление объектов.

DML (Data Manipulation Language) — Манипуляция данными

Команды для работы с содержимым таблиц.

  • SELECT: Извлечение (чтение) записей.
  • INSERT: Создание новых записей.
  • UPDATE: Модификация существующих записей.
  • DELETE: Удаление записей.

DCL (Data Control Language) — Управление доступом

Команды для управления правами и безопасностью.

  • GRANT: Предоставление пользователю определенных прав.
  • REVOKE: Отзыв ранее выданных прав.

TCL (Transaction Control Language) — Управление транзакциями

Команды для обеспечения свойств ACID (атомарности и надежности).

  • BEGIN: Инициализация транзакции.
  • COMMIT: Фиксация изменений в базе данных.
  • ROLLBACK: Откат изменений к исходному состоянию при ошибке.

2. Ограничения (Constraints)

Правила целостности, обеспечивающие корректность данных (Consistency) на уровне схемы:

  • NOT NULL: Запрещает сохранение пустого значения в столбце.
  • UNIQUE: Обеспечивает уникальность значений во всех строках столбца.
  • PRIMARY KEY: Уникальный идентификатор строки (автоматически включает UNIQUE и NOT NULL).
  • FOREIGN KEY: Ссылочная целостность (связывает столбец с первичным ключом другой таблицы).
  • CHECK: Проверка значений на соответствие заданному логическому условию.
  • EXCLUDE: Ограничение-исключение для предотвращения пересекающихся данных.

3. Подходы к работе с БД в Go

Общий интерфейс database/sql

  • Суть: Стандартный пакет Go, задающий универсальный контракт для работы с SQL.
  • Механизм регистрации: Драйверы регистрируются через функцию init(). Используется “пустой” импорт: import _ "github.com/lib/pq".
  • Использование: Приложение работает через sql.Open("driverName", "DSN") и стандартные методы db.Query, db.Exec. Это обеспечивает независимость кода от конкретной реализации СУБД.
  • Пул соединений: Пакет содержит встроенную структуру DB, которая автоматически управляет пулом соединений (создание, переиспользование, закрытие).

Кастомные драйверы

  • Суть: Библиотеки, не использующие интерфейс database/sql.
  • Преимущества: Предоставляют доступ к специфическим функциям СУБД (например, асинхронность в Postgres) и могут быть производительнее.
  • Пример: clientv3 для хранилища etcd.

4. Драйверы для PostgreSQL

  1. lib/pq:
    • Реализует подход database/sql.
    • Полностью нативный (Go).
    • Статус: Устарел (Maintenance Mode). Не рекомендуется для новых систем.
  2. pgx:
    • Современный и активно развивающийся драйвер.
    • Версии: pgx/v4, pgx/v5 (актуальные).
    • Особенности: Может работать как через интерфейс database/sql (через адаптер), так и через собственный расширенный интерфейс. Рекомендуемый выбор.

5. Запуск и миграции

Способы запуска СУБД

  • Локально: Установка в ОС (brew, apt). Удобно для тестов, данные сохраняются.
  • Docker (Контейнер): Изолированное окружение, легкое управление версиями. Требует настройки volumes для сохранения данных и использования инструментов миграций.
  • Внешний инстанс: Облачные решения или корпоративные БД.

Автоматизация изменений (Migrations as Code)

Использование SQL-скриптов вручную не рекомендуется из-за риска ошибок.

  • Инструмент goose:
    • Поддерживает двунаправленность (Up — накат, Down — откат).
    • Хранит состояние примененных миграций непосредственно в БД.
    • Каждая миграция по умолчанию выполняется в транзакции (TCL).
  • Лучшие практики миграций:
    • Минимальная логика в одном файле.
    • Осмысленные имена.
    • Обратная совместимость: Изменения не должны ломать работу старых версий приложения (актуально для постепенного обновления/канареечных выкаток).

6. Практическая работа с pgx

Подключение и контекст

  • pgx.Connect(ctx, "DSN"): Одиночное соединение.
  • DSN (Data Source Name): Формат postgres://user:password@host:port/dbname.
  • Правила:
    • Всегда использовать context для контроля таймаутов.
    • Всегда закрывать соединение через defer conn.Close(ctx).

Пул соединений (pgxpool)

В нагруженных приложениях вместо одиночных коннектов используется пул:

  • pgxpool.New(ctx, "DSN"): Создание пула.
  • Настройки (лимиты, таймауты) передаются через параметры в DSN-строке.

Выполнение запросов

  • Одна строка: db.QueryRow(...).Scan(&var).
  • Множество строк: db.Query(...) возвращает итератор Rows. Необходимо обходить его в цикле и вызывать rows.Scan().
  • Библиотека scany: Позволяет автоматически маппить (заполнять) структуры Go данными из БД, сокращая объем шаблонного кода.

7. Методы написания SQL-запросов

Подход Инструменты Плюсы Минусы
Raw SQL pgx, database/sql Гибкость, прозрачность, макс. скорость. Много boilerplate-кода, нет проверки типов при компиляции.
Query Builder squirrel Защита от инъекций, динамические запросы. Доп. слой абстракции.
ORM GORM Высокая скорость разработки. Низкая производительность, неявный SQL (риск неэффективных запросов).
Codegen sqlc Типобезопасность, скорость Raw SQL. Сложность с динамическими запросами.

Резюме по Codegen (sqlc)

Принцип: Разработчик пишет чистый SQL, а утилита генерирует строго типизированный Go-код для выполнения этих запросов. Сочетает производительность Raw SQL и удобство типизации.