Расскажите о вашем опыте работы с PostgreSQL в Go-приложениях.

Ответ

Да, я активно использовал PostgreSQL в качестве основной СУБД для Go-приложений. Мой опыт включает работу через стандартный интерфейс database/sql и современные драйверы.

Основные инструменты:

  • database/sql: Стандартный пакет Go, который предоставляет общий интерфейс для работы с SQL-базами данных.
  • Драйверы: В основном использовал pgx, так как он является наиболее производительным и функциональным современным драйвером. Ранее также работал с lib/pq.

Ключевые концепции database/sql:


  1. sql.DB — это пул соединений: Важно понимать, что sql.DB представляет собой потокобезопасный пул соединений, а не одно конкретное подключение. Его нужно создавать один раз при старте приложения и переиспользовать.


  2. Разделение запросов:

    • db.QueryRowContext(): Для запросов, возвращающих одну строку. Результат считывается через .Scan().
    • db.QueryContext(): Для запросов, возвращающих множество строк. Результат итерируется в цикле с обязательным rows.Close() для возврата соединения в пул.
    • db.ExecContext(): Для команд, не возвращающих строки (INSERT, UPDATE, DELETE).

Пример запроса одной строки:

import (
    "database/sql"
    "context"
    _ "github.com/jackc/pgx/v5/stdlib"
)

var db *sql.DB // Инициализируется при старте

func GetUserName(ctx context.Context, userID int) (string, error) {
    var name string
    // Используем QueryRowContext для запроса одной строки
    err := db.QueryRowContext(ctx, "SELECT name FROM users WHERE id = $1", userID).Scan(&name)
    if err != nil {
        if err == sql.ErrNoRows {
            // Это не ошибка приложения, а ожидаемый результат "не найдено"
            return "", fmt.Errorf("user not found")
        }
        // Реальная ошибка БД
        return "", err
    }
    return name, nil
}

Мой опыт также включает:

  • Транзакции: Использование db.Begin() для выполнения группы операций в атомарной транзакции с COMMIT или ROLLBACK.
  • Подготовленные запросы: Работа с db.PrepareContext() для повышения производительности часто выполняемых запросов и защиты от SQL-инъекций.
  • Миграции БД: Использование инструментов вроде golang-migrate/migrate или goose для версионирования схемы базы данных.
  • Работа с ORM: Имею представление о работе с ORM-библиотеками, такими как GORM или sqlc (генератор кода), и понимаю их преимущества и недостатки.