---
title: "Знакомство: зачем Go-проекту pgx"
description: "Зачем Go-проекту pgx: сравнение database/sql, lib/pq и нативного драйвера pgx — скорость, нативный протокол PostgreSQL, pgxpool, Batch и COPY."
course: "PostgreSQL в Go: драйвер pgx с нуля до продакшена"
url: "https://golangmaster.ru/courses/pgx/lessons/01-znakomstvo-pgx/"
published: "2026-09-19"
updated: "2026-09-21"
minutes: 16
---

# Знакомство: зачем Go-проекту pgx

*Курс «PostgreSQL в Go: драйвер pgx с нуля до продакшена», урок 1 из 25. ~16 минут. Обновлено 21.09.2026. Источник: https://golangmaster.ru/courses/pgx/lessons/01-znakomstvo-pgx/*

Зачем нужен pgx, когда в стандартной библиотеке Go уже есть database/sql, а на драйвере lib/pq выросли тысячи туториалов? Потому что pgx разговаривает с PostgreSQL по нативному протоколу: без промежуточного слоя абстракции, с собственными типами и встроенным пулом соединений — именно поэтому он быстрее и богаче возможностями. В этом уроке разберём, как pgx устроен, чем отличается от database/sql и lib/pq и когда абстрактный интерфейс всё ещё оправдан.

Сценарий, с которого начинается половина Go-проектов: нужен бэкенд, которому где-то хранить данные. Выбор базы обычно короткий — PostgreSQL: транзакции, надёжность, богатый SQL и хостинг на любой платформе от ноутбука до облака. Но за ним сразу встаёт второй вопрос, который новички часто пропускают: чем читать и писать в эту базу из Go. Варианта три. Первый — интерфейс database/sql из стандартной библиотеки плюс любой совместимый драйвер: так написаны статьи десятилетней давности и половина старого кода. Второй — pgx через тот же database/sql, за счёт адаптера stdlib. Третий — нативный API pgx напрямую, минуя стандартный интерфейс. Разница между тремя путями не вкусовая: скорость запросов может отличаться в разы, а часть возможностей PostgreSQL — быстрая массовая загрузка COPY, уведомления LISTEN/NOTIFY, нативные типы вроде UUID и JSONB — доступна только на нативном пути. От выбора зависит и обработка ошибок: нативный pgx отдаёт pgconn.PgError с кодами SQLSTATE, через общий интерфейс та же информация теряется в обёртках — и это не экспериментальный путь, а тот, которым идут новые сервисы. В этом уроке пройдём по всем трём вариантам и соберём критерии выбора, а весь остальной курс построен на третьем — нативном — подходе.

### Что такое pgx: нативный драйвер PostgreSQL для Go

pgx — библиотека для работы с PostgreSQL из Go, которую пишет Jack Christensen с десятками контрибьюторов; проект ведёт историю с 2013 года, а пятая мажорная версия, v5, вышла в 2022-м и живёт по пути импорта github.com/jackc/pgx/v5. Пятая версия переписана с прицелом на обобщения Go 1.18: CollectRows и RowToStructByName появились именно в ней, четвёртая поддерживается, но новые проекты стартуют с v5. Слово «нативный» здесь не маркетинг: pgx сам реализует протокол, по которому клиент общается с сервером Postgres — так называемый wire protocol. Драйвер не прячет SQL за конструкторами запросов и не переводит типы через общий знаменатель: вы пишете настоящий SQL, а типы Postgres — UUID, массивы, JSONB, временные метки с таймзоной — приходят в Go такими, какие они есть, через собственный пакет pgtype. Из нативности растёт всё остальное: пакетная отправка запросов одним рейсом по сети (Batch), сверхбыстрая загрузка данных через COPY FROM, подписка на события базы LISTEN/NOTIFY и пул соединений pgxpool, встроенный прямо в библиотеку. По количеству проектов, звёзд и ответов на Stack Overflow это самый используемый драйвер PostgreSQL в экосистеме golang: поверх него работают GORM, sqlc и значительная часть самописных бэкендов. Из практичных мелочей v5: драйвер сам кэширует prepared statements — запрос с плейсхолдерами после первого исполнения уезжает на сервер уже скомпилированным, и повторные вызовы экономят и трафик, и работу планировщика; в database/sql этим приходится управляться вручную, через Prepare и Stmt.

### Что даёт нативный pgx одним списком

- Собственная реализация протокола Postgres: без промежуточного слоя database/sql и его сканера типов
- pgtype: нативные типы UUID, JSONB, массивов, NUMERIC и timestamptz без ручных конвертаций
- pgxpool: пул соединений с настройкой лимитов и жизненного цикла коннектов
- Batch и COPY FROM: сотни и миллионы строк без построчных поездок по сети
- LISTEN/NOTIFY: подписка на события базы без опроса таблицы
- CollectRows и RowToStructByName: срез структур одной строкой кода на обобщениях Go 1.18+

### Первый взгляд: pgx в шести осмысленных строках

```go
package main

import (
        "context"
        "fmt"
        "log"

        "github.com/jackc/pgx/v5/pgxpool"
)

func main() {
        ctx := context.Background()

        pool, err := pgxpool.New(ctx, "postgres://app:secret@localhost:5432/app")
        if err != nil {
                log.Fatal(err)
        }
        defer pool.Close()

        var name string
        err = pool.QueryRow(ctx, "SELECT name FROM users WHERE id = $1", 42).Scan(&name)
        if err != nil {
                log.Fatal(err)
        }

        fmt.Println(name)
}
```

Пока не разбирайте по слогам — это телепорт вперёд. pgxpool.New создаёт пул соединений из строки подключения, pool.Ping проверяет, что база отвечает, QueryRow выполняет запрос с плейсхолдером $1, а Scan копирует результат в переменную. Каждой строке — свой урок: установка и подключение во втором, строка DSN в третьем, пул в четвёртом, запросы в пятом.

### database/sql, lib/pq и pgx: кто быстрее и почему

Вопрос «кто быстрее» раскладывается на два слоя. Первый слой — интерфейс: database/sql из стандартной библиотеки описывает, как выглядит работа с любой SQL-базой в Go. Интерфейс удобен, но цена абстракции реальна: каждое значение проходит через собственный сканер типов, а часть протокольных возможностей базы через общий знаменатель просто не торчит. Второй слой — драйвер. lib/pq — чистый Go-драйвер Postgres, который годами был стандартом де-факто, но сейчас объявлен в режиме поддержки: критические баги правят, новых возможностей не добавляют. По бенчмаркам из репозитория pgx нативный pgx обгоняет связку database/sql + lib/pq в разы на простых запросах: выигрыш идёт и от отсутствия промежуточного слоя, и от умения отправлять несколько команд одним рейсом по сети. Механика простая: сканер database/sql прогоняет каждую колонку через отражения и общий знаменатель типов, а нативный pgx кладёт байты протокола прямо в структуры Go и умеет пайплайнинг — отправлять следующий запрос, не дожидаясь ответа на предыдущий. Причём pgx быстрее даже в паре с database/sql через адаптер stdlib — драйверная часть выигрывает сама по себе. Для типового API это означает снижение p95 по базе заметнее, чем от любого тюнинга кода приложения: накладные расходы драйвера уходят почти в ноль, а узким местом честно становится сам SQL.

### Три способа работать с Postgres в Go

| Способ | Под капотом | Что получаете | Чего не хватает |
| --- | --- | --- | --- |
| database/sql + lib/pq | абстрактный интерфейс и драйвер в режиме поддержки | максимальную совместимость со старым кодом | скорости, нативных типов, Batch и COPY |
| database/sql + pgx/stdlib | интерфейс database/sql над нативным pgx | совместимый код на скорости драйвера pgx | нативного API: Batch, COPY, LISTEN/NOTIFY недоступны |
| нативный pgx v5 | собственный протокол Postgres и pgxpool | полную скорость, типы pgtype, Batch, COPY, NOTIFY | готового интерфейса database/sql — его заменяет pgx |

> **GORM и sqlc сидят поверх pgx.** Postgres-драйвер GORM (gorm.io/driver/postgres) под капотом использует pgx через адаптер stdlib, а sqlc умеет генерировать код сразу под pgx/v5. Понимание драйвера объясняет поведение надстроек: почему ORM иногда тормозит на массовых вставках и что именно компилируется из сгенерированного кода. Подробный курс по ORM — на странице [GORM](/courses/gorm/); sqlc вернёмся в девятом уроке этого курса.

> **lib/pq для новых проектов — не вариант.** lib/pq работает и никуда не денется: серверы на нём живут годами. Но авторы проекта прямо объявили режим поддержки — исправления без развития. Старые туториалы продолжают учить lib/pq по инерции; для нового кода экосистема советует pgx, а миграция со старого обычно сводится к смене импорта и адаптеру stdlib.

### Когда database/sql всё ещё нужен

Честный ответ: реже, чем принято думать. Аргумент за абстракцию «мы потом сменим PostgreSQL на MySQL» на практике реализуется в единицах проектов из тысяч: смена СУБД тянет переписывание SQL, миграций и тестов, и последним вопросом там встаёт интерфейс драйвера, а не первым. Реальные причины бывают другие: вы пишете библиотеку, которую будут подключать к чужим сервисам, и не хотите тащить жёсткую зависимость от конкретной базы; у команды горы кода на *sql.DB и ORM, который ожидает именно этот тип; или продукт правда поставляется под разные СУБД. Замечу и обратное: SQL, который обещает абстракция, тоже не переносится бесплатно — у MySQL и Postgres разные диалекты, типы и оконные функции, поэтому «писать на общем знаменателе» означает писать на наименее мощном подмножестве обоих. Плата за абстракцию видна не в бенчмарках, а в исчезающих возможностях: LISTEN/NOTIFY и COPY через database/sql недоступны в принципе, а половина нативных типов превращается в []byte с ручной конвертацией. Моё спорное мнение, которое я проговариваю на каждом архитектурном ревью: в прикладных сервисах абстракция «на всякий случай» — чистый убыток, я пишу на нативном pgx и не выношу интерфейс хранилища ради гипотетической смены базы. Условие, при котором я прав: сервис один, СУБД выбрана осознанно, и менять её никто не собирается. Если условие не выполняется — девятый урок покажет, как жить через pgx/stdlib с минимальными потерями.

Кейс, который помогает почувствовать разницу. Команда сервисов доставки пять лет писала через lib/pq и решилась на переезд, потому что хотела COPY для ночных выгрузок и нативные UUID. Сам переезд занял вечер: импорт сменился, адаптер stdlib прикрыл старый слой, SQL не трогали вовсе. Настоящая работа началась после: NULL-колонки, которые lib/pq пропускала в sql.NullString, потребовали пересмотра моделей; NUMERIC, который раньше приходил строкой, теперь отдаётся нативным типом; а бенчмарк на типовой выборке показал ускорение вставок втрое — за счёт Batch, который раньше приходилось эмулировать конкатенацией стейтментов. Отдельная приятность: часть багов ушла сама — pgtype проверяет диапазоны при сканировании, а не молча кладёт переполненное число, и UUID перестал быть строкой, которую легко перепутать. Мораль: сменить драйвер легко, снять с него сливки — только на нативном API.

> **Типичная ошибка: pgx через database/sql ради скорости.** Команда слышит «pgx быстрее», ставит адаптер stdlib и не видит ни ускорения, ни новых фич — потому что скорость и Batch с COPY живут в нативном API, а через интерфейс database/sql торчит только быстрый драйвер под старым слоем. Если код уже на *sql.DB — адаптер даёт лёгкий выигрыш и совместимость. Если пишете с нуля — берите нативный API сразу.

### Из чего состоит pgx: пакеты внутри модуля

Модуль github.com/jackc/pgx/v5 распадается на несколько пакетов, и по ходу курса вы встретите их все. Пакет pgx — главный: типы Conn, Rows, Tx и весь высокоуровневый API запросов. Пакет pgconn живёт ниже: низкоуровневое соединение, протокол и ошибки — тип pgconn.PgError с кодами SQLSTATE приходит именно оттуда, и с ним вы будете разбирать конфликты уникальности. Внутри pgconn живут и CommandTag — отчёт Exec о затронутых строках, — и низкоуровневые события соединения. Пакет pgtype — типы данных Postgres: UUID, NUMERIC, массивы, JSONB, их сериализация и сканирование. Пул pgxpool вынесен в одноимённый подпакет и построен над универсальным пулом puddle — отдельной библиотекой того же автора. Знать границы полезно не для эрудиции: когда компилятор не находит тип или ошибка приходит не та, что вы ждали, первый вопрос — из какого пакета вы импортировали. В v5 граница смягчена алиасами: pgx.ErrNoRows — то же самое, что pgconn.ErrNoRows, и оба варианта встречаются в документации и чужом коде.

### Меню импорта: каркас типового файла

```go
import (
        "context"

        "github.com/jackc/pgx/v5"         // Conn, Rows, Tx: главный API
        "github.com/jackc/pgx/v5/pgconn"  // протокол, PgError, CommandTag
        "github.com/jackc/pgx/v5/pgtype"  // UUID, JSONB, массивы, NUMERIC
        "github.com/jackc/pgx/v5/pgxpool" // пул соединений
)
```

Меню модуля: в одном файле редко нужны все четыре пакета сразу, но такой импорт-каркас повторяется в половине сервисов на pgx. Пакет pgx присутствует почти всегда, pgxpool — в долгоживущих сервисах, pgtype — там, где нативные типы, pgconn — где разбор SQLSTATE-ошибок. По курсу каждый пакет получит свой урок.

### Проверка: скорость pgx

**Вопрос:** За счёт чего нативный pgx обгоняет связку database/sql + lib/pq?

- Он кэширует результаты SELECT на диске
- ✅ Он говорит по нативному протоколу Postgres и не гоняет данные через слой database/sql
- Он открывает десять соединений на каждый запрос
- Он переписывает SQL под индексы автоматически

Источники выигрыша — отсутствие промежуточного сканера типов database/sql, нативные типы pgtype и протокольные приёмы вроде отправки пачки команд одним рейсом. Кэшей и автореписывания SQL у драйвера нет: база и план запроса остаются вашей заботой.

### Проверка: статус lib/pq

**Вопрос:** Какой сейчас статус lib/pq?

- Проект удалён с GitHub, репозиторий недоступен
- ✅ Режим поддержки: исправления выпускают, новых возможностей не добавляют
- Он остаётся самым быстрым драйвером Postgres для Go
- lib/pq работает только через сторонние плагины

README lib/pq объявляет maintenance mode: драйвер живёт, критические баги правят, но развития нет. Поэтому для новых проектов экосистема рекомендует pgx, а lib/pq встречается в основном в унаследованном коде.

### Что напечатает программа?

```go
var name string

row := pool.QueryRow(ctx,
    "SELECT name, email FROM users WHERE id = $1", 42)

err := row.Scan(&name)
fmt.Println(name, err)
```

**Что выведет программа?**

- Аня <nil>
- ✅ ошибка: число полей результата не совпадает с числом приёмников
- Аня anya@example.com
- пустая строка и <nil>

SELECT возвращает два поля, а Scan получил один приёмник — драйвер не угадывает, куда девать вторую колонку, и возвращает ошибку рассинхрона. Напечатается прежнее значение name и текст ошибки. Верный код — Scan(&name, &email); удобнее сканировать сразу в структуру, этому посвящён седьмой урок.

### Соберите подключение к Postgres

Восстановите первые строки работы с пулом: конструктор, проверка связи и закрытие.

```go
pool, err := pgxpool.___(ctx, "postgres://app:secret@localhost:5432/app")
if err != nil {
        log.Fatal(err)
}

if err = pool.___(ctx); err != nil {
        log.Fatal(err)
}
defer pool.___()
```

Подсказка: Пул создаёт pgxpool.New, связь проверяет Ping, а освобождение ресурсов — метод Close.

Правильные ответы в пропусках: `New`, `Ping`, `Close`

### Соберите каркас программы

Расставьте строки в порядке исполнения: контекст, пул, обработка ошибки, закрытие, пинг.

Правильный порядок строк:

1. `ctx := context.Background()`
2. `dsn := "postgres://app:secret@localhost:5432/app"`
3. `pool, err := pgxpool.New(ctx, dsn)`
4. `if err != nil { log.Fatal(err) }`
5. `defer pool.Close()`
6. `err = pool.Ping(ctx)`
7. `log.Println("пул готов к работе")`

### Карточки: словарь pgx

- **pgx** — Нативный драйвер PostgreSQL для Go (github.com/jackc/pgx/v5): собственная реализация протокола, pgxpool, Batch, COPY, LISTEN/NOTIFY, типы pgtype.
- **Нативный драйвер** — Говорит по wire-протоколу PostgreSQL напрямую, минуя интерфейс database/sql: отсюда скорость и доступ к фичам базы.
- **database/sql** — Абстрактный интерфейс стандартной библиотеки Go для SQL-баз: одинаковый API над любым драйвером, но без специфичных фич.
- **lib/pq** — Драйвер Postgres для database/sql: чистый Go, годами был стандартом, сейчас в режиме поддержки.
- **pgx/stdlib** — Адаптер, который открывает pgx через интерфейс database/sql: совместимость с экосистемой ценой нативного слоя.
- **pgxpool** — Встроенный пул соединений pgx: создаёт, переиспользует и утилизирует коннекты. Тема четвёртого урока.

### Как устроен курс и что запускать

Курс из двадцати пяти уроков ведёт от первого подключения до итогового сервиса заметок со сканированием структур, транзакциями, Batch, COPY и LISTEN/NOTIFY. Формат честный: песочница сайта исполняет только чистый Go без сети, поэтому pgx-код в уроках не запускается в браузере — в песочнице каждого урока откроется тематическая программа на чистом Go, а для практики нужен локальный Go и PostgreSQL, проще всего в Docker одной командой из домашнего задания. Интерактивы — тесты, «что выведет», заполните пропуски и сборка программы — работают прямо на странице и засчитывают XP, так что материал можно проходить откуда угодно, но код всё равно скопируйте в терминал: соединение с настоящей базой не заменит никакая эмуляция. Темп спокойный: урок занимает пятнадцать-двадцать пять минут, и за месяц ровного чтения курс проходится целиком. Дальше по плану: во [втором уроке](/courses/pgx/lessons/02-ustanovka-podklyuchenie/) ставим модуль и подключаемся к Postgres, в [четвёртом](/courses/pgx/lessons/04-pool-soedinenij/) настраиваем пул под прод-нагрузку. Если Go пока на уровне «читал», сначала пройдите [базовый курс](/courses/go-basics/): структуры, срезы и ошибки из него понадобятся уже на пятом уроке.

## Коротко: что запомнить

- pgx — нативный драйвер PostgreSQL для Go: собственный wire-протокол без промежуточного слоя database/sql.
- lib/pq объявлен в режиме поддержки: работает, но не развивается; для новых проектов экосистема рекомендует pgx.
- Нативный pgx быстрее связки database/sql + lib/pq и открывает pgtype, Batch, COPY и LISTEN/NOTIFY.
- database/sql оправдан для библиотек под разные СУБД и кода, уже написанного под *sql.DB, — для этого есть адаптер pgx/stdlib.
- GORM и sqlc строятся поверх pgx: понимание драйвера объясняет поведение надстроек.
- Модуль v5 состоит из пакетов pgx, pgconn, pgtype и pgxpool — знать их границы полезно при разборе ошибок.
- Код курса запускается локально: нужен Go и Postgres в Docker; песочница показывает программу на чистом Go.

## Домашнее задание

Разверните Postgres одной командой: docker run --name pgx-course -e POSTGRES_USER=app -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=app -p 5432:5432 -d postgres:16. Соберите каркас из урока: go mod init pgx-demo, go get github.com/jackc/pgx/v5, вставьте код подключения и добейтесь строки в выводе вместо ошибки соединения. Потом остановите контейнер и запустите программу снова — зафиксируйте текст ошибки соединения: во втором уроке он станет главным героем.

## Вопросы и ответы по уроку

### Что такое pgx простыми словами?

Библиотека, через которую Go-программа общается с PostgreSQL: открывает соединения, отправляет SQL, принимает строки и ошибки. Нативный протокол делает её самым быстрым драйвером Postgres в экосистеме Go, а pgxpool — стандартным способом держать соединения под нагрузкой.

### Нужен ли database/sql, если ставлю pgx?

Нет: нативный API pgx самодостаточен, и в новых сервисах я ставлю его по умолчанию. database/sql нужен, когда вы пишете библиотеку под разные СУБД или подключаетесь к чужому коду на *sql.DB — тогда берите адаптер pgx/stdlib, о котором расскажет девятый урок.

### Можно ли проходить курс без установленного PostgreSQL?

Да: весь код разбирается построчно прямо на странице, интерактивы засчитываются без базы. Для практики руками хватит Docker — команда из домашнего задания поднимает Postgres одной строкой, а запуск программы — go run main.go.
