vault backup: 2026-04-28 20:56:51
This commit is contained in:
+33
-49
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user