database/sql
go-sql-driverdatabase/sql

我们查询MySQL的大体代码demo如下:

package main
import (
        "database/sql"
        "log"
        _ "github.com/go-sql-driver/mysql"
)

var DB *sql.DB
var dataBase = "root:[email protected](127.0.0.1:3306)/?loc=Local&parseTime=true"

func main() {
        var err error
        DB, err = sql.Open("mysql", dataBase)
        if err != nil {
                log.Fatalln("open db fail:", err)
        }

        var connection_id int
        err := DB.QueryRow("select CONNECTION_ID()").Scan(&connection_id)
        if err != nil {
                log.Println("query connection id failed:", err)
                return
        }

        log.Println("connection id:", connection_id)
}
go-sql-driver
1.驱动注册

驱动注册代码:

func init() {
    	sql.Register("mysql", &MySQLDriver{})
}
github.com/go-sql-driver/mysql/driver.go

上面的demo中,却看到这个操作。
那是什么时候做的呢?

答案是import 的时候:

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

接着看下Register 函数的实现:

// Register makes a database driver available by the provided name.
// If Register is called twice with the same name or if driver is nil,
// it panics.
func Register(name string, driver driver.Driver) {
	driversMu.Lock()
	defer driversMu.Unlock()
	if driver == nil {
		panic("sql: Register driver is nil")
	}
	if _, dup := drivers[name]; dup {
		panic("sql: Register called twice for driver " + name)
	}
	drivers[name] = driver
}

其中,drivers是一个map,定义为:

var (
	drivers   = make(map[string]driver.Driver)
)
RegisterMySQLDrivermysql
Registerdriver.Driver

接口定义如下:

// Driver is the interface that must be implemented by a database
// driver.
//
// Database drivers may implement DriverContext for access
// to contexts and to parse the name only once for a pool of connections,
// instead of once per connection.
type Driver interface {
	// Open returns a new connection to the database.
	// The name is a string in a driver-specific format.
	//
	// Open may return a cached connection (one previously
	// closed), but doing so is unnecessary; the sql package
	// maintains a pool of idle connections for efficient re-use.
	//
	// The returned connection is only used by one goroutine at a
	// time.
	Open(name string) (Conn, error)
}
OpenMySQLDriver
go-sql-drivergithub.com/go-sql-driver/mysql/driver.go
// Open new Connection.
// See https://github.com/go-sql-driver/mysql#dsn-data-source-name for how
// the DSN string is formatted
func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
    ... ...
	// New mysqlConn
	mc := &mysqlConn{
		maxAllowedPacket: maxPacketSize,
		maxWriteSize:     maxPacketSize - 1,
		closech:          make(chan struct{}),
	}
    ... ...
    return mc, nil
}
2.打开一个database
DB, err = sql.Open("mysql", dataBase)

第一个参数是driver名称,第二个参数是dsn(DataSourceName),指数据库地址。

driversmysqlDB
DB
type DB struct {
	connector driver.Connector
	... ...
}

type dsnConnector struct {
	dsn    string
	driver driver.Driver
}

func (t dsnConnector) Connect(_ context.Context) (driver.Conn, error) {
	return t.driver.Open(t.dsn)
}

func (t dsnConnector) Driver() driver.Driver {
	return t.driver
}
3.查询
var connection_id int
err := DB.QueryRow("select CONNECTION_ID()").Scan(&connection_id)
if err != nil {
        log.Println("query connection id failed:", err)
        return
}

log.Println("connection id:", connection_id)

底层调用关系是这样的:

func (db *DB) QueryRow()
  -->db.QueryRowContext() 
    -->db.QueryContext()
        --> db.conn() --> 最后调用的是MySQLDriver.Open()
        --> db.queryDC() --> 最后调用的是mysqlConn.Query()

具体调用关系如下图所示。
在这里插入图片描述

其他的查询也是类似的调用过程。

MySQLDriver
4.参考