Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Postgresql 维护同一行的并发更新的完整性_Postgresql_Go_Transactions_Go Gorm_Transaction Isolation - Fatal编程技术网

Postgresql 维护同一行的并发更新的完整性

Postgresql 维护同一行的并发更新的完整性,postgresql,go,transactions,go-gorm,transaction-isolation,Postgresql,Go,Transactions,Go Gorm,Transaction Isolation,在下面的代码片段中,我尝试查找、删除和创建同一项,但在两个不同线程中的两个不同事务中 在线程1中,我创建事务1,找到该项并将其删除 完成后,我允许线程2创建事务2,并尝试查找该项。Find()方法在这里阻塞,因为我使用了这个选项 回到线程1,重新创建项目并提交事务1,这允许线程2中的Find()完成。以下是由此产生的问题: 如果我使用隔离级别“ReadCommitted”,我会得到一个找不到的错误-这对我来说毫无意义,因为我认为ReadCommitted事务可以看到其他人应用的更新 如果使用隔离

在下面的代码片段中,我尝试查找、删除和创建同一项,但在两个不同线程中的两个不同事务中

在线程1中,我创建事务1,找到该项并将其删除

完成后,我允许线程2创建事务2,并尝试查找该项。
Find()
方法在这里阻塞,因为我使用了这个选项

回到线程1,重新创建项目并提交事务1,这允许线程2中的
Find()
完成。以下是由此产生的问题:

如果我使用隔离级别“ReadCommitted”,我会得到一个找不到的错误-这对我来说毫无意义,因为我认为ReadCommitted事务可以看到其他人应用的更新

如果使用隔离级别“Serializable”,则会出现错误:
pq:由于并发更新而无法序列化访问

为什么我会看到这种行为?我认为在第二次查找unblocks之后,它应该为我提供最新的行

如何使行在修改过程中,任何其他读取都将锁定,并在其他线程中完成时解锁返回最新数据

db,err:=gorm.Open(“postgres”,“host=localHost-port=5432 user=postgres-dbname=test-rm-password=postgres-sslmode=disable”)
如果出错!=无{panic(“连接数据库失败”)}
db.SingularTable(真)
db.DropTableIfExists(&Product{})
db.AutoMigrate(&Product{})
db.Create(&产品{代码:“A”,价格:1000})
//SQL:在“产品”(“代码”、“价格”)中插入返回“产品”的值('A',1000)。“id”
txOpt:=&sql.TxOptions{Isolation:sql.LevelSerializable}
doneTrans1:=make(chan结构{})
go func(){
项1:=&产品{}
tx1:=db.BeginTx(context.Background(),txOpt)
err=tx1.Set(“gorm:query_选项”,“FOR UPDATE”).Find(item1,“code=?”,“A”).Error
//SQL:从“产品”中选择*进行更新,其中(代码='A')
项目1.价格=3000
err=tx1.Delete(item1).错误
//SQL:从“产品”中删除,其中“产品”。“id”=1

doneTrans1也许我理解错了-我以前没有用过gorm。 但是,从您的查询注释中,两个goroutine中的两个事务都有一个“SELECT..FOR UPDATE”,并且它们并行运行。在尝试“SELECT..FOR UPDATE”相同的行之前,主goroutine没有等待在第二个goroutine中启动的事务提交

根据您的解释,可能是您在第二个goroutine中错误地包含了“FOR UPDATE”


或者您可以在第二个goroutine中使用锁,并在提交后释放它。而主goroutine等待获取锁,然后才执行其查询。

也许我理解错了-我以前从未使用过gorm。 但是,从您的查询注释中,两个goroutine中的两个事务都有一个“SELECT..FOR UPDATE”,并且它们并行运行。在尝试“SELECT..FOR UPDATE”相同的行之前,主goroutine没有等待在第二个goroutine中启动的事务提交

根据您的解释,可能是您在第二个goroutine中错误地包含了“FOR UPDATE”


或者,您可以在第二个goroutine中使用锁,并在提交后释放它。而主goroutine等待获取锁,然后才执行其查询。

要回答这个问题,我认为最好消除goroutine的复杂性(事实上,完全可以),并将重点放在SQL上。以下是SQL语句的运行顺序(我忽略了错误发生后的所有内容,因为这基本上是不相关的,而且执行顺序变得复杂/可变!)

在主程序中

INSERT  INTO "product" ("code","price") VALUES ('A',1000) RETURNING "products"."id"
BEGIN TX1
SELECT * FROM "product"  WHERE (code = 'A') FOR UPDATE
DELETE FROM "product"  WHERE "product"."id" = 1
BEGIN TX2
SELECT * FROM "product"  WHERE (code = 'A') FOR UPDATE -- ERROR occurs here
在GoRoutine中

INSERT  INTO "product" ("code","price") VALUES ('A',1000) RETURNING "products"."id"
BEGIN TX1
SELECT * FROM "product"  WHERE (code = 'A') FOR UPDATE
DELETE FROM "product"  WHERE "product"."id" = 1
BEGIN TX2
SELECT * FROM "product"  WHERE (code = 'A') FOR UPDATE -- ERROR occurs here
在主程序中

INSERT  INTO "product" ("code","price") VALUES ('A',1000) RETURNING "products"."id"
BEGIN TX1
SELECT * FROM "product"  WHERE (code = 'A') FOR UPDATE
DELETE FROM "product"  WHERE "product"."id" = 1
BEGIN TX2
SELECT * FROM "product"  WHERE (code = 'A') FOR UPDATE -- ERROR occurs here
请回答你的问题

问题1

如果我使用隔离级别“ReadCommitted”,我会得到一个找不到的错误- 这对我来说毫无意义,因为我认为 事务可以看到其他人应用的更新

从以下文件:

更新、删除、选择用于更新和选择用于共享命令 在搜索目标行方面的行为与SELECT相同:它们 将仅查找在命令start时提交的目标行 但是,这样的目标行可能已经更新(或 被另一个并发事务删除或锁定) 在这种情况下,可能的更新程序将等待第一次更新 正在更新要提交或回滚的事务(如果仍处于 如果第一个更新程序回滚,则其效果为 否定,第二个更新程序可以继续更新 最初找到行。如果第一个更新程序提交,则第二个更新程序 如果第一个更新程序删除了该行,则将忽略该行,否则将忽略该行 尝试将其操作应用于行的更新版本

因此,TX2中用于更新的
SELECT*FROM“product”,其中(code='A')将等待TX1完成。此时TX1已删除产品A,因此该行将被忽略,不会返回任何结果。现在我知道TX1也会重新创建产品A,但请记住“SELECT查询(没有FOR UPDATE/SHARE子句)只查看在查询开始之前提交的数据;”,并且由于select在TX1重新创建记录之前开始,因此不会看到该记录

问题2

如果我使用隔离级别“Serializable”,我会得到错误:pq:can 由于并发更新,无法序列化访问

从文档(Serializable是一个更高的级别,因此这些规则以及一些更严格的规则适用):

更新、删除、选择用于更新和选择用于共享命令 在搜索目标行方面的行为与SELECT相同:它们 将仅查找在事务结束时提交的目标行 开始时间。但是,这样的目标行可能已经更新 (o)