Введение в pgx: мощный PostgreSQL драйвер для Go
Библиотека pgx (PostgreSQL eXtended) — это современный, высокопроизводительный драйвер и набор инструментов для работы с PostgreSQL из языка программирования Go. В отличие от стандартного пакета database/sql, pgx предоставляет прямой доступ к протоколу PostgreSQL, что даёт значительный прирост производительности и доступ к уникальным возможностям СУБД, таким как LISTEN/NOTIFY, COPY протокол, работа с массивами и составными типами, и многое другое.
Зачем использовать pgx вместо стандартного database/sql? Во-первых, pgx работает на 30-50% быстрее за счёт отсутствия лишних абстракций. Во-вторых, он поддерживает все современные возможности PostgreSQL 12+ (и более новых версий). В-третьих, pgx предоставляет богатый API для работы с соединениями, пулом соединений, подготовленными запросами и транзакциями. Наконец, pgx полностью совместим с database/sql через специальный адаптер, что позволяет использовать его в проектах, где уже используется стандартный интерфейс.
Установка pgx
Установка pgx в проект Go выполняется стандартной командой go get. Для работы с PostgreSQL вам потребуется установить основной пакет pgx и, возможно, дополнительные модули:
# Установка основного пакета pgx v5 (рекомендуемая версия)
go get github.com/jackc/pgx/v5
# Установка пакета для работы с пулом соединений
go get github.com/jackc/pgx/v5/pgxpool
# Установка адаптера для совместимости с database/sql
go get github.com/jackc/pgx/v5/stdlib
После выполнения этих команд, вы сможете импортировать pgx в свои Go-файлы. Убедитесь, что у вас установлен Go версии 1.18 или выше, так как pgx v5 использует дженерики.
Быстрый старт: первое подключение к PostgreSQL
Давайте напишем простейшую программу, которая подключается к базе данных PostgreSQL и выполняет запрос. Предположим, что у вас запущен PostgreSQL на localhost:5432, база данных называется testdb, пользователь testuser с паролем testpass.
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/jackc/pgx/v5"
)
func main() {
// Строка подключения
connStr := "postgres://testuser:testpass@localhost:5432/testdb"
// Устанавливаем соединение
conn, err := pgx.Connect(context.Background(), connStr)
if err != nil {
log.Fatalf("Не удалось подключиться к БД: %v", err)
}
defer conn.Close(context.Background())
// Выполняем простой запрос
var greeting string
err = conn.QueryRow(context.Background(), "SELECT 'Hello, PostgreSQL!'").Scan(&greeting)
if err != nil {
log.Fatalf("Ошибка выполнения запроса: %v", err)
}
fmt.Println(greeting)
}
Этот код устанавливает соединение, выполняет запрос и выводит результат. Обратите внимание на использование context.Background() — это стандартный способ передачи контекста в Go, который позволяет контролировать таймауты и отмену операций.
Основные методы и функции pgx
Рассмотрим наиболее важные методы и функции библиотеки pgx. Для каждого метода приведём описание, параметры и рабочий пример кода.
1. pgx.Connect — установка соединения
Что делает: Создаёт новое подключение к PostgreSQL. Это основной способ начать работу с базой данных.
Параметры:
ctx context.Context— контекст для управления временем жизни соединенияconnString string— строка подключения в формате URI или DSN
Возвращает: (*pgx.Conn, error)
Пример:
conn, err := pgx.Connect(context.Background(), "postgres://user:pass@localhost:5432/mydb")
if err != nil {
log.Fatal(err)
}
defer conn.Close(context.Background())
2. conn.QueryRow — выполнение запроса с одной строкой результата
Что делает: Выполняет SQL-запрос, который возвращает ровно одну строку. Удобно для SELECT с условием по первичному ключу.
Параметры:
ctx context.Context— контекстsql string— SQL-запрос с плейсхолдерами ($1, $2, ...)args ...interface{}— аргументы для плейсхолдеров
Возвращает: pgx.Row (имеет метод Scan для извлечения данных)
Пример:
var name string
var age int
err := conn.QueryRow(context.Background(),
"SELECT name, age FROM users WHERE id = $1", 42).Scan(&name, &age)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Пользователь: %s, возраст: %d\n", name, age)
3. conn.Query — выполнение запроса с множеством строк
Что делает: Выполняет SQL-запрос и возвращает итератор по строкам результата. Используется, когда ожидается несколько строк.
Параметры: Аналогично QueryRow
Возвращает: (pgx.Rows, error)
Пример:
rows, err := conn.Query(context.Background(), "SELECT id, name FROM users WHERE age > $1", 18)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id int
var name string
err := rows.Scan(&id, &name)
if err != nil {
log.Fatal(err)
}
fmt.Printf("ID: %d, Name: %s\n", id, name)
}
4. conn.Exec — выполнение запроса без возврата строк
Что делает: Выполняет SQL-запрос, который не возвращает строки (INSERT, UPDATE, DELETE, CREATE TABLE и т.д.). Возвращает количество затронутых строк.
Параметры: Аналогично Query
Возвращает: (pgconn.CommandTag, error)
Пример:
ct, err := conn.Exec(context.Background(),
"INSERT INTO users (name, age) VALUES ($1, $2)", "Alice", 30)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Затронуто строк: %d\n", ct.RowsAffected())
5. conn.Begin — начало транзакции
Что делает: Начинает новую транзакцию PostgreSQL. Все последующие запросы будут выполняться в рамках этой транзакции до вызова Commit или Rollback.
Параметры: ctx context.Context
Возвращает: (pgx.Tx, error)
Пример:
tx, err := conn.Begin(context.Background())
if err != nil {
log.Fatal(err)
}
defer tx.Rollback(context.Background()) // откат, если не будет Commit
_, err = tx.Exec(context.Background(), "UPDATE accounts SET balance = balance - $1 WHERE id = $2", 100, 1)
if err != nil {
log.Fatal(err)
}
_, err = tx.Exec(context.Background(), "UPDATE accounts SET balance = balance + $1 WHERE id = $2", 100, 2)
if err != nil {
log.Fatal(err)
}
err = tx.Commit(context.Background())
if err != nil {
log.Fatal(err)
}
6. conn.Prepare — подготовка запроса
Что делает: Создаёт подготовленный запрос на стороне сервера. Это повышает производительность при многократном выполнении одного и того же запроса с разными параметрами.
Параметры:
ctx context.Context— контекстname string— имя подготовленного запроса (можно пустую строку)sql string— SQL-запрос
Возвращает: (*pgconn.StatementDescription, error)
Пример:
_, err := conn.Prepare(context.Background(), "getUser", "SELECT name, age FROM users WHERE id = $1")
if err != nil {
log.Fatal(err)
}
// Использование подготовленного запроса
var name string
var age int
err = conn.QueryRow(context.Background(), "getUser", 42).Scan(&name, &age)
if err != nil {
log.Fatal(err)
}
7. pgxpool.New — создание пула соединений
Что делает: Создаёт пул соединений к PostgreSQL. Пул управляет жизненным циклом соединений, автоматически переподключается при обрывах и позволяет эффективно использовать ресурсы в многопоточных приложениях.
Параметры:
ctx context.Context— контекстconnString string— строка подключения
Возвращает: (*pgxpool.Pool, error)
Пример:
pool, err := pgxpool.New(context.Background(), "postgres://user:pass@localhost:5432/mydb")
if err != nil {
log.Fatal(err)
}
defer pool.Close()
// Получение соединения из пула
conn, err := pool.Acquire(context.Background())
if err != nil {
log.Fatal(err)
}
defer conn.Release()
var count int
err = conn.QueryRow(context.Background(), "SELECT COUNT(*) FROM users").Scan(&count)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Всего пользователей: %d\n", count)
8. conn.CopyFrom — массовая вставка данных
Что делает: Выполняет массовую вставку строк с использованием протокола COPY PostgreSQL. Это самый быстрый способ вставить много данных (в десятки раз быстрее обычных INSERT).
Параметры:
ctx context.Context— контекстtableName pgx.Identifier— имя таблицы (можно указать схему)columnNames []string— имена столбцовrowSrc pgx.CopyFromSource— источник данных (обычно pgx.CopyFromRows)
Возвращает: (int64, error) — количество вставленных строк
Пример:
// Подготовка данных для вставки
rows := [][]interface{}{
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35},
}
copyCount, err := conn.CopyFrom(
context.Background(),
pgx.Identifier{"users"},
[]string{"name", "age"},
pgx.CopyFromRows(rows),
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Вставлено строк: %d\n", copyCount)
9. conn.Listen / conn.Unlisten — подписка на уведомления
Что делает: Позволяет подписаться на уведомления PostgreSQL (LISTEN/NOTIFY). Это механизм асинхронного уведомления между клиентами через базу данных.
Параметры:
ctx context.Context— контекстchannel string— имя канала
Возвращает: error
Пример:
// Подписка на канал
err := conn.Listen(context.Background(), "my_channel")
if err != nil {
log.Fatal(err)
}
// Ожидание уведомления (в отдельной горутине)
notification, err := conn.WaitForNotification(context.Background())
if err != nil {
log.Fatal(err)
}
fmt.Printf("Пол