Gorm In 占位符替换大数据量耗时太久解决方法

1.问题

在 gorm 中使用 in 语法如下:

var db *gorm.DB
// 初始化db(省略)
db, err := gorm.Open(mysql.New(mysql.Config{
		DSN: "user:pwd@tcp(host:port)/db?charset=utf8&parseTime=True&loc=Local", 
	}), &gorm.Config{})
	
err = db.Debug().Where("id in(?)", ids).Find(&model).Error

当表中数据量大且 切片 ids 数据量大时,查询会变得很慢

2.原因

gorm 将 ids 替换 sql 占位符 ? 耗时久

3.解决方案

3.1.方案①

可以手动将切片拼接,再替换占位符

err := db.Debug().Where("id in(?)", strings.Join(ids,",")).Find(&model).Error
  • 优点:处理简单,影响范围小
  • 缺点:所有场景都需要修改

3.2.方案②

另外可以在初始化 db 时添加 interpolateparams 参数为 MySQL 开启 interpolateparams 以减少 roundtrip

db, err := gorm.Open(mysql.New(mysql.Config{
		DSN: "user:pwd@tcp(host:port)/db?charset=utf8&interpolateParams=true&parseTime=True&loc=Local", 
	}), &gorm.Config{})

注意:
使用该方案不能同时开启 gorm 预编译PrepareStmt,否则耗时仍然很久(原因未知)
gorm开启预编译模式

  • 优点:只需修改初始化 db 代码
  • 缺点:影响范围广,不能与部分编码一起使用【若一起使用,有sql注入风险】

4.环境复现

  1. 创建表
CREATE TABLE `models_test` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `created_at` int DEFAULT NULL
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=51023 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
  1. 定义model
type ModelsTest struct {
	Id        int
	Name      string
	CreatedAt int64 
}

func (ModelsTest) TableName() string {
	return "models_test"
}
  1. 初始化db
func GetBiReportDb() (*gorm.DB, error) {
	db, err := gorm.Open(mysql.New(mysql.Config{
			DSN: "user:pwd@tcp(host:port)/db?charset=utf8&parseTime=True&loc=Local", 
		}), &gorm.Config{})
	// 处理 error
	return db, nil
}

var db *gorm.DB

func init() {
	db, _ = GetBiReportDb()
}
  1. 批量插入数据
func TestInQuery(t *testing.T) {
	var datas []map[string]interface{}

	for i := 0; i < 50000; i++ {
		data := make(map[string]interface{})
		data["Name"] = fmt.Sprintf("test%v", i)

		datas = append(datas, data)
	}

	// Create from map
	err := db.Table("declaring_models_test").Debug().CreateInBatches(&datas, 500).Error
	// 处理 error
	... 
}
  1. 查询数据
func TestInQuery(t *testing.T) {
	...
	
	var ids []string
	for i := 0; i < 30000; i++ {
		ids = append(ids, strconv.Itoa(i))
	}

	now := time.Now().Unix()
	err := db.Debug().Where("id in(?)", ids).Find(&d).Error
	// 处理 error
	fmt.Println("查询用时:", time.Now().Unix()-now, "s")
}

5.参考资料

  1. gorm缓存预编译语句
  2. 为 MySQL 开启 interpolateparams 以减少 roundtrip
  3. gorm性能优化

相关推荐

  1. 将SQL中的替换成参数

    2024-06-07 02:32:04       17 阅读
  2. js ${} 使用

    2024-06-07 02:32:04       50 阅读
  3. 哈哈哈哈

    2024-06-07 02:32:04       59 阅读
  4. go 中的 fmt

    2024-06-07 02:32:04       57 阅读
  5. c c++编程 fmt:

    2024-06-07 02:32:04       30 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-07 02:32:04       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-07 02:32:04       106 阅读
  3. 在Django里面运行非项目文件

    2024-06-07 02:32:04       87 阅读
  4. Python语言-面向对象

    2024-06-07 02:32:04       96 阅读

热门阅读

  1. MySQL学习——从命令行调用MySQL 程序

    2024-06-07 02:32:04       26 阅读
  2. 【视频转码】ZLMediaKit漏洞报告的问题

    2024-06-07 02:32:04       30 阅读
  3. 2024-05-30 vscode python不能调试

    2024-06-07 02:32:04       33 阅读
  4. 【WPF编程宝典】第9讲:几何图形

    2024-06-07 02:32:04       30 阅读
  5. 设计模式之状态模式

    2024-06-07 02:32:04       30 阅读
  6. 展锐8541E配置USB ACM

    2024-06-07 02:32:04       32 阅读
  7. mongodb 编码格式 Detected BSON

    2024-06-07 02:32:04       28 阅读
  8. element-plus datePicker 如何输出时间戳

    2024-06-07 02:32:04       31 阅读
  9. 0098__gconf、dconf系统配置编辑器

    2024-06-07 02:32:04       30 阅读
  10. Python实现 植物大战僵尸

    2024-06-07 02:32:04       31 阅读