上篇文章关于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),指数据库地址。
该函数从drivers
map中找到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
代码关系如下图所示。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!