打开数据库句柄

database/sql 包通过减少您管理连接的需要来简化数据库访问。与许多数据访问 API 不同,使用 database/sql 时,您无需显式打开连接、执行工作,然后关闭连接。相反,您的代码会打开一个表示连接池的数据库句柄,然后使用该句柄执行数据访问操作,仅在需要释放资源时(例如由检索到的行或预处理语句持有的资源)才调用 Close 方法。

换句话说,是由 sql.DB 表示的数据库句柄负责连接管理,代表您的代码打开和关闭它们。当您的代码使用句柄执行数据库操作时,这些操作可以并发访问数据库。有关更多信息,请参阅管理连接

注意:您也可以保留数据库连接。有关更多信息,请参阅使用专用连接

除了 database/sql 包中可用的 API 外,Go 社区还为所有最常见(和许多不常见)的数据库管理系统(DBMSes)开发了驱动程序。

打开数据库句柄时,您需要遵循以下高级步骤:

  1. 查找驱动程序。

    驱动程序在您的 Go 代码和数据库之间转换请求和响应。有关更多信息,请参阅查找和导入数据库驱动程序

  2. 打开数据库句柄。

    导入驱动程序后,您可以为特定数据库打开一个句柄。有关更多信息,请参阅打开数据库句柄

  3. 确认连接。

    打开数据库句柄后,您的代码可以检查连接是否可用。有关更多信息,请参阅确认连接

您的代码通常不会显式打开或关闭数据库连接——这由数据库句柄完成。但是,您的代码应该释放它在过程中获得的资源,例如包含查询结果的 sql.Rows。有关更多信息,请参阅释放资源

查找和导入数据库驱动程序

您需要一个支持您正在使用的 DBMS 的数据库驱动程序。要查找您的数据库的驱动程序,请参阅SQLDrivers

要使驱动程序可用于您的代码,您需要像导入其他 Go 包一样导入它。这是一个示例:

import "github.com/go-sql-driver/mysql"

请注意,如果您没有直接从驱动程序包调用任何函数——例如当它被 sql 包隐式使用时——您将需要使用空白导入,即在导入路径前加上下划线:

import _ "github.com/go-sql-driver/mysql"

注意:作为最佳实践,避免使用数据库驱动程序自己的 API 进行数据库操作。相反,使用 database/sql 包中的函数。这将有助于保持您的代码与 DBMS 的松散耦合,从而在需要时更容易切换到不同的 DBMS。

打开数据库句柄

一个 sql.DB 数据库句柄提供了单独或在事务中读写数据库的能力。

您可以通过调用 sql.Open(它接受一个连接字符串)或 sql.OpenDB(它接受一个 driver.Connector)来获取数据库句柄。两者都返回一个指向 sql.DB 的指针。

注意:请务必将您的数据库凭据排除在 Go 源代码之外。有关更多信息,请参阅存储数据库凭据

使用连接字符串打开

当您希望使用连接字符串进行连接时,请使用sql.Open 函数。字符串的格式会因您使用的驱动程序而异。

这是一个 MySQL 的示例:

db, err = sql.Open("mysql", "username:password@tcp(127.0.0.1:3306)/jazzrecords")
if err != nil {
    log.Fatal(err)
}

但是,您可能会发现以更结构化的方式捕获连接属性会使您的代码更具可读性。具体细节会因驱动程序而异。

例如,您可以用以下代码替换前面的示例,它使用 MySQL 驱动程序的 Config 来指定属性,并使用其 FormatDSN 方法 来构建连接字符串。

// Specify connection properties.
cfg := mysql.NewConfig()
cfg.User = username
cfg.Passwd = password
cfg.Net = "tcp"
cfg.Addr = "127.0.0.1:3306"
cfg.DBName = "jazzrecords"

// Get a database handle.
db, err = sql.Open("mysql", cfg.FormatDSN())
if err != nil {
    log.Fatal(err)
}

使用 Connector 打开

当您希望利用连接字符串中不可用的驱动程序特定连接功能时,请使用

处理错误

您的代码应该检查尝试创建句柄时(例如使用 sql.Open)是否出现错误。这不是连接错误。相反,如果 sql.Open 无法初始化句柄,您将收到错误。例如,如果它无法解析您指定的 DSN,就可能发生这种情况。

确认连接

当您打开数据库句柄时,sql 包可能不会立即创建新的数据库连接。相反,它可能会在您的代码需要时创建连接。如果您不会立即使用数据库并希望确认可以建立连接,请调用 PingPingContext

以下示例中的代码通过 ping 数据库来确认连接。

db, err = sql.Open("mysql", connString)

// Confirm a successful connection.
if err := db.Ping(); err != nil {
    log.Fatal(err)
}

存储数据库凭据

避免将数据库凭据存储在 Go 源代码中,这可能会将数据库内容暴露给他人。相反,找到一种方法将它们存储在代码外部但可供代码访问的位置。例如,考虑一个存储凭据并提供您的代码可以用来检索凭据以向 DBMS 进行身份验证的 API 的秘密保管应用程序。

一种流行的方法是在程序启动之前将秘密存储在环境中,可能从秘密管理器加载,然后您的 Go 程序可以使用 os.Getenv 读取它们:

username := os.Getenv("DB_USER")
password := os.Getenv("DB_PASS")

这种方法还允许您自己设置环境变量以进行本地测试。

释放资源

尽管您不使用 database/sql 包显式管理或关闭连接,但您的代码应该在不再需要时释放它已获得的资源。这些资源可以包括由表示查询返回数据的 sql.Rows 或表示预处理语句的 sql.Stmt 持有的资源。

通常,您通过推迟调用 Close 函数来关闭资源,以便在包含函数退出之前释放资源。

以下示例中的代码推迟 Close 以释放由 sql.Rows 持有的资源。

rows, err := db.Query("SELECT * FROM album WHERE artist = ?", artist)
if err != nil {
    log.Fatal(err)
}
defer rows.Close()

// Loop through returned rows.