最近我决定走出我的舒适区,学习一下Golang,因为我现在的公司正在使用它。我想在这篇文章中分享我在Golang中创建和更新多个记录到PostgreSQL时遇到的问题,而我当前使用的ORM库(GORM)还不支持批量创建和更新操作。
在本文中,我们将通过不同的方法进行上述操作,并对它们进行基准测试。让我们开始吧。
首先,我需要创建一个新的项目文件夹,其中包含所有的源代码:
mkdir go-bulk-create && cd go-bulk-create
go
go mod init github.com/TrinhTrungDung/go-bulk-create
gorm
go get -u github.com/jinzhu/gorm
test.db
docker-compose.ymldocker-compose
docker-compose up -d
现在我们有了一个新的数据库,我们创建一个新文件来尝试我所说的所有方法:
touch main.go
main.goUser
基准测试
让我们首先创建测试文件:
touch main_test.go
该文件包含以下一些辅助函数:
这就是我们需要的所有工具,让我们为每种方法进行基准测试。
1. 使用GORM批量插入多个记录
main_test.go
go test -benchmem -run=^$ github.com/TrinhTrungDung/go-bulk-create -bench BenchmarkOrmCreate
- 测试结果:
ORM批量创建的测试结果
我们得到了函数在一秒钟内运行的结果(默认情况下),它以大约5.5ms/迭代的速度创建了240个记录,分配了大约9000 B/迭代和105个不同的内存分配/迭代。
对于一个较大的系统来说,效果很差...
2. 通过使用INSERT语句原生地插入多个记录
main_test.goINSERT
go test -benchmem -run=^$ github.com/TrinhTrungDung/go-bulk-create -bench BenchmarkCreate
- 测试结果:
测试基准测试得到错误
什么?你一定会想知道为什么我们在对上述函数进行基准测试时会得到这样的错误。
如果我们在控制台中读取错误,我们会注意到PostgreSQL支持的最大参数数是65535。每次迭代,我们都会追加两个参数(名称、密码),因此最大迭代次数必须小于或等于65535/2 = 32767。
但是,从速度和内存方面来看,你可以看到明显的改善。在一秒钟内,它生成大约150000条记录,并且每次迭代的花费为8354纳秒/迭代,比第一种方法快了超过600倍!
此外,它还将每次迭代的字节数和内存分配次数减少了约十倍。这是一个巨大的改进,但如果我们在实际情况下实现这种方法,数据的一致性就没有保障。
3. 使用批处理大小的INSERT语句插入多个记录
让我们看看这种方法是否可以解救第二种方法。
main_test.goBenchmarkCreate
go test -benchmem -run=^$ github.com/TrinhTrungDung/go-bulk-create -bench BenchmarkBulkCreate
- 测试结果:
好的,它可能比第二种方法表现稍差,但现在它保证了插入新记录时的一致性,这是你必须首先考虑的最重要的事情。
现在,你应该有一些问题,比如:“当我们使批处理大小动态或插入更多的列时会发生什么?”我们来到最后一种方法。
4. 使用动态批处理大小的INSERT语句插入多个记录
benchmarkBulkCreatesizebenchmarkBulkCreate
go test -benchmem -run=^$ github.com/TrinhTrungDung/go-bulk-create -bench BenchmarkBulkCreateSize
- 测试结果:
使用不同批处理大小的基准测试
当我们将批处理大小设置为大于100时,每秒创建的记录数和创建速度没有任何差异。它也适用于每次迭代分配的字节数和内存分配次数。
让我们看看如果我们改变插入参数的数量会发生什么。为此,我们只需将辅助函数更改为以下内容:
- 测试结果:
使用不同的批处理大小和附加参数进行基准测试
当比较不同批处理大小之间的统计数据时,添加更多参数似乎没有任何区别。
结论INSERT
如果你有更好的解决方案,请在评论区与我分享。