zoukankan      html  css  js  c++  java
  • golang go-sql-driver/mysql基本原理

    上篇文章关于golang database_sql 包讲述了 database/sql 的整体设计框架。

    本文简要介绍go-sql-driver/mysql的调用关系,是如何与database/sql关联起来的,包括从驱动注册到具体查询,每个步骤的底层调用。

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

    package main
    import (
            "database/sql"
            "log"
            _ "github.com/go-sql-driver/mysql"
    )
    
    var DB *sql.DB
    var dataBase = "root:Aa123456@tcp(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)
    }
    

    从上面的代码可以看到,
    要连接MySQL,首先是注册MySQL驱动 go-sql-driver/mysql

    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)
    )
    

    也就是说,
    Register只是将MySQLDriver放到一个map中,key为mysql

    Register 的第二个参数类型是driver.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)
    }
    

    接口中定义了一个Open函数,因此MySQLDriver必须实现这个函数。

    go-sql-driver/mysql中已在文件github.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),指数据库地址。

    该函数从driversmap中找到mysql的driver,将其作为参数初始化DB结构体,并返回。

    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.参考

    go-sql-driver /mysql

    golang package sql

  • 相关阅读:
    【BZOJ 5000 OI树】
    九、表锁
    八、ORDER BY优化
    七、索引优化分析
    六、JVM之垃圾回收
    五、JVM之堆内存
    四、JVM之栈与栈帧
    三、JVM之方法区
    二、JVM之体系结构
    一、JVM之类加载器
  • 原文地址:https://www.cnblogs.com/lanyangsh/p/13912249.html
Copyright © 2011-2022 走看看