Введение: Почему Go и базы данных — идеальная пара
Язык программирования Go (Golang) завоевал популярность благодаря высокой производительности, встроенной поддержке конкурентности и простоте развертывания. Однако настоящая сила Go раскрывается при работе с базами данных. Будь то реляционная PostgreSQL или документо-ориентированная MongoDB, Go предоставляет элегантные инструменты для эффективного взаимодействия с данными.
В этой статье мы разберем практические аспекты подключения, выполнения CRUD-операций и оптимизации запросов к PostgreSQL и MongoDB на Go. Вы узнаете, как использовать стандартные пакеты и популярные драйверы, чтобы писать надежный и быстрый код.
1. Работа с PostgreSQL в Go
Для работы с PostgreSQL в Go используется стандартный пакет database/sql в паре с драйвером pgx или lib/pq. pgx рекомендуется из-за лучшей производительности и поддержки расширенных возможностей PostgreSQL.
1.1. Установка и подключение
Установите драйвер через go modules:
go get github.com/jackc/pgx/v5
Пример подключения к базе данных:
package main
import (
"context"
"fmt"
"github.com/jackc/pgx/v5/pgxpool"
"log"
)
func main() {
dsn := "postgres://user:password@localhost:5432/mydb?sslmode=disable"
pool, err := pgxpool.New(context.Background(), dsn)
if err != nil {
log.Fatal("Unable to connect to database:", err)
}
defer pool.Close()
fmt.Println("Connected to PostgreSQL!")
}
Совет: Используйте пул соединений (pgxpool) для production-среды — это уменьшает накладные расходы на установку новых соединений.
1.2. CRUD операции
Рассмотрим базовые операции на примере таблицы users:
// CREATE
func createUser(pool *pgxpool.Pool, name string, email string) (int, error) {
var id int
err := pool.QueryRow(context.Background(),
"INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id",
name, email).Scan(&id)
if err != nil {
return 0, err
}
return id, nil
}
// READ
func getUser(pool *pgxpool.Pool, id int) (string, string, error) {
var name, email string
err := pool.QueryRow(context.Background(),
"SELECT name, email FROM users WHERE id = $1", id).Scan(&name, &email)
if err != nil {
return "", "", err
}
return name, email, nil
}
// UPDATE
func updateUserEmail(pool *pgxpool.Pool, id int, newEmail string) error {
_, err := pool.Exec(context.Background(),
"UPDATE users SET email = $1 WHERE id = $2", newEmail, id)
return err
}
// DELETE
func deleteUser(pool *pgxpool.Pool, id int) error {
_, err := pool.Exec(context.Background(),
"DELETE FROM users WHERE id = $1", id)
return err
}
Обратите внимание на использование параметризованных запросов с $1, $2 — это защищает от SQL-инъекций.
1.3. Транзакции
PostgreSQL поддерживает транзакции, что критично для целостности данных:
func transferMoney(pool *pgxpool.Pool, fromID, toID int, amount float64) error {
tx, err := pool.Begin(context.Background())
if err != nil {
return err
}
defer tx.Rollback(context.Background()) // откат при ошибке
_, err = tx.Exec(context.Background(),
"UPDATE accounts SET balance = balance - $1 WHERE id = $2", amount, fromID)
if err != nil {
return err
}
_, err = tx.Exec(context.Background(),
"UPDATE accounts SET balance = balance + $1 WHERE id = $2", amount, toID)
if err != nil {
return err
}
return tx.Commit(context.Background())
}
2. Работа с MongoDB в Go
MongoDB — документо-ориентированная NoSQL база данных. Официальный драйвер mongo-go-driver обеспечивает полную поддержку всех возможностей MongoDB.
2.1. Установка и подключение
go get go.mongodb.org/mongo-driver/mongo
Пример подключения:
package main
import (
"context"
"fmt"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"log"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))
if err != nil {
log.Fatal(err)
}
defer client.Disconnect(ctx)
fmt.Println("Connected to MongoDB!")
}
2.2. CRUD операции с документами
MongoDB работает с коллекциями (аналог таблиц) и документами (аналог строк в BSON-формате).
type User struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
Name string `bson:"name"`
Email string `bson:"email"`
}
// CREATE
func createUser(client *mongo.Client, user User) (*mongo.InsertOneResult, error) {
collection := client.Database("mydb").Collection("users")
result, err := collection.InsertOne(context.Background(), user)
return result, err
}
// READ
func getUserByID(client *mongo.Client, id primitive.ObjectID) (User, error) {
collection := client.Database("mydb").Collection("users")
var user User
err := collection.FindOne(context.Background(),
bson.M{"_id": id}).Decode(&user)
return user, err
}
// UPDATE
func updateUserEmail(client *mongo.Client, id primitive.ObjectID, newEmail string) error {
collection := client.Database("mydb").Collection("users")
_, err := collection.UpdateOne(context.Background(),
bson.M{"_id": id},
bson.M{"$set": bson.M{"email": newEmail}})
return err
}
// DELETE
func deleteUser(client *mongo.Client, id primitive.ObjectID) error {
collection := client.Database("mydb").Collection("users")
_, err := collection.DeleteOne(context.Background(), bson.M{"_id": id})
return err
}
2.3. Индексы и агрегации
Для производительности обязательно создавайте индексы:
func createEmailIndex(client *mongo.Client) error {
collection := client.Database("mydb").Collection("users")
indexModel := mongo.IndexModel{
Keys: bson.M{"email": 1},
Options