sql driver.png

上图中是 driver.go 中定义的所有接口, 他们为不同实现的数据库引擎定义了相似的使用方法,降低了学习成本

Register()

首先使用一个数据库要先找到它的数据库引擎,比如使用 mysql 就需要先引入 github.com/go-sql-driver/mysql

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

这里只需要初始化就可以了,因为我们需要将 这个代码包内的 driver 先注册到 drivers 中, drivers 是一个 map[string][driver] 存储这我们需要的 driver ,通过名称作为索引。

Open()

现在 driver 存储在我们的 drivers 中,如果要使用数据库我们应该把它取出来

Untitled Diagram.png

根据数据库名称将 driver 取出,然后生成 一个 DB,DB 并不一定意味着一定能够连接上数据库了,这得看 driver 的实现了,如果满足 DriverContext 接口的话,可能会在生成 Connector 的时候,检测连接是否能成功建立,如果不满足,只是将 Driver 放入 dsnConnector 中,真正连接的时候还是使用 driver 的 Conn() 方法。

  1. if driverCtx, ok := driveri.(driver.DriverContext); ok {
  2. connector, err := driverCtx.OpenConnector(dataSourceName)
  3. if err != nil {
  4. return nil, err
  5. }
  6. return OpenDB(connector), nil
  7. }
  8. return OpenDB(dsnConnector{dsn: dataSourceName, driver: driveri}), nil

在生成 DB 的时候,还起了两个 gorountine,分别使用两个 channel 进行通信,其中一个是建立连接,另一个是将连接恢复为初始状态,方便下次使用的。

conn()

虽然现在已经有了 DB 但是还没有真正操作数据库,所有的操作都是通过 driverConn 进行的,现在我们来看看是怎么生成 driverConn 的。

生成 driverConn 是使用 DB.conn() 函数,这个函数的签名是这样的,我们可以看出这里传入了一个 connReuseStrategy 这是一个 uint8 的值用来表示是否应该使用缓存的 conn

  1. func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn, error)
  2. type connReuseStrategy uint8
  3. const (
  4. alwaysNewConn connReuseStrategy = iota
  5. cachedOrNewConn
  6. )

首先来看一下新建连接,这个比较简单,直接使用 DB 中准备好的 Connector 获得 driver.Conn 然后生成 driverConn 就可以了。但是如果已有的连接数超出了最大连接,我们就得先进行等待了。等待的时候会先生成一个 channel,然后存入 connRequests 中,如果有连接被释放或者有连接关闭的时候调用 maybeOpenNewConnextions() 都会检查是否这个结构体内存储着需要建立的连接,然后利用生成 DB 的时候建立的 openerCh 生成一个新连接,再通过这个 channel 返回。
Untitled Diagram.png
需要注意的是,这里用 range 从 connRequests 中取出 req ,但是对 map 使用 range 是随机获取的,并不是按照放入的顺序。

  1. for reqKey, req = range db.connRequests {
  2. break
  3. }

同样如果没有等待的 request,那就把空闲的 driverConn 存到 DB 中的 freeConn 中,下一次就可以直接使用旧的 conn 了。

一般情况下,获取连接都是使用的 cachedOrNewConn,只有在像下面这样,获取连接的次数超过了最大尝试次数,才会直接新建一个连接,这个最大尝试次数是一个常量没法更改,数值是 2

  1. for i := 0; i < maxBadConnRetries; i++ {
  2. dc, err = db.conn(ctx, cachedOrNewConn)
  3. if err != driver.ErrBadConn {
  4. break
  5. }
  6. }
  7. if err == driver.ErrBadConn {
  8. dc, err = db.conn(ctx, alwaysNewConn)
  9. }
  10. if err != nil {
  11. return err
  12. }

Untitled Diagram (1).png