首页
Preview

如何在 Golang 中正确地批量创建和更新(第一部分)

最近我决定走出我的舒适区,学习一下Golang,因为我现在的公司正在使用它。我想在这篇文章中分享我在Golang中创建和更新多个记录到PostgreSQL时遇到的问题,而我当前使用的ORM库(GORM)还不支持批量创建和更新操作。

在本文中,我们将通过不同的方法进行上述操作,并对它们进行基准测试。让我们开始吧。

首先,我需要创建一个新的项目文件夹,其中包含所有的源代码:

mkdir go-bulk-create && cd go-bulk-create

然后,我使用go模块初始化一个新的Go项目:

go mod init github.com/TrinhTrungDung/go-bulk-create

我们需要使用gorm依赖项,安装它如下:

go get -u github.com/jinzhu/gorm

我使用Docker Compose在本地机器容器中创建了一个新的隔离的PostgreSQL数据库实例,命名为test.db

  • 在当前目录中创建一个新文件,docker-compose.yml

  • 文件内容应该如下所示:

  • 运行docker-compose文件以创建一个新的数据库:

docker-compose up -d

现在我们有了一个新的数据库,我们创建一个新文件来尝试我所说的所有方法:

touch main.go
  • 我们首先需要成功连接到我们的数据库。main.go的内容如下:

  • 我们创建一个名为User的示例表格并将其迁移到数据库中。

基准测试

让我们首先创建测试文件:

touch main_test.go

该文件包含以下一些辅助函数:

这就是我们需要的所有工具,让我们为每种方法进行基准测试。

1. 使用GORM批量插入多个记录

  • 我们将以下基准测试函数添加到main_test.go文件中,用于ORM批量创建:

  • 运行以下命令以获得速度和内存性能的基准测试:

go test -benchmem -run=^$ github.com/TrinhTrungDung/go-bulk-create -bench BenchmarkOrmCreate
  • 测试结果:

ORM批量创建的测试结果

我们得到了函数在一秒钟内运行的结果(默认情况下),它以大约5.5ms/迭代的速度创建了240个记录,分配了大约9000 B/迭代和105个不同的内存分配/迭代。

对于一个较大的系统来说,效果很差...

2. 通过使用INSERT语句原生地插入多个记录

  • 我们在main_test.go文件中添加另一个使用INSERT语句进行批量创建的函数:

  • 使用以下命令运行测试函数:

go test -benchmem -run=^$ github.com/TrinhTrungDung/go-bulk-create -bench BenchmarkCreate
  • 测试结果:

测试基准测试得到错误

什么?你一定会想知道为什么我们在对上述函数进行基准测试时会得到这样的错误。

如果我们在控制台中读取错误,我们会注意到PostgreSQL支持的最大参数数是65535。每次迭代,我们都会追加两个参数(名称、密码),因此最大迭代次数必须小于或等于65535/2 = 32767。

但是,从速度和内存方面来看,你可以看到明显的改善。在一秒钟内,它生成大约150000条记录,并且每次迭代的花费为8354纳秒/迭代,比第一种方法快了超过600倍!

此外,它还将每次迭代的字节数和内存分配次数减少了约十倍。这是一个巨大的改进,但如果我们在实际情况下实现这种方法,数据的一致性就没有保障。

3. 使用批处理大小的INSERT语句插入多个记录

让我们看看这种方法是否可以解救第二种方法。

  • 首先,在main_test.go文件中添加一个新的批量创建函数,就像BenchmarkCreate函数一样。然而,我们将列表实例划分为多个小块,每个块的批处理大小为500,如下所示:

  • 使用以下命令运行测试函数:

go test -benchmem -run=^$ github.com/TrinhTrungDung/go-bulk-create -bench BenchmarkBulkCreate
  • 测试结果:

好的,它可能比第二种方法表现稍差,但现在它保证了插入新记录时的一致性,这是你必须首先考虑的最重要的事情。

现在,你应该有一些问题,比如:“当我们使批处理大小动态或插入更多的列时会发生什么?”我们来到最后一种方法。

4. 使用动态批处理大小的INSERT语句插入多个记录

  • 首先,我们将benchmarkBulkCreate函数重构为接受另一个名为size的参数:

  • 现在,我们使用各自大小为1、100、500和1000的参数来对benchmarkBulkCreate函数进行基准测试。

  • 通过运行以下命令对不同批处理大小进行测试基准测试:

go test -benchmem -run=^$ github.com/TrinhTrungDung/go-bulk-create -bench BenchmarkBulkCreateSize
  • 测试结果:

使用不同批处理大小的基准测试

当我们将批处理大小设置为大于100时,每秒创建的记录数和创建速度没有任何差异。它也适用于每次迭代分配的字节数和内存分配次数。

让我们看看如果我们改变插入参数的数量会发生什么。为此,我们只需将辅助函数更改为以下内容:

  • 测试结果:

使用不同的批处理大小和附加参数进行基准测试

当比较不同批处理大小之间的统计数据时,添加更多参数似乎没有任何区别。

结论

我个人默认选择批处理大小为500的INSERT语句,因为它适用于所有情况。在下一部分中,我将对批量更新进行基准测试。

如果你有更好的解决方案,请在评论区与我分享。

译自:https://betterprogramming.pub/how-to-bulk-create-and-update-the-right-way-in-golang-part-i-e15a8e5585d1

版权声明:本文内容由TeHub注册用户自发贡献,版权归原作者所有,TeHub社区不拥有其著作权,亦不承担相应法律责任。 如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

点赞(0)
收藏(0)
菜鸟一只
你就是个黄焖鸡,又黄又闷又垃圾。

评论(0)

添加评论