在并发goroutines中使用Golang和Gorm死锁进行大容量插入



我正在尝试使用Gorm、Golang和MySQL大容量插入许多记录。我的代码如下:

package main
import (
"fmt"
"sync"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type Article struct {
gorm.Model
Code string `gorm:"size:255;uniqueIndex"`
}
func main() {
db, err := gorm.Open(mysql.Open("root@tcp(127.0.0.1:3306)/q_test"), nil)
if err != nil {
panic(err)
}
db.AutoMigrate(&Article{})
// err = db.Exec("TRUNCATE articles").Error
err = db.Exec("DELETE FROM articles").Error
if err != nil {
panic(err)
}
// Build some articles
n := 10000
var articles []Article
for i := 0; i < n; i++ {
article := Article{Code: fmt.Sprintf("code_%d", i)}
articles = append(articles, article)
}
// // Save articles
// err = db.Create(&articles).Error
// if err != nil {
//  panic(err)
// }
// Save articles with goroutines
chunkSize := 100
var wg sync.WaitGroup
wg.Add(n / chunkSize)
for i := 0; i < n; i += chunkSize {
go func(i int) {
defer wg.Done()
chunk := articles[i:(i + chunkSize)]
err := db.Create(&chunk).Error
if err != nil {
panic(err)
}
}(i)
}
wg.Wait()
}

当我运行这个代码时有时(大约三分之一(,我会得到这个错误:

panic: Error 1213: Deadlock found when trying to get lock; try restarting transaction

如果我在没有goroutines(注释行(的情况下运行代码,就不会出现死锁。此外,我注意到,如果删除code字段上的唯一索引,死锁就不会再发生了。如果我用TRUNCATE articles替换DELETE FROM articles语句,那么死锁似乎就不会再发生了。我还用Postgresql运行了相同的代码,它可以工作,没有死锁。

知道为什么只有MySQL上的唯一索引才会出现死锁,以及如何避免死锁吗?

  • DELETE语句使用行锁执行,表中的每一行都被锁定以进行删除
  • TRUNCATE TABLE始终锁定表和页,但不锁定每一行

来源:https://stackoverflow.com/a/20559931/18012302

我认为mysql需要时间来进行DELETE查询。

在查询删除后尝试添加time.Sleep

err = db.Exec("DELETE FROM articles").Error
if err != nil {
panic(err)
}
time.Sleep(time.Second)