为了账号安全,请及时绑定邮箱和手机立即绑定

延迟结构 sqlx.Close() 以堆栈溢出结束

延迟结构 sqlx.Close() 以堆栈溢出结束

Go
慕斯王 2021-12-07 10:08:30
我刚开始学习围棋。当天的课程是将我的数据库处理程序包装在一个结构中以避免使用全局范围变量。到目前为止我认为我理解它并希望像我之前所做的那样推迟 Close() 方法,它以堆栈溢出结束。我找不到解释为什么会发生这种情况,也找不到正确的方法来做到这一点。这是关键代码:package exportertype DB struct {    *sqlx.DB    queriesExecuted int}func Open(dataSourceName string) *DB {    connection := sqlx.MustConnect("mysql", dataSourceName)    db := &DB{connection, 0}    return db}func (db *DB) Close() {    db.Close() // this is where the stack growth happens}func (db *DB) GetArticles() []oxarticle {  ... }package mainfunc main() {    exporter := feedexporter.Open("root:pass@/feedexport")    defer exporter.Close()    articles := exporter.GetArticles()}没有 defer exporter.Close() 一切正常,包括它以:运行时:goroutine 堆栈超过 1000000000 字节的限制致命错误:堆栈溢出不关闭连接感觉很糟糕;) 处理这个问题的正确方法是什么?
查看完整描述

1 回答

?
慕丝7291255

TA贡献1859条经验 获得超6个赞

你在你的Close()方法中触发了无限递归:

func (db *DB) Close() {
    db.Close() // you're currently IN this exact method!
    }

什么你大概意思做的是调用Close()的方法sqlx.DB是已嵌入到您的自定义结构DB结构。我对这个sqlx包不太熟悉,但根据文档,该类型甚至没有Close()方法。这很可能是因为sqlx.DB实际上并不代表单个连接,而是一个透明地创建和关闭连接的连接池:

数据库实例不是连接,而是代表数据库的抽象。这就是为什么创建数据库不会返回错误并且不会恐慌的原因。它在内部维护一个连接池,并在第一次需要连接时尝试连接。

文档深入解释了如何处理此连接池(重点是我的):

默认情况下,池会无限增长,只要池中没有可用连接,就会创建连接。您可以使用 DB.SetMaxOpenConns 来设置池的最大大小。未使用的连接被标记为空闲,然后在不需要时关闭。为避免建立和关闭大量连接,请使用 DB.SetMaxIdleConns 将最大空闲大小设置为适合查询负载的大小。

不小心抓住连接很容易陷入困境。为了防止这种情况:

  1. 确保您 Scan() 每个 Row 对象

  2. 确保您对每个 Rows 对象使用 Close() 或通过 Next() 完全迭代

  3. 确保每个事务通过 Commit() 或 Rollback() 返回其连接


查看完整回答
反对 回复 2021-12-07
  • 1 回答
  • 0 关注
  • 166 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信