【发布时间】:2020-06-05 11:42:12
【问题描述】:
在以下代码 sn-p 中,我尝试在 2 个不同线程中的 2 个不同事务中查找、删除和创建相同的项目。
在线程 1 中,我创建事务 1,找到该项目并将其删除。
完成后,我允许线程 2 创建事务 2,并尝试查找项目。 Find() 方法在这里阻塞,因为我使用选项 FOR UPDATE。
回到线程 1,重新创建项目并提交事务 1,这允许线程 2 中的 Find() 完成。以下是那里出现的问题:
如果我使用隔离级别“ReadCommitted”,我会收到一个未找到的错误 - 这对我来说毫无意义,因为我认为 ReadCommitted 事务可以看到其他人应用的更新。
如果我使用隔离级别“Serializable”,我会收到错误:pq: could not serialize access due to concurrent update。
为什么我会看到这种行为?我认为在第二次 find 解除阻塞后,它应该为我提供最新的行。
我怎样才能使当一行正在被修改时,任何其他读取都将锁定,并在其他线程中完成后解锁返回最新数据?
db, err := gorm.Open("postgres", "host=localHost port=5432 user=postgres dbname=test-rm password=postgres sslmode=disable")
if err != nil { panic("failed to connect database") }
db.SingularTable(true)
db.DropTableIfExists(&Product{})
db.AutoMigrate(&Product{})
db.Create(&Product{Code: "A", Price: 1000})
// SQL: INSERT INTO "product" ("code","price") VALUES ('A',1000) RETURNING "products"."id"
txOpt := &sql.TxOptions{Isolation: sql.LevelSerializable}
doneTrans1 := make(chan struct{})
go func(){
item1 := &Product{}
tx1 := db.BeginTx(context.Background(), txOpt)
err = tx1.Set("gorm:query_option", "FOR UPDATE").Find(item1, "code = ?", "A").Error
// SQL: SELECT * FROM "product" WHERE (code = 'A') FOR UPDATE
item1.Price = 3000
err = tx1.Delete(item1).Error
// SQL: DELETE FROM "product" WHERE "product"."id" = 1
doneTrans1<-struct{}{}
time.Sleep(time.Second * 3)
err = tx1.Create(item1).Error
// SQL: INSERT INTO "product" ("id","code","price") VALUES (1,'A',3000) RETURNING "product"."id"
tx1.Commit()
}()
// Ensure other trans work started
<-doneTrans1
time.Sleep(time.Second * 2)
item2 := &Product{}
tx2 := db.BeginTx(context.Background(), txOpt)
err = tx2.Set("gorm:query_option", "FOR UPDATE").Find(item2, "code = ?", "A").Error
// SQL: SELECT * FROM "product" WHERE (code = 'A') FOR UPDATE
// ERROR occurs here
item2.Price = 5000
err = tx2.Delete(item2).Error
err = tx2.Create(item2).Error
tx2.Commit()
time.Sleep(time.Second * 5)
【问题讨论】:
-
与您的问题无关,但您没有使用线程。你正在使用 goroutines。 Go 根本不允许你访问线程。
标签: postgresql go transactions go-gorm transaction-isolation