2015-10-19 169 views
2

遇到并发事务和SQL事务处理问题。我有下面(错误检查和这样为清楚起见移除)的(存根)代码:Golang并发SQL事务处理

dao, _ := sql.Open("postgres", args) 

tx1 := dao.Begin() 
res, _ := tx1.Exec("UPDATE <...>", args...) 
// error check 

tx2 := dao.Begin() 
res, _ = tx2.Exec("UPDATE <same>", args...) 

_ = tx1.Commit() 

_ = tx2.Commit() 

这发生一个单元测试中。这个想法是强制并发失败,因为两个Execs试图更新同一行,以确保给出正确的冲突错误响应。但是,第二个Exec会永久阻止。

据我所知,这种类型的阻塞只有在数据库不在数据库连接时才会发生,但事务都应该在自己的(独占)连接中运行,而且我没有设置一个最大的连接在任何地方(我也尝试将其设置为像100,没有影响)。

这是奇怪的部分。如果我将tx2 Exec()和Commit()分离成一个带有同步通道的独立goroutine,以阻止主线程运行tx1.Commit(),直到tx2运行它的Exec(),同样的事情发生,无限期地阻塞tx2.Exec()。如果我使用time.Sleep()而不是同步通道,则tx2.Exec会阻止,直到睡眠结束并运行tx1.Commit(),然后完成预期的错误(pq: could not serialize access due to concurrent update

我是否错过了某些东西关于golang的SQL包或postgres驱动程序如何处理连接池?为什么第二个事务Exec阻塞直到第一个事务被提交?是不是两人可以同时运行的交易点,以及哪个提交(或开始?)首先获胜?

+0

你对postgres使用什么包?我认为你的问题与我现在删除的答案一致,但是我的语言绝对是错误的。从'Open'返回的'* DB'对象可以用来管理多个连接,你的驱动程序可能有一个限制或者没有被设计为打开新的连接,每个驱动程序都有自己的实现,它们的列表可以是在这里找到; https://github.com/golang/go/wiki/SQLDrivers – evanmcdonnal

+1

https://github.com/lib/pq 我查看了源代码,它似乎没有任何限制我可以找到。一般来说,鼓励司机让golang处理汇集,从我所知道的。 – Kaedys

+1

嗯我不知道,但如果你看看这里的来源,它似乎大部分的责任是在司机身上; https://golang.org/src/database/sql/sql.go?s=5752:6728#L211 – evanmcdonnal

回答

2

经过进一步研究,看起来这实际上并不是Golang或SQL或PQ包的问题。这是内PostgreSQL的固有(和设计)行为:

UPDATE,DELETE,SELECT FOR UPDATE和SELECT FOR SHARE命令的行为SELECT一样在搜索目标行的方面:他们只能找到目标从命令开始时间开始提交的行。但是,此类目标行在发现时可能已被另一个并发事务更新(或删除或锁定)。在这种情况下,将要更新的将等待第一个更新事务提交或回滚(如果它仍在进行中)。

http://www.postgresql.org/docs/9.1/static/transaction-iso.html

所以块是发生在Postgres的,而不是围棋。这可以通过使用psql运行更新(或插入或删除等)同一记录到不同终端的并发事务来确认。第二个更新/插入/删除将阻塞,直到第一个被调用的事务调用COMMIT或ROLLBACK。

+0

作为一个侧面说明,如果你想让它不等待,如果它不能得到一个锁,那么你可以将'NOWAIT'添加到查询中。来源:http:// stackoverflow。com/questions/23702284 /如何手动锁定和解锁一行 –

+0

Ooooo,这也很方便。我必须牢记这一点。谢谢,@MatthewClark! – Kaedys