你有一个僵局。在最坏的情况下,您有15个goroutine持有15个数据库连接,而所有这15个goroutine都需要一个新的连接才能继续。但是要获得新的连接,就必须前进并释放一个连接:死锁。
链接的维基百科文章详细介绍了防止死锁的方法。例如,代码执行仅在拥有需要(或将要使用)的所有资源时才进入关键部分(锁定资源)。在这种情况下,这意味着您将必须保留2个连接(恰好2个;如果只有1个可用,请保留并等待),如果您有2个,则仅继续进行查询。但是在Go中,您无法提前保留连接。在执行查询时,将根据需要分配它们。
通常应避免这种模式。您不应该编写首先保留(有限)资源(在这种情况下为db连接)的代码,然后再释放它,它需要另一个资源。
一个简单的解决方法是执行第一个查询,将其结果保存(例如保存到Go切片中),完成后再进行后续查询(但也不要忘记先关闭
sql.Rows)。这样,您的代码不需要同时连接2个连接。
并且不要忘记处理错误!为了简洁起见,我省略了它们,但是您不应在代码中使用它们。
它看起来像这样:
go func() { defer wg.Done() rows, _ := db.Query("SELECt * FROM reviews LIMIT 1") var data []int // Use whatever type describes data you query for rows.Next() { var something int rows.Scan(&something) data = append(data, something) } rows.Close() for _, v := range data { // You may use v as a query parameter if needed db.Exec("SELECt * FROM reviews LIMIT 1") }}()请注意,
rows.Close()应将其作为
defer语句执行,以确保它将被执行(即使出现紧急情况)。但是,如果仅使用
deferrows.Close(),则仅在执行后续查询后才执行,因此不会防止死锁。因此,我将其重构为在另一个可以使用的函数(可能是匿名函数)中调用它
defer:
rows, _ := db.Query("SELECt * FROM reviews LIMIT 1") var data []int // Use whatever type describes data you query func() { defer rows.Close() for rows.Next() { var something int rows.Scan(&something) data = append(data, something) } }()还要注意,在第二个
for循环中,由捕获的prepared语句(
sql.Stmt)
DB.Prepare()可能是执行多次相同(参数化)查询的更好选择。
另一个选择是在新的goroutine中启动后续查询,以便在释放当前锁定的连接(或任何其他goroutine锁定的任何其他连接)时发生在其中执行的查询,但是如果没有显式同步,您将无法控制何时他们被处决了。它可能看起来像这样:
go func() { defer wg.Done() rows, _ := db.Query("SELECt * FROM reviews LIMIT 1") defer rows.Close() for rows.Next() { var something int rows.Scan(&something) // Pass something if needed go db.Exec("SELECt * FROM reviews LIMIT 1") }}()为了使您的程序也等待这些goroutine,请使用
WaitGroup已经生效的:
// Pass something if needed wg.Add(1) go func() { defer wg.Done() db.Exec("SELECt * FROM reviews LIMIT 1") }()


