Гид по технологиям

SQL в Go: database/sql, драйверы и лучшие практики

5 min read Go программирование Обновлено 09 Jan 2026
SQL в Go: database/sql и драйверы
SQL в Go: database/sql и драйверы

Используйте пакет database/sql из стандартной библиотеки для работы с реляционными БД в Go. Подключайте соответствующий драйвер (MySQL, PostgreSQL, SQLite и т.д.), применяйте подготовленные выражения и транзакции, контролируйте пул соединений и обязательно защищайте запросы от SQL-инъекций. Этот материал объясняет основные приёмы, отличия драйверов и даёт практические шаблоны кода и чек-листы.

Схема: несколько компонентов подключены к центральному серверу базы данных

Что такое SQL и как Go взаимодействует с реляционными базами данных

SQL — язык структурированных запросов для работы с таблицами, строками и столбцами. В Go стандартный пакет database/sql обеспечивает абстракцию для выполнения SQL-команд и работы с результатами. Конкретную реализацию обеспечивает драйвер для вашего СУБД.

Коротко: database/sql — это интерфейс. Драйвер — реализация для конкретной СУБД.

Основные драйверы для Go

  • github.com/go-sql-driver/mysql — MySQL/MariaDB
  • github.com/lib/pq или jackc/pgx — PostgreSQL (pgx часто быстрее и даёт больше возможностей)
  • github.com/mattn/go-sqlite3 — SQLite
  • github.com/denisenkom/go-mssqldb — Microsoft SQL Server

Ресурс по драйверам и популярности баз данных для Go

Установка и импорт драйвера

Создайте модуль Go (go mod init) и установите нужный драйвер:

go get -u github.com/go-sql-driver/mysql
go get github.com/mattn/go-sqlite3

Импортируйте пакет database/sql и драйвер для побочных эффектов (underscore import):

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)

Так вы регистрируете драйвер в runtime, а код взаимодействует через database/sql.

Подключение к базе данных

Используйте sql.Open для создания объекта *sql.DB. sql.Open не обязательно открывает соединение немедленно — он возвращает объект-менеджер соединений:

db, err := sql.Open("sqlite3", "models/testdb.db") // SQLite

// или

db, err := sql.Open("mysql", "user:password@/dbname") // MySQL

Нужно проверить ошибку и протестировать соединение:

if err != nil {
    log.Fatalln(err)
}

if err := db.Ping(); err != nil {
    log.Fatalln("не удалось подключиться к БД:", err)
}

Важные операции с объектом *sql.DB

  • db.Close() — закрывает пул соединений при завершении приложения.
  • db.Ping() или db.PingContext(ctx) — проверяет доступность БД.
  • db.SetMaxOpenConns(n), db.SetMaxIdleConns(n), db.SetConnMaxLifetime(d) — настраивают пул соединений.

Пример: настройка пула

db.SetMaxOpenConns(25)
db.SetMaxIdleConns(25)
db.SetConnMaxLifetime(5 * time.Minute)

Подготовленные выражения и Exec

Подготовленные выражения помогают избежать SQL-инъекций и ускоряют многократное выполнение одинаковых запросов.

command, err := db.Prepare("CREATE TABLE IF NOT EXISTS login(username TEXT, password TEXT)")
if err != nil {
    log.Fatalln(err)
}
_, err = command.Exec()
if err != nil {
    log.Fatalln(err)
}

Вставка с плейсхолдерами (формат плейсхолдеров зависит от драйвера):

command, err := db.Prepare("INSERT INTO login(username, password) values(?,?)")
exec, err := command.Exec(value1, value2)

Проверка результата:

affected, err := exec.RowsAffected()
if err != nil { return }
fmt.Println("rows affected:", affected)

id, err := exec.LastInsertId()
if err != nil { return }
fmt.Println("last id:", id)

Внимание: PostgreSQL-драйверы обычно используют нумерованные плейсхолдеры $1, $2 вместо ?

Запросы и чтение результатов

Для выборок используйте db.Query или db.QueryContext:

rows, err := db.Query("SELECT username, password FROM User")
if err != nil { return }
defer rows.Close()

var username, password string
for rows.Next() {
    if err := rows.Scan(&username, &password); err != nil {
        log.Fatalln(err)
    }
    fmt.Println(username, password)
}

if err := rows.Err(); err != nil {
    log.Fatalln(err)
}

Используйте rows.Close и rows.Err для корректной обработки ошибок.

Транзакции

Транзакции гарантируют атомарность нескольких операций:

ctx := context.Background()
tx, err := db.BeginTx(ctx, nil)
if err != nil { log.Fatalln(err) }

_, err = tx.ExecContext(ctx, "UPDATE accounts SET balance = balance - ? WHERE id = ?", 100, fromID)
if err != nil {
    tx.Rollback()
    return
}

_, err = tx.ExecContext(ctx, "UPDATE accounts SET balance = balance + ? WHERE id = ?", 100, toID)
if err != nil {
    tx.Rollback()
    return
}

if err := tx.Commit(); err != nil {
    tx.Rollback()
}

Используйте контекст для таймаутов и отмены.

Различия между драйверами и плейсхолдерами

  • MySQL и SQLite обычно принимают знак вопроса (?) как плейсхолдер.
  • PostgreSQL (lib/pq, pgx) использует $1, $2, …
  • MSSQL имеет свои особенности в строке подключения и параметрах.

Учитывайте это при портировании кода между СУБД.

Обработка ошибок и контексты

Всегда проверяйте и логируйте ошибки. Для веб-приложений используйте context.Context для запросов к базе, чтобы прерывать долгие операции при разрыве HTTP-соединения:

ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
defer cancel()
row := db.QueryRowContext(ctx, "SELECT ... WHERE id = ?", id)

Миграции схемы

Для управления схемой используйте инструменты миграций (например, golang-migrate, goose). Ручные CREATE TABLE в коде подходят для быстрых прототипов, но для продакшена нужны версионируемые миграции.

Безопасность и жёсткая защита

  • Никогда не конкатенируйте строки для SQL — используйте параметризованные запросы.
  • Ограничьте привилегии учетной записи БД только необходимыми правами.
  • Шифруйте соединения при необходимости (TLS).
  • Логируйте запросы с осторожностью — не записывайте пароли или персональные данные.

Когда database/sql не подходит

  • Если нужен ORM с автоматическим отображением моделей на таблицы, рассмотрите GORM или sqlc/ent.
  • Для высокопроизводительных приложений PostgreSQL pgx может дать лучшие результаты.
  • Для простых скриптов SQLite иногда удобнее, но не для параллельной нагрузки.

Альтернативные подходы

  • ORM (GORM, ent) упрощают CRUD и миграции, но скрывают SQL и могут снизить контроль.
  • SQL генераторы (sqlc) генерируют типобезопасный код на основе SQL-результатов.
  • Использование драйвера pgx без database/sql даёт доступ к расширенным возможностям PostgreSQL.

Контрпримеры и когда оно ломается

  • Небрежная работа с пулом соединений приводит к исчерпанию fd и таймаутам.
  • Отсутствие правильной обработки ошибок в транзакциях может оставить данные в неконсистентном состоянии.
  • Неправильные плейсхолдеры при смене драйвера приводят к синтаксическим ошибкам.

Мини-методология для добавления БД в проект

  1. Выберите СУБД по требованиям: ACID, масштабирование, репликация.
  2. Добавьте модуль миграций и создайте базовую схему.
  3. Подключите драйвер и настроьте пул соединений.
  4. Покройте критические операции транзакциями и тестами.
  5. Настройте мониторинг каналов ошибок и метрик пула.

Ролевые чек-листы

Разработчик

  • Использовать контексты для запросов.
  • Применять параметризованные запросы.
  • Закрывать rows и stmt.
  • Писать юнит- и интеграционные тесты с тестовой БД.

Администратор БД

  • Настроить безопасную учётную запись и бэкапы.
  • Контролировать лимиты соединений и метрики.
  • Прямо тестировать миграции на стейджинге.

Операции и DevOps

  • Настроить мониторинг latency, connection pool usage и ошибок.
  • Обеспечить TLS и ротацию паролей.
  • Планировать резервирование и восстановление.

Критерии приёмки

  • Приложение подключается и отвечает на Ping.
  • Миграции применяются без ошибок.
  • Критические сценарии завершаются атомарно (транзакции).
  • Запросы не содержат конкатенированных параметров.

Шаблоны и сниппеты

Пример безопасного запроса с контекстом:

ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
row := db.QueryRowContext(ctx, "SELECT name FROM users WHERE id = ?", id)
var name string
if err := row.Scan(&name); err != nil {
    if err == sql.ErrNoRows {
        // обработать отсутствие
    } else {
        return err
    }
}

Пример try/rollback для транзакций:

func transfer(ctx context.Context, db *sql.DB, fromID, toID int, amount int) error {
    tx, err := db.BeginTx(ctx, nil)
    if err != nil { return err }
    defer func() {
        if p := recover(); p != nil {
            tx.Rollback()
            panic(p)
        }
    }()

    // операции tx.ExecContext...

    if err := tx.Commit(); err != nil {
        tx.Rollback()
        return err
    }
    return nil
}

Совместимость и миграция

  • При смене БД проверьте SQL-диалекты (BOOLEAN, AUTOINCREMENT, LIMIT/OFFSET).
  • Используйте абстракции SQL только там, где они действительно нужны.
  • Тестируйте на стейджинге с той же СУБД, что и прод.

Короткая справка по отладке

  • Проверяйте пул соединений: превышение MaxOpenConns = блокировки.
  • Включайте подробный лог драйвера на время разработки.
  • Используйте EXPLAIN для медленных запросов.

Короткий план внедрения в проект

  1. Добавить драйвер и базовую конфигурацию db в init.
  2. Настроить миграции и выполнить на стейджинге.
  3. Писать тесты, покрывающие CRUD и транзакции.
  4. Настроить мониторинг и алерты на ошибки БД.

Итог

Пакет database/sql даёт надёжную и универсальную основу для работы с реляционными базами в Go. Комбинация подготовленных выражений, транзакций, корректной настройки пула и использования контекстов позволит создать стабильное и безопасное приложение. При необходимости используйте ORM или более низкоуровневые драйверы для специальных возможностей или оптимизации.

Важно

  • Всегда используйте параметризованные запросы.
  • Контролируйте пул соединений.
  • Тестируйте миграции и критические пути с реальной СУБД.

Краткое резюме

SQL в Go — это сочетание database/sql и подходящего драйвера. Понимание различий между драйверами, управление транзакциями, соблюдение безопасности и настройка пула соединений — ключевые навыки успешного использования баз данных в Go.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

RDP: полный гид по настройке и безопасности
Инфраструктура

RDP: полный гид по настройке и безопасности

Android как клавиатура и трекпад для Windows
Гайды

Android как клавиатура и трекпад для Windows

Советы и приёмы для работы с PDF
Документы

Советы и приёмы для работы с PDF

Calibration в Lightroom Classic: как и когда использовать
Фото

Calibration в Lightroom Classic: как и когда использовать

Отключить Siri Suggestions на iPhone
iOS

Отключить Siri Suggestions на iPhone

Рисование таблиц в Microsoft Word — руководство
Office

Рисование таблиц в Microsoft Word — руководство