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.作者投稿可能会经我们编辑修改或补充。

关注我们

一个IT知识分享的公众号