vault backup: 2026-04-28 20:56:51

This commit is contained in:
2026-04-28 20:56:51 +08:00
parent 39aaa384ba
commit 0cc894a3cb
14 changed files with 1566 additions and 440 deletions
+33 -49
View File
@@ -43,48 +43,33 @@ for _, u := range users {
> db.Session(&gorm.Session{FullSaveRecords: true}).Create(&hugeSlice)
> ```
### 高速批量插入(Exec)
### 高速批量插入(CreateInBatches)
对于超大批量(数万行),GORM 提供了更高效的执行方式——跳过模型解析,直接执行原始 SQL:
当数据量超过 10K 时,建议主动控制批次大小,避免单次 SQL 过大:
```go
// 方案一:使用 raw sql 手动拼接(推荐用于极大数据量)
values := make([]string, len(users))
args := make([]any, 0, len(users)*3)
for i, u := range users {
values[i] = fmt.Sprintf("(%d, %d, %s)",
u.CreatedAt.Unix(), u.Age, tx.Migrator().ColumnName(u, "name"))
args = append(args, u.Name)
}
db.Exec(fmt.Sprintf("INSERT INTO users (created_at, age, name) VALUES %s",
strings.Join(values, ", "), args...))
// 方案二:用 CreateInBatches(内置分批,更简单)
err := db.CreateInBatches(&users, 500).Error // 每批 500 条
```
> [!important] CreateInBatches vs Create
>
> [!tip] CreateInBatches vs Create
> | 特性 | Create | CreateInBatches |
> |------|--------|-----------------|
> | 参数 | 总数量 | 每批大小(batch size) |
> | 钩子触发 | 每条都触发 | 每条都触发 |
> | 适用场景 | 中小批量(≤10K) | 超大批量(>10K) |
> | 可控性 | GORM 内部决定批次 | 可自定义 batch size |
> | 内部实现 | GORM 自动拆批(默认约 256 条) | 按指定 batch size 拆分 |
> | 适用场景 | 中小批量(≤10K) | 超大批量(>10K),可控分批 |
> | 参数控制 | 不可调 | 可自定义每批大小 |
```go
// 演示差异
db.CreateInBatches(&users, 100) // 每批 100 条
// users 有 350 条 → 分成 4 批:100, 100, 100, 50
// 350 条数据 → 4 批:100, 100, 100, 50
db.CreateInBatches(&users, 100).Error
db.CreateInBatches(&users, 200) // 每批 200 条
// users 有 350 条 → 分成 2 批:200, 150
// 350 条数据 → 2 批:200, 150
db.CreateInBatches(&users, 200).Error
```
> [!question] 思考题
> 为什么 CreateInBatches 的第一个参数是 slice,第二个是 batch size?为什么不叫 ` batchSize` 而用 `total`?
>
> > **答案**:因为设计时参考的是「总共要处理的总数」语义。但在实际使用中,把它理解为「每批大小」更加直观。注意它不是总上限,而是批次阈值。
> 如果 users 只有 50 条记录,传入 batch size = 100,会发生什么?
>
> > **答案**:不会报错,只会执行一批——包含全部 50 条。`CreateInBatches` 的行为是「向上取整分批次」,没有足够数据也不会跳过执行。
## Update — 批量更新
@@ -229,15 +214,14 @@ db.Clauses(clause.OnConflict{
GORM 允许你在特定操作阶段注入自己的逻辑,实现真正的批量定制:
```go
// 在批量创建完成后自动发送通知
db.Callback().Create().After("gorm:create").Register("send_notifications", func(tx *gorm.DB) {
// tx.Statement.Dest 包含被创建的数据
if dest, ok := tx.Statement.Dest.([]User); ok {
for _, user := range dest {
sendWelcomeEmail(user.Email)
}
}
// GORM v2:通过 Session 注册回调
db.Session(&gorm.Session{
DryRun: true, // 仅生成 SQL 不执行,用于调试
}, func(tx *gorm.DB) error {
return tx.Create(&users).Error
})
// 也可以直接在模型上定义钩子(见 [[09-钩子函数]])
```
> [!tip] Callback 阶段排序
@@ -245,29 +229,29 @@ db.Callback().Create().After("gorm:create").Register("send_notifications", func(
> ```
> CREATE: BeforeQuery → BeforeCreate → Create → AfterCreate → AfterQuery
> UPDATE: BeforeQuery → BeforeUpdate → Update → AfterUpdate → AfterQuery
> DELETE: BeforeQuery → BeforeDelete → Delete → AfterDelete → AfterQuery
> DELETE: BeforeQuery → BeforeDelete → Delete → AfterDelete → AfterQuery
> FIND: BeforeQuery → Query → AfterFind → AfterQuery
> ```
> 你可以在任意阶段的前后注册回调。
> 你可以在任意阶段的前后注册回调。对于超大批量,建议在事务层控制而非逐条回调,避免性能瓶颈。
## 批量操作决策图
```mermaid
flowchart TD
Start[需要批量操作数据] --> OpType{操作类型_}
Start[需要批量操作数据] --> OpType{"操作类型"}
OpType --> |插入| InsertQ{"数据量?"}
InsertQ --> |< 10K| SimpleInsert["Create(slice)"]
InsertQ --> |≥ 10K| BatchInsert["CreateInBatches(batchSize)"]
InsertQ --> |< 10K| SimpleInsert["Create\\(slice\\)"]
InsertQ --> |≥ 10K| BatchInsert["CreateInBatches\\(batchSize\\)"]
OpType --> |更新| UpdateQ{"是否需要精准字段控制?"}
UpdateQ --> |不需要| MapUpdate["Updates(map)"]
UpdateQ --> |需要| FieldControl["Select/Omit + Updates"]
UpdateQ --> |不需要| MapUpdate["Updates\\(map\\)"]
UpdateQ --> |需要| FieldControl["Select / Omit + Updates"]
OpType --> |删除| DelCheck["先 Count 确认范围<br/>再 Delete"]
OpType --> |存在则更新| Upsert["Clauses(OnConflict)"]
OpType --> |存在则更新| Upsert["Clauses\\(OnConflict\\)"]
style Start fill:#4FC08D,color:#fff
style BatchInsert fill:#3B82F6,color:#fff
style MapUpdate fill:#F59E0B,color:#000