go-sql-driver/mysql是如何被调用的

最近学习到go语言使用go-sql-driver/mysql驱动时,遇到一些问题,这里总结和分析下,看对gopher们有木有用。

首先,网上找的文章,大都是雷同的怎么怎么使用,但比较少涉及到具体的调用流程,我也是初学者,理解不一定透彻,如有问题,读者朋友们请不吝赐教。

网上的文章包括官方都是说先导入包:

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

)

然后就开始各种api如何调用的例子了,但为何import包时,"github.com/go-sql-driver/mysql"加下划线空指示符呢?假如不加下划线,编译时会报imported and not used: "github.com/go-sql-driver/mysql"错误,那么不导入行不行?显然也不行,这样的导入方式的意义何在?这个驱动是如何被调用的呢?可能很多人没有注意或者现在发现这个问题也有点蒙圈了,别急,先让我们温习下:

1.go语言本身没有没有提供连接mysql的驱动,但是定义了标准接口供第三方开发驱动。而go-sql-driver/mysql是比较流行的一个,它实现了go定义的标准接口。所以我们使用标准接口,实际调用的是go-sql-driver/mysql驱动的实现。

2.我们知道,go语言的每个包都有一个或多个init函数,且该函数没有参数和返回值,也不能显示的调用,它用于执行初始化任务,也可用于在执行开始之前验证程序的正确性。

一个包的初始化顺序如下:

  • 包级别的变量首先被初始化
  • 接着init函数被调用。一个包可以有多个init函数(在一个或多个文件中),它们的调用顺序为编译器解析它们的顺序。

如果一个包导入了另一个包,被导入的包先初始化。一个包可能被包含多次,但是它只被初始化一次。

如果你想一个包的init函数被调用但不想在代码的其他地方引用它,那么可以使用下划线空指示符来处理。


好了,讲了这么多,跟我一开始说的问题有几毛钱关系?干货就在这:

先看一段代码吧:

package db

import (
	"fmt"
	"database/sql"
	"time"
	_"github.com/go-sql-driver/mysql"
	"mycom.com/test/config"
)

var iShareDB *sql.DB

func InitDB() {
	var err error
	cfg := &config.GlobalConfig
	
	dataSourceName := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8", cfg.DBUser, cfg.DBPasswd, cfg.DBAddr, cfg.DBName)
	iShareDB, err = sql.Open("mysql", dataSourceName)
	if err != nil {
		panic(err)
	}
	iShareDB.SetConnMaxLifetime(time.Second*time.Duration(cfg.DBConnMaxLifeTime))
	iShareDB.SetMaxOpenConns(cfg.DBMaxOpenConn)	// 设置最大打开的连接数,默认值为0表示不限制
	iShareDB.SetMaxIdleConns(cfg.DBMaxIdelConn)	// 设置闲置的连接数
	err = iShareDB.Ping()
	if err != nil {
		panic(err)
	}
}

这里比较重要的就是sql.Open()的调用,先进入该函数看看:

func Open(driverName, dataSourceName string) (*DB, error) {
	driversMu.RLock()
	driveri, ok := drivers[driverName]    // drivers是个map,通过"mysql"键找到对应的驱动,那么驱动是如何注册进来的呢?
	driversMu.RUnlock()
	if !ok {
		return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName)
	}
	db := &DB{
		driver:       driveri,
		dsn:          dataSourceName,
		openerCh:     make(chan struct{}, connectionRequestQueueSize),
		lastPut:      make(map[*driverConn]string),
		connRequests: make(map[uint64]chan connRequest),
	}
	go db.connectionOpener()
	return db, nil
}

mysql驱动是如何注册进来的?查看go-sql-driver/mysql代码,在driver.go文件最后找到如下代码:

func init() {
	sql.Register("mysql", &MySQLDriver{})
}

为何import _"github.com/go-sql-driver/mysql" 现在是不是明了了。看sql.open()函数还启动了协程

go db.connectionOpener(),这里最终调用该函数:

// Open one new connection
func (db *DB) openNewConnection() {
	// maybeOpenNewConnctions has already executed db.numOpen++ before it sent
	// on db.openerCh. This function must execute db.numOpen-- if the
	// connection fails or is closed before returning.
	ci, err := db.driver.Open(db.dsn)
	db.mu.Lock()
	defer db.mu.Unlock()
        // ...其他省略
}

函数第一行ci, err := db.driver.Open(db.dsn),调用的是go-sql-driver/mysql/driver.go的Open函数了,所以,是go-sql-driver/mysql也实现了该标准接口:

// Driver is the interface that must be implemented by a database
// driver.
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)
}
当然,驱动实现里有很多复杂的实现,我也在学习中。当初初次使用go-sql-driver/mysql的迷茫,现在是不是解决了?
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值