数据库与 context — 一条从连接池到优雅关闭的完整链
数据库与 context — 一条从连接池到优雅关闭的完整链
这一章讲什么: Go 程序怎么跟数据库打交道——从开一个连接池、跑查询, 到用 GORM 省去样板、用事务保原子性;以及 context:一个贯穿全部调用的 「 Deadline + 取消信号 + 行李牌」机制。 它在全书链条里的位置: 第 07 章的 REST API 把这里的 repository 包进 HTTP handler; 第 10 章的部署把这里的优雅关闭接进容器编排。这一章是「后端」二字的地基。 主线例子(贯穿本章): 一个「下单」操作——建连接池 → 开启事务 → 插订单、 扣库存 → 提交或回滚 → 全程 3 秒超时兜底 → 服务重启时优雅收尾。每一步都有具体代码。
1. 顶层全景:数据层的一张结构图
HTTP handler(第 07 章)──ctx──► service ──ctx──► repository ──ctx──► database/sql ──► 驱动 ──► PostgreSQL/MySQL
│ │ │
│ │ ├─ *sql.DB = 连接池(不是单连接!)
│ │ ├─ *sql.Tx = 事务
│ │ └─ QueryContext/ExecContext/BeginTx
│ └─ GORM 可选:模型、CRUD、迁移
└─ context 贯穿:超时 ▪ 取消 ▪ 关联 ID
图说:ctx 从入口一路传到底,任何一层超时,全链同时收工。这是本章的脊柱。2. database/sql:一个接口,三个类型
解决什么问题: 市面上有几十种数据库,Go 不想为每种写一个 API。database/sql
提供一套统一接口;真正的协议细节由各数据库的驱动包实现(驱动在 init 里
自注册一个名字,如 "postgres"、"mysql"——第 03 章那个下划线导入的兑现)1。
三个核心类型2:
| 类型 | 是什么 | 什么时候用 |
|---|---|---|
*sql.DB | 连接池(不是一个连接!),管连接的创建、复用、超时 | 99% 的日常 |
*sql.Conn | 钉死到单条连接的把柄 | 要保证多次操作在同一连接上 |
*sql.Tx | 事务上下文,一串 SQL 原子执行 | 多步写操作 |
sql.DB 可被所有 goroutine 共享——内部已经做好了并发安全3。
配套方法三个动词:Exec(执行不返回行的语句)、Query(返回多行游标——游标:指向结果集中当前位置、可逐行往前推进的把柄)、
QueryRow(只取一行的优化路径)4。
连接驱动选型(原书给了社区现状)5:
| 数据库 | 经典驱动 | 新派驱动 |
|---|---|---|
| PostgreSQL | github.com/lib/pq | github.com/jackc/pgx/v5(更快、功能多) |
| MySQL | github.com/go-sql-driver/mysql(事实标准) | — |
3. 连接:连接串、Ping、池参数
3.1 连接串与「Open 不等于连通」
PostgreSQL(键值式)6:
connStr := "host=localhost port=5432 user=appuser password=secret dbname=appdb sslmode=disable"
db, err := sql.Open("postgres", connStr)MySQL(DSN 式)6:
dsn := "appuser:secret@tcp(localhost:3306)/appdb?parseTime=true&charset=utf8mb4"
db, err := sql.Open("mysql", dsn)一个反直觉的点:sql.Open 不真的连数据库——它只造出池的把柄。
必须 db.Ping():这一步才验证账号、网络、库名全对6。
程序启动时 Ping 失败就 log.Fatal,把「配错了」暴露在最早的时刻。
3.2 池的四个旋钮
db.SetMaxOpenConns(50) // 最多同时开几条连接
db.SetMaxIdleConns(25) // 闲置保留几条(热了不用重建)
db.SetConnMaxLifetime(30*time.Minute) // 一条连接最长活多久(防「陈旧的连接」)
db.SetConnMaxIdleTime(5*time.Minute) // 一条连接闲多久就被回收为什么要管:PostgreSQL 每条连接在服务端是一个独立进程——池开太大, 内存先被数据库那边吃光;MySQL 轻些,但池过小,请求排队,延迟照样飙升7。 调参原则(原书):匹配负载、别超配、定期轮换、看指标、查询短促8。 池的指标(open/in-use/wait 时长)可以暴露给 Prometheus 指导调优9。
3.3 安全清单
- 生产别关 SSL(
sslmode=disable只配本地开发)10; - 密码别写死在代码里——环境变量(操作系统里给程序读的键值对配置,程序启动时现取)或密钥管理器;
- 池上限先测算再放行,别让应用打垮数据库;
- MySQL 记得
parseTime=true,否则时间戳会被当成字节数组而不是time.Time10。
主线例子第 ① 步: 下单服务启动,建池、Ping、设四个旋钮:
db, err := sql.Open("pgx", dsn)
if err != nil { log.Fatalf("db open failed: %v", err) }
db.SetMaxOpenConns(50)
db.SetMaxIdleConns(25)
db.SetConnMaxLifetime(30 * time.Minute)
db.SetConnMaxIdleTime(5 * time.Minute)
if err := db.Ping(); err != nil { log.Fatalf("db ping failed: %v", err) }4. 查询与扫描:低层 API 的正确姿势
单行——注意错误不在 QueryRow 返回,而在 Scan 时才出现:
var name string
var age int
err := db.QueryRowContext(ctx,
"SELECT name, age FROM users WHERE id=$1", userID).Scan(&name, &age)
if err != nil {
if err == sql.ErrNoRows { /* 没这条记录,单独处理 */ }
return err
}多行——游标式逐行读,流式处理大结果集,不全量进内存:
rows, err := db.QueryContext(ctx, "SELECT id, email FROM subscribers WHERE active=true")
if err != nil { return err }
defer rows.Close()
for rows.Next() {
var id int
var email string
if err := rows.Scan(&id, &email); err != nil { return err }
// 处理这一行
}
if err := rows.Err(); err != nil { return err } // 循环正常结束≠没出错原书的五条扫描纪律:循环外一次声明变量(少分配)、扫进原生类型、
用 Context 版查询、defer rows.Close() 紧跟查询、rows.Err() 收尾检查11。
——全是第 03、04 章纪律(早返回、defer、查 error)的复用,没有一条是新知识,
这就是 Go 的风格:机制少,纪律多。
参数怎么传: 永远用 $1、? 这类占位符传参,驱动会做预编译语句
(prepared statement)——SQL 结构和用户数据分开,注入攻击无路可走12。
5. GORM:省样板,不省理解
ORM(Object-Relational Mapping,对象-关系映射)把表映射成结构体, CRUD 不用手写 SQL。GORM 是 Go 生态最流行的一个13。
模型即表,用 struct tag 控制列属性:
type User struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:100;not null"`
Email string `gorm:"uniqueIndex"`
Age int
CreatedAt time.Time
}查询可读性优先:
db.First(&user, 42) // 主键取一条
db.Where("age > ?", 18).Order("created_at desc").Limit(20).Find(&users)
db.Model(&User{}).Select("name", "email").Scan(&results)
db.Preload("Orders").First(&user) // 预加载关联,免手写 JOIN迁移( schema 演变):db.AutoMigrate(&User{}, &Order{}) 按模型建表/补列/
建索引——但它从不删列,防误删数据14。生产上更常用带版本号的迁移工具
(golang-migrate、goose、dbmate),迁移脚本进版本库15。
GORM 与手写 SQL 并存: CRUD、关系、迁移交给 GORM;复杂分析查询、
批量操作、数据库特性优化用 db.Raw("SELECT COUNT(*) ...")16。
原书还点了一类典型性能病:N+1 查询(取一个列表时对每条记录再查一次关联——
本该一次 JOIN 或 Preload 解决),并指出这类模式 AI 审查比人眼更容易抓到17。
判断(我们的,不是书里的): GORM 这类 ORM 在 Go 社区的争议点原书没展开—— 它隐藏 SQL,复杂查询调试时「生成的 SQL 是什么」要
db.Debug()才看得见。 原书的选择(并存策略)是对的:默认手写 SQL 保透明,样板实在多的地方用 GORM。 全项目 GORM 化的团队,迟早会在一个慢查询上花掉省下的全部时间。 如果错,会错在: 如果团队里人人都熟 SQL 且查询不复杂,ORM 的省样板收益 确实可观——「手写优先」是保守默认,不是绝对律令。
5.1 N+1:ORM 时代最经典的性能病
GORM 把取数据变简单之后,最常见的慢查询不是 SQL 写错,而是N+1:
var users []User
db.Find(&users) // 1 条查询取出 N 个用户
for _, u := range users {
db.Where("user_id = ?", u.ID).Find(&u.Orders) // 又 N 条,一人一条
}取 100 个用户 = 101 条查询。数据库来回的网络开销把每条查询的真实成本放大 几十倍,而且 ORM 的写法看着如此自然,review 时极易漏看。两个修法:
- 预加载:
db.Preload("Orders").Find(&users)——GORM 自动改成一条 IN 查询 (或一次 JOIN),把 N+1 压回 2 条; - 手 JOIN:复杂关联直接
db.Raw("SELECT ... JOIN ..."),第 06 章的混合策略。
原书在这一节专门点了 AI 的用处:这类模式(嵌套循环里藏查询)是模式识别器 最擅长抓的形状,贴一段 handler 代码让它「找 N+1」,比人扫一遍更可靠—— 但修法选预加载还是 JOIN,仍要你看数据量和索引后定。
5.2 迁移的两条路:AutoMigrate 与版本化脚本
- AutoMigrate 适合开发期:模型一改,表自动跟上;只增不删的保护(原书明写) 防止误删列,但也意味着它不会帮你清理——重命名一列的结果是新列加旧列并存;
- 版本化迁移(golang-migrate、goose、dbmate)适合生产:每个变更一个带编号的 SQL 文件,进版本库、可回滚、可在 CI 里对干净数据库重放验证。生产纪律只有一条: 表结构怎么变,代码就怎么变,二者同一个 PR、同一次发布。
6. 事务:显式三步,defer 保命
解决什么问题: 下单要插订单行 + 扣库存行 + 记日志行,中途任何一步失败, 数据库不能留下「扣了库存没订单」的烂账。事务保证一串操作要么全成、要么全不算。
Go 的姿势极其显式:Begin → 干活 → 成了 Commit、败了 Rollback,全写在面上18:
func (r *OrderRepo) CreateOrder(ctx context.Context, o *Order) error {
tx, err := r.db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
if err != nil { return err }
if _, err := tx.ExecContext(ctx,
`INSERT INTO orders (user_id, amount) VALUES ($1, $2)`, o.UserID, o.Amount); err != nil {
tx.Rollback()
return fmt.Errorf("insert order: %w", err)
}
if _, err := tx.ExecContext(ctx,
`UPDATE inventory SET stock = stock - 1 WHERE product_id=$1`, o.ProductID); err != nil {
tx.Rollback()
return fmt.Errorf("inventory update: %w", err)
}
return tx.Commit()
}主线例子第 ② 步到此就位。 原书强调三条纪律19:
defer tx.Rollback()的写法——Commit 成功了再调 Rollback 也无害, 让 defer 替你把「任何早返回路径」都兜住,比上面逐条手写 Rollback 更稳;- 事务要短——长事务占着锁拖垮别人;校验和查询尽量放到 Begin 之前;
- 用 Context 版(
BeginTx(ctx, ...)、*Context方法)——超时或取消 时事务自动回滚,连接归还池子。
错误分类(retryable 与否):PostgreSQL 的序列化冲突(这里的「序列化」不是数据格式转换,而是数据库隔离级别的一种:让并发事务像排队一样一个个执行,SQLSTATE 40001)是
瞬时冲突,可以安全重试;用结构化错误包装(第 04 章)把错误码带出来,
重试循环就有据可依20。GORM 的事务助手 db.Transaction(func(tx) error {...})
语义相同:函数返回 nil 提交,返回 error 回滚——但底层语义要懂,不然
嵌套事务、部分更新的怪 bug 找上门时只会盯着 ORM 发呆21。
7. context:全链路的 Deadline、取消与行李牌
解决什么问题: 一个 HTTP 请求进来,穿过中间件、handler、service、 数据库;用户关了浏览器、负载均衡器超时、管理员手动取消——这个「别干了」 的信号怎么一路传到最深处? 靠到处传布尔标志或全局变量,必然漏。
context.Context 就是合同:每个可能阻塞的操作,第一个参数都收 ctx; 上游死了,下游立刻停。 HTTP 请求到达时,服务器自动生成一个携带 连接生命周期的 ctx,之后每一层透传22。
7.1 超时:给操作掐表
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
defer cancel() // 释放资源,别省
rows, err := db.QueryContext(ctx, "SELECT * FROM large_table")两秒一到,驱动中断查询、连接还池,函数带着 context.DeadlineExceeded 返回23。
分布式里的关键动作是「透传剩余预算」:A 调 B 时把 ctx 传过去,
B 自然知道自己只剩多少秒,不会从头再起一个全量超时24。
7.2 取消:最快的答案赢得一切
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
results := make(chan string, 2)
go func() { r, _ := fastQuery(ctx); results <- r; cancel() }() // 快者归来, cancel() 杀掉慢者
go func() { r, _ := slowQuery(ctx); results <- r }()
answer := <-resultscancel() 一调,所有拿着这个 ctx 的 goroutine 在各自的 <-ctx.Done() 处醒来退出
——第 05 章竞态与泄漏问题的标准解25。
7.3 传值:行李牌,不是搬运工
context.WithValue 可以把「关联 ID」「登录用户」这类跟着请求生死的小物件
挂进 ctx,深层函数不用加参数也能读到26:
type key int // 自建类型当 key,防撞名
const requestIDKey key = 0
ctx = context.WithValue(ctx, requestIDKey, reqID)
// 深处:log.Printf("request=%s ...", ctx.Value(requestIDKey))原书转述了官方文档的强烈告诫:ctx 值要小、要省着用——它不是结构体的替代品, 只装「与请求生命周期绑定的、穿透深调用链会弄脏签名」的数据27。
7.4 不带 context 的裸调用是债
原书在架构原则里把话说绝:生产代码里不许出现不带 ctx 的 Query/Exec—— 这是硬性要求,不是风格偏好28。理由:客户端断连时,昂贵的查询继续跑完, 纯属烧数据库的 CPU 和锁。
7.5 三层架构的完整代码骨架(把第 07 章的签名对上)
第 9 节那张签名表,配上最小的实现长这样(书名与库存字段为演示):
// repository:唯一摸 SQL 的层
func (r *BookRepo) FindByID(ctx context.Context, id int64) (*Book, error) {
row := r.db.QueryRowContext(ctx,
"SELECT id, title, stock FROM books WHERE id=$1", id)
var b Book
if err := row.Scan(&b.ID, &b.Title, &b.Stock); err != nil {
return nil, fmt.Errorf("FindByID: %w", err) // 包装,带上层名
}
return &b, nil
}
// service:编排,不碰 SQL
func (s *BookService) GetBook(ctx context.Context, id int64) (*Book, error) {
return s.repo.FindByID(ctx, id) // ctx 原样透传,不重建
}
// handler:翻译 HTTP ⇄ 领域
func (h *BookHandler) GetBook(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
defer cancel()
id, _ := strconv.ParseInt(r.URL.Query().Get("id"), 10, 64)
book, err := h.service.GetBook(ctx, id)
if err != nil {
writeError(w, http.StatusNotFound, "book not found") // 第 07 章的信封
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(book)
}三层各一行纪律:repository 每个错误都包层名;service 不造 ctx、不透传算输; handler 只管协议翻译(路径参数、状态码、JSON)。review 时三层的 diff 应该 一眼能说出是哪层的活——说不出,就是层渗了。
7.6 池参数的失效模式(反着学)
四个旋钮各有一种「调错方向」的典型事故(演示性描述):
| 旋钮 | 调错方向 | 事故形状 |
|---|---|---|
| MaxOpen 太大 | 数据库先垮 | PG 每连接一个进程,几百连接吃光内存,查询全面变慢 |
| MaxOpen 太小 | 应用排队 | 请求在池外干等,延迟飙,ctx 超时齐发 |
| MaxIdle 太小 | 反复握手 | 每个查询都重建 TCP+认证,延迟里一半是握手 |
| Lifetime 不设 | 撞上云负载均衡器 | 空闲连接被 LB 悄悄断掉,应用拿到死连接报错 |
最后一行是云时代特有的:云上数据库前面的负载均衡会回收「太久没说话」的连接, 所以 Lifetime 要小于对方的回收时限——这是原书「定期轮换」原则背后的具体 威胁模型。
8. 优雅关闭:发布不炸毛的五步
云环境天天重启(发版、扩缩容),服务必须有条不紊地死:
- 停收新请求:
srv.Shutdown(ctx)让监听器停止接受; - 播发关闭 context:带 10 秒 Deadline 的根 ctx 传遍各组件;
- 在途请求干完或超时:handler 手里的 ctx 自然到期;
- 关连接池:
db.Close(); - 全程日志可审计29。
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
<-stop // 等到 Ctrl+C 或编排器的停止信号
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
srv.Shutdown(ctx)
db.Close()(SIGTERM 是操作系统/容器编排器发来的「请退出」信号;signal.Notify 让程序
接得住它而不是被硬杀。29)
主线例子第 ③ 步: 下单服务发版。在途的下单事务要么 10 秒内提交完成, 要么随 ctx 到期自动回滚——不会出现「半笔订单」。
9. 收成三层架构:handler → service → repository
原书把全章收成一张签名对照表,这套形状在成百上千个 endpoint 上都稳定30:
| 层 | 签名 | 职责 |
|---|---|---|
| handler | GetUser(w, r *http.Request) | 从 r.Context() 取 ctx,设超时,翻译错误为 HTTP 响应 |
| service | GetUser(ctx, id) | 业务规则、编排多个 repository |
| repository | GetUserByID(ctx, id) | 只跟 SQL 打交道,把错误包 %w 传出 |
ctx 从不丢失、从不重建——这是铁律30。中间件统一造超时(比如全站 2 秒), 每个 endpoint 继承同一政策31。OpenTelemetry 之类的分布式追踪库自动把 span 绑在 ctx 上,API 和数据库边界连成一条 trace32。
全章汇成六条架构原则33:每条 DB 操作必带 ctx;数据库初始化集中一处; 关闭语义全服务共享;错误包装 + 关联 ID + 结构化日志;用短 Deadline 的 ctx 做 压力测试;AI 辅助查反模式(缺 ctx 的查询、永不取消的函数、池配置不当)。
10. 作者的判断与证据
硬事实: sql.DB 是池、Open 不连通要 Ping、占位符防注入、事务 API 形态、 ctx 超时取消语义、四池参数、Shutdown 流程——全部官方文档级事实261929。 经验主张: 池参数的具体数值(50/25/30 分钟)是示例不是定律,原书也明说 要按负载匹配8;「生产禁止裸 Query/Exec」是社区纪律的强表述28。
11. 边界与局限
- NoSQL 只在章首提了一句,MongoDB/Redis 的具体交互整章没有——标题里的 「NoSQL」名不副实。
- pgx 的高级特性(JSONB、数组、二进制协议)只列了清单,没示例34。
- 测试策略一笔带过(Docker 起库 + 事务回滚),没形成可照做的章节。
- AI 部分最薄:只有配置脚手架与 N+1 识别两处,没有数据库专属的 AI 工作流。
12. 可带走的
database/sql= 统一接口;驱动 init 自注册;三类型:DB(池)、Conn、Tx。sql.Open不连通,db.Ping()才验证。- 池四旋钮:MaxOpen / MaxIdle / 存活期 / 空闲期;PostgreSQL 每连接一个服务端进程,别超配。
- 查询三动词:Exec / Query / QueryRow;占位符传参防注入。
- 扫描五纪律:外置声明、原生类型、Context 版、defer Close、Err 收尾。
- GORM:模型 tag、First/Where/Preload、AutoMigrate 只增不删、版本化迁移用 goose 等。
- 事务:BeginTx → defer Rollback → 成了 Commit;要短;ctx 到期自动回滚。
- context 三件套:WithTimeout 掐表、WithCancel 竞速取消、WithValue 挂关联 ID(小而省)。
- 生产硬规矩:裸 Query/Exec 不准出现;ctx 逐层透传不重建。
- 优雅关闭五步走:停收、播 ctx、等在途、关池、留日志。
- 三层签名:handler 造超时、service 编业务、repository 摸 SQL。
- AI 在本章的正当位置:配置脚手架、N+1 与缺 ctx 这类模式识别。
13. 原文地图
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 三类型与统一接口 | 6. Database Interaction and Context Management in Go | text/16-ch06-6-database-interaction-and-context-management-in.txt:81(搜「three primary types」) |
| 驱动选型 | 同上 | text/16-ch06-6-database-interaction-and-context-management-in.txt:134(搜「Choosing the Appropriate Database Driver」) |
| 连接串与 Ping | 同上 | text/16-ch06-6-database-interaction-and-context-management-in.txt:152(搜「Constructing Connection Strings」)text/16-ch06-6-database-interaction-and-context-management-in.txt:171(搜「does not validate」) |
| 池参数与 PG 进程模型 | 同上 | text/16-ch06-6-database-interaction-and-context-management-in.txt:181(搜「Connection Pool」)text/16-ch06-6-database-interaction-and-context-management-in.txt:198(搜「dedicated server process」) |
| 安全清单 | 同上 | text/16-ch06-6-database-interaction-and-context-management-in.txt:219(搜「Security Considerations」) |
| 查询与扫描纪律 | 同上 | text/16-ch06-6-database-interaction-and-context-management-in.txt:299(搜「Querying and Scanning」)text/16-ch06-6-database-interaction-and-context-management-in.txt:349(搜「Best Practices」) |
| GORM 与迁移 | 同上 | text/16-ch06-6-database-interaction-and-context-management-in.txt:362(搜「Using GORM」);AutoMigrate 不删列见 :441 |
| 事务三步纪律 | 同上 | text/16-ch06-6-database-interaction-and-context-management-in.txt:491(搜「Begin() or BeginTx()」)text/16-ch06-6-database-interaction-and-context-management-in.txt:500(CreateOrder 全例) |
| 重试与隔离级别 | 同上 | text/16-ch06-6-database-interaction-and-context-management-in.txt:570(搜「isolation level is configured explicitly」)text/16-ch06-6-database-interaction-and-context-management-in.txt:575(搜「retryable」) |
| context 超时 | 同上 | text/16-ch06-6-database-interaction-and-context-management-in.txt:634(搜「Deadlines and Timeouts」) |
| context 取消竞速 | 同上 | text/16-ch06-6-database-interaction-and-context-management-in.txt:663(搜「Cleaning up Resources」) |
| WithValue 告诫 | 同上 | text/16-ch06-6-database-interaction-and-context-management-in.txt:693(搜「Request-Scoped Metadata」)text/16-ch06-6-database-interaction-and-context-management-in.txt:717(搜「strongly cautions」) |
| 池四参数调优 | 同上 | text/16-ch06-6-database-interaction-and-context-management-in.txt:845(搜「Connection Pool Tuning」) |
| 优雅关闭生命周期 | 同上 | text/16-ch06-6-database-interaction-and-context-management-in.txt:894(搜「Lifecycle of a Graceful Shutdown」)text/16-ch06-6-database-interaction-and-context-management-in.txt:905(完整代码) |
| 三层架构签名 | 同上 | text/16-ch06-6-database-interaction-and-context-management-in.txt:992(搜「Architecture of a Context-Aware Data Layer」) |
| 六条架构原则 | 同上 | text/16-ch06-6-database-interaction-and-context-management-in.txt:949(搜「Key Architectural Principles」) |
Footnotes
-
出处:「6. Database Interaction and Context Management in Go」第 68–80 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:69,搜「database/sql package serves」)。驱动自注册与「driver packages」见 :74。 ↩ -
出处:同章第 81–92 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:81,搜「three primary types」)。 ↩ ↩2 -
出处:同章第 111–112 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:111,搜「designed with concurrency in mind」)。 ↩ -
出处:同章第 93–102 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:94,搜「Exec and Query methods」)。 ↩ -
出处:同章第 134–150 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:135,搜「widely adopted driver」)。 ↩ -
出处:同章第 152–179 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:152,搜「Constructing Connection Strings」;「does not validate the connection immediately」见 :171,Ping 见 :174)。 ↩ ↩2 ↩3 ↩4 -
出处:同章第 181–201 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:181,搜「Understanding sql.DB」;「dedicated server process」见 :198)。 ↩ -
出处:同章第 870–885 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:870,搜「Principles for Effective Pool Tuning」)。 ↩ ↩2 -
出处:同章第 1110–1125 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:1110,搜「Connection Pool Behavior」)。 ↩ -
出处:同章第 219–235 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:219,搜「Security Considerations」)。 ↩ ↩2 -
出处:同章第 299–360 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:34,搜「Querying and Scanning Rows Efficiently with database/sql」;五条纪律见 :349)。 ↩ -
出处:同章第 103–110 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:103,搜「Prepared statements」)。 ↩ -
出处:同章第 362–370 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:366,搜「most widely adopted ORM」)。 ↩ -
出处:同章第 426–442 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:427,搜「built-in migration engine」;「does not remove existing columns」见 :441)。 ↩ -
出处:同章第 443–449 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:443,搜「Versioned Migrations」)。 ↩ -
出处:同章第 451–477 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:451,搜「Combining Manual SQL」;db.Raw例见 :457)。 ↩ -
出处:同章第 473–477 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:474,搜「N+1 queries」)。 ↩ -
出处:同章第 478–498 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:479,搜「Transactions form the backbone」)。 ↩ -
出处:同章第 499–561 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:499,搜「CreateOrder」;「defer tx.Rollback()」见 :505;事务要短见 :551)。 ↩ ↩2 -
出处:同章第 574–579 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:575,搜「retryable」;SQLSTATE 40001 见 :576)。 ↩ -
出处:同章第 580–599 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:580,搜「simplify transaction handling」)。 ↩ -
出处:同章第 609–632 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:609,搜「Mastering Context」;请求到达自动生成 ctx 见 :622)。 ↩ -
出处:同章第 634–661 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:634,搜「Deadlines and Timeouts」;驱动监听取消见 :651)。 ↩ -
出处:同章第 656–661 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:657,搜「propagate the remaining time budget」)。 ↩ -
出处:同章第 663–691 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:663,搜「Cleaning up Resources」;fastQuery/slowQuery 代码见 :672)。 ↩ -
出处:同章第 693–722 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:693,搜「Request-Scoped Metadata」;key 类型例见 :701)。 ↩ -
出处:同章第 716–722 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:717,搜「strongly cautions」)。 ↩ -
出处:同章第 949–967 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:949,搜「Key Architectural Principles」)。「No Query or Exec calls should appear in production code」为原文明写。 ↩ ↩2 -
出处:同章第 887–940 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:887,搜「Graceful Shutdowns」;五步见 :894;完整 main 示例见 :905)。 ↩ ↩2 ↩3 -
出处:同章第 992–1018 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:992,搜「Architecture of a Context-Aware Data Layer」)。「context never gets lost, or is recreated」见 :1017。 ↩ ↩2 -
出处:同章第 1039–1057 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:1039,搜「Deadlines and Timeouts」;中间件统一管理见 :1056)。 ↩ -
出处:同章第 1093–1108 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:1093,搜「Observability and Context」)。 ↩ -
出处:同章第 949–967 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:949,搜「Key Architectural Principles」)。 ↩ -
出处:同章第 237–259 段(
text/16-ch06-6-database-interaction-and-context-management-in.txt:237,搜「Advanced PostgreSQL Considerations」)。 ↩