go之如果成千上万的Goroutine尝试访问数据库,如何避免sql连接关闭
linjiqin
阅读:205
2025-06-02 22:19:02
评论:0
我的章节表大约有2000000行,我想针对某些特定条件更新每一行:
func main(){
rows, err := db.Query("SELECT id FROM chapters where title = 'custom_type'")
if err != nil {
panic(err)
}
for rows.Next() {
var id int
_ = rows.Scan(&id)
fmt.Println(id)
go updateRowForSomeReason(id)
}
}
func updateRowForSomeReason(id int) {
row, err := db.Query(fmt.Sprintf("SELECT id FROM chapters where parent_id = %v", id))
if err != nil {
panic(err) <----- // here is the panic occurs
}
for rows.Next() {
// ignore update code for simplify
}
}
在
updateRowForSomeReason内部,我为每一行执行update语句。
它将运行几秒钟,之后将显示错误:
323005
323057
323125
323244
323282
323342
323459
323498
323556
323618
323693
325343
325424
325468
325624
325816
326001
326045
326082
326226
326297
panic: sql: database is closed
请您参考如下方法:
这样看来,这似乎并不是一个Go问题,而是一个有关如何在代码中优化SQL结构的问题。您正在通过对2,000,000行执行查询来获取结果集:
rows, err := db.Query("SELECT id FROM chapters where title = 'custom_type'")
然后针对此结果集中的每一行执行另一个查询:
row, err := db.Query(fmt.Sprintf("SELECT id FROM chapters where parent_id = %v", id))
然后针对每个代码执行更多代码,显然是一个接一个地执行:
for rows.Next() {
// ignore update code for simplify
}
这实际上是语句的两个嵌套级别,这是将所有这些结果加载到程序内存中然后执行独立的UPDATE语句的效率很低的方式:
SELECT
+---->SELECT
+---->UPDATE
相反,您可以在数据库本身中完成所有工作,这将大大提高效率。您没有显示
UPDATE语句是什么,但这是关键部分。假设您要设置
publish标志。您可以执行以下操作:
UPDATE chapters
SET publish=true
WHERE parent_id in
(SELECT id FROM chapters
WHERE title='custom_type')
RETURNING id;
通过使用嵌套查询,您可以将三个单独的查询全部组合为一个查询。数据库具有优化操作和构建最有效的查询计划所需的所有信息,并且您仅执行 单个
db.Query操作。
RETURNING子句允许您检索最终在操作中更新的ID列表。因此代码很简单:
func main(){
rows, err := db.Query("UPDATE chapters SET publish=true WHERE parent_id in" +
"(SELECT id FROM chapters WHERE title='custom_type')" +
"RETURNING id;")
if err != nil {
panic(err)
}
for rows.Next() {
var id int
_ = rows.Scan(&id)
fmt.Println(id)
}
}
声明
1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。



