在 Go 中通过语句或数据库使用 QueryRow

下面的查询执行方法在 DB 中发出完全相同的(3 个)查询,这很好且易于理解 -见下文。但是,我想知道的是,就 Go 而言,两者在内存、cpu、缓存、连接使用等方面是否存在真正的区别?


查询


2020-02-22T12:29:23.858393Z 41 Prepare SELECT id, uuid, name FROM users WHERE id = ?

2020-02-22T12:29:23.859601Z 41 Execute SELECT id, uuid, name FROM users WHERE id = 1

2020-02-22T12:29:23.861607Z 41 Close   stmt

方法


func Select1(query string, args ...interface{}) (*sql.Row, error) {

    stmt, err := DB.Prepare(query)

    if err != nil {

        return nil, err

    }

    defer stmt.Close()


    return stmt.QueryRow(args...), nil

}

func Select2(query string, args ...interface{}) *sql.Row {

    return DB.QueryRow(query, args...)

}


繁星点点滴滴
浏览 394回答 2
2回答

蓝山帝景

除了@mkopriva 在关于客户端的评论中所说的内容之外,使用准备好的语句还有服务器端的影响。重要的是要注意 using*sql.DB使用连接池:DB 是代表零个或多个底层连接池的数据库句柄为什么这很重要?那么并发访问可能会触发多个物理连接。并且当使用准备好的语句时——它们应该被重用——只有在同一个数据库会话(即连接)上使用时才有效。连接池(使用*sql.DB)本质上不能保证这一点。因此,如果有 100 个并发查询 - 理论上可能会生成 100 个并发数据库连接 -不是一个 - 但在服务器端最多有 100 个准备好的语句。您可以通过sql.DB.Conn()获得与数据库的单个连接,其中:在同一个 Conn 上运行的查询将在同一个数据库会话中运行。这意味着任何先前生成的准备好的语句都将获得重用的好处(因为它们使用相同的“会话”)。总之,权衡准备好的语句对客户端和服务器影响的好处,以及您是否真的通过并发使用获得了这些好处。

MMTTMM

我花了更多时间进行测试和分析,所以这是我的发现 - 也证实了@mkopriva 的评论。如果使用SELECT语句,无论是否使用查询占位符,您都应该更喜欢使用DB.Query和/或DB.QueryRow而不是DB.Prepare&STMT.Exec组合。DB.Query当我检查 MySQL 查询日志时,它看起来就像DB.QueryRow 它们用于DB.Exec没有参数占位符的查询一样。这意味着只有一个( Query) 网络往返而不是三个( Prepare,Execute和Close)!那么为什么不直接使用DB.Exec呢?因为它用于不返回结果集的查询。顺便说一句,DB.Exec它直接释放它的连接回池,但DB.Query它的连接保持在池之外,直到rows.Close()被调用。如果您忘记调用它,这可能会导致连接“泄漏”和连接不可用。因此,从技术上讲,DB.Query如果您没有正确地做事,就会有其自身的风险。最后要注意的是,如果您的INSERT,UPDATE和DELETEquery 参数没有 SQL 注入,那么请使用DB.Execelse 来DB.Prepare代替。对于手动准备查询参数,您可以使用fmt.Sprintf()函数。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go