Go SDK

最近更新时间:2026-05-07 11:54:32

我的收藏

操作场景

Go 语言以其高并发性能和编译执行效率,广泛应用于后端微服务和基础设施开发。MongoDB 官方 Go Driver 提供了类型安全的数据库操作 API,与 Go 的并发模型良好配合。本文提供使用 Go 官方驱动连接云数据库 MongoDB 的完整示例。

前提条件

已创建云数据库 MongoDB 实例,且实例状态为运行中
已准备一台与 MongoDB 实例在同一 VPC 内的云服务器 CVM,且已安装 Go 1.18 或以上版本。

安装驱动

说明:
早期社区驱动 mgogopkg.in/mgo.v2)已停止维护,请使用 MongoDB 官方 Go Driver。
go get go.mongodb.org/mongo-driver/mongo
go.mod 示例:
module mongodb-demo

go 1.21

require go.mongodb.org/mongo-driver v1.13.1

注意事项

Context 使用:Go 驱动的所有操作都接受 context.Context 参数,用于控制超时和取消。生产环境中应为每个操作设置合理的超时时间。
密码编码:使用 url.QueryEscape() 对密码进行 URL 编码,避免特殊字符导致连接串解析错误。
结构体映射:通过 bson 标签将 Go 结构体字段映射到 MongoDB 文档字段。字段名默认采用小写,建议显式指定 bson 标签。
连接复用mongo.Client 内置连接池,应在应用中全局复用,通常作为依赖注入到各处理函数。
Cursor 关闭:使用 Find() 返回的 Cursor 在使用完毕后必须调用 Close(),推荐使用 defer 语句。

连接和 CRUD 操作示例

package main

import (
"context"
"fmt"
"log"
"net/url"
"time"

"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)

// User 定义用户文档结构
type User struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
Username string `bson:"username"`
Age int `bson:"age"`
Email string `bson:"email"`
CreatedAt time.Time `bson:"created_at"`
}

func main() {
// 连接 URI(请替换为您的实际连接信息)
// 如密码包含特殊字符,使用 url.QueryEscape() 进行编码
user := "mongouser"
password := url.QueryEscape("thepasswordA1")
host := "10.66.187.127:27017"
uri := fmt.Sprintf("mongodb://%s:%s@%s/admin", user, password, host)

// 副本集连接串(推荐):
// uri := fmt.Sprintf("mongodb://%s:%s@IP1:27017,IP2:27017,IP3:27017/admin?replicaSet=cmgo-xxxxxxxx_0", user, password)

// 配置连接参数
clientOpts := options.Client().
ApplyURI(uri).
SetMaxPoolSize(50). // 连接池上限
SetMinPoolSize(5). // 连接池下限
SetConnectTimeout(10 * time.Second). // 连接超时
SetSocketTimeout(30 * time.Second). // 套接字超时
SetMaxConnIdleTime(60 * time.Second). // 空闲连接超时
SetRetryWrites(true). // 启用可重试写入
SetRetryReads(true) // 启用可重试读取

// 建立连接
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

client, err := mongo.Connect(ctx, clientOpts)
if err != nil {
log.Fatalf("连接失败: %v", err)
}
defer func() {
if err := client.Disconnect(context.Background()); err != nil {
log.Printf("断开连接失败: %v", err)
}
fmt.Println("连接已关闭")
}()

// 验证连接
if err := client.Ping(ctx, readpref.Primary()); err != nil {
log.Fatalf("Ping 失败: %v", err)
}
fmt.Println("连接成功")

// 选择数据库和集合
collection := client.Database("mydb").Collection("users")

// ========== 插入文档 ==========
user1 := User{
Username: "jack",
Age: 31,
Email: "jack@example.com",
CreatedAt: time.Now(),
}
insertResult, err := collection.InsertOne(context.Background(), user1)
if err != nil {
log.Fatalf("插入失败: %v", err)
}
fmt.Printf("插入文档 ID: %v\\n", insertResult.InsertedID)

// 批量插入
users := []interface{}{
User{Username: "alice", Age: 28, Email: "alice@example.com", CreatedAt: time.Now()},
User{Username: "bob", Age: 35, Email: "bob@example.com", CreatedAt: time.Now()},
}
insertManyResult, err := collection.InsertMany(context.Background(), users)
if err != nil {
log.Fatalf("批量插入失败: %v", err)
}
fmt.Printf("批量插入 %d 条文档\\n", len(insertManyResult.InsertedIDs))

// ========== 查询文档 ==========
var foundUser User
err = collection.FindOne(context.Background(), bson.M{"username": "jack"}).Decode(&foundUser)
if err != nil {
log.Fatalf("查询失败: %v", err)
}
fmt.Printf("查询单条: %+v\\n", foundUser)

// 条件查询(年龄大于 30)
cursor, err := collection.Find(context.Background(), bson.M{"age": bson.M{"$gt": 30}})
if err != nil {
log.Fatalf("条件查询失败: %v", err)
}
defer cursor.Close(context.Background())

fmt.Println("年龄大于 30 的用户:")
for cursor.Next(context.Background()) {
var u User
if err := cursor.Decode(&u); err != nil {
log.Printf("解码失败: %v", err)
continue
}
fmt.Printf(" - %s, 年龄: %d\\n", u.Username, u.Age)
}

// ========== 更新文档 ==========
updateResult, err := collection.UpdateOne(
context.Background(),
bson.M{"username": "jack"},
bson.M{"$set": bson.M{"age": 32}},
)
if err != nil {
log.Fatalf("更新失败: %v", err)
}
fmt.Printf("更新 %d 条文档\\n", updateResult.ModifiedCount)

// ========== 删除文档 ==========
deleteResult, err := collection.DeleteMany(
context.Background(),
bson.M{"username": bson.M{"$in": bson.A{"jack", "alice", "bob"}}},
)
if err != nil {
log.Fatalf("删除失败: %v", err)
}
fmt.Printf("删除 %d 条文档\\n", deleteResult.DeletedCount)
}
运行输出示例:
连接成功
插入文档 ID: ObjectID("6789abcdef1234567890abcd")
批量插入 2 条文档
查询单条: {ID:ObjectID("6789abcdef1234567890abcd") Username:jack Age:31 Email:jack@example.com CreatedAt:2026-04-17 15:00:00 +0800 CST}
年龄大于 30 的用户:
- jack, 年龄: 31
- bob, 年龄: 35
更新 1 条文档
删除 3 条文档
连接已关闭

常见问题

连接时提示 "server selection error: server selection timeout"

错误信息示例:
server selection error: context deadline exceeded, current topology: { Type: Unknown, Servers: [{ Addr: 10.66.187.127:27017, Type: Unknown, Last error: connection() error occurred during connection handshake: auth error: sasl conversation error: unable to authenticate using mechanism "SCRAM-SHA-1" }, ] }
请按以下步骤排查:
1. 确认 URI 中指定了认证库 admin
// 正确:URI 路径为 /admin
uri := "mongodb://mongouser:thepasswordA1@10.66.187.127:27017/admin"

// 正确:连接业务库时通过 authSource 指定认证库
uri = "mongodb://mongouser:thepasswordA1@10.66.187.127:27017/mydb?authSource=admin"

// 错误:连接业务库时未指定 authSource,驱动将默认以 mydb 库认证,导致认证失败
uri = "mongodb://mongouser:thepasswordA1@10.66.187.127:27017/mydb"
2. 确认密码中的特殊字符已进行 URL 编码:使用 url.QueryEscape() 对密码进行编码:
import "net/url"

password := url.QueryEscape("pass@123") // 输出: pass%40123
uri := fmt.Sprintf("mongodb://mongouser:%s@10.66.187.127:27017/admin", password)
3. 确认网络连通性:在 CVM 上执行以下命令,验证网络是否可达:
telnet <MongoDB实例IP> 27017
输出 Connected 表示网络可达,请检查认证配置。
输出 Connection timed out 表示网络不通,请确认 CVM 与 MongoDB 实例在同一 VPC 内,并检查安全组规则。

运行时提示 "context deadline exceeded"

错误信息示例:
context deadline exceeded
该错误表示操作超时。根据出现的场景不同,排查方式有所区别:
场景一:连接阶段超时。
检查 SetConnectTimeout 配置是否合理。内网环境下,10秒足够:
clientOpts := options.Client().
ApplyURI(uri).
SetConnectTimeout(10 * time.Second)
场景二:查询或写入阶段超时。
检查传入操作的 context 超时是否过短:
// 错误:超时过短,复杂查询可能无法在 1 秒内完成
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)

// 正确:根据业务复杂度设置合理的超时时间
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

cursor, err := collection.Find(ctx, bson.M{"age": bson.M{"$gt": 30}})
场景三:长时间运行后出现超时。
可能是空闲连接被服务端断开。请确认以下配置:
clientOpts := options.Client().
ApplyURI(uri).
SetMaxConnIdleTime(60 * time.Second). // 空闲连接 60 秒后关闭
SetRetryWrites(true). // 开启可重试写入
SetRetryReads(true) // 开启可重试读取

编译时提示 "cannot find module providing package go.mongodb.org/mongo-driver/..."

错误信息示例:
cannot find module providing package go.mongodb.org/mongo-driver/mongo
请按以下步骤排查:
1. 确认 Go 模块已初始化:在项目目录下确认存在 go.mod 文件。如不存在,请执行:
go mod init <module-name>
2. 安装 MongoDB Go Driver。
go get go.mongodb.org/mongo-driver/mongo
3. 执行 go mod tidy 整理依赖。
go mod tidy
安装完成后,确认 go.mod 文件中包含如下依赖:
require go.mongodb.org/mongo-driver v1.13.1

使用已停维的 mgo 驱动是否可行?

社区驱动 mgogopkg.in/mgo.v2)已于2018年停止维护,存在以下限制:
对比项
mgo(已停维)
官方 Go Driver
维护状态
已停止维护
活跃维护
MongoDB 版本支持
最高3.6
支持4.0 - 8.0
Context 支持
不支持
支持
事务支持
不支持
支持
可重试读写
不支持
支持