首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >go-sql-driver 源码分析

go-sql-driver 源码分析

作者头像
golangLeetcode
发布2022-08-02 19:17:51
发布2022-08-02 19:17:51
6000
举报

一、go-sql-driver使用过程

1、建立连接

首先是Open,

db, err := sql.Open(“mysql”, “user:password@/dbname”)

db 是一个*sql.DB类型的指针,在后面的操作中,都要用到db

open之后,并没有与数据库建立实际的连接,与数据库建立实际的连接是通过Ping方法完成。此外,db应该在整个程序的生命周期中存在,也就是说,程序一启动,就通过Open获得db,直到程序结束,再Close db,而不是经常Open/Close。

err = db.Ping()

2、基本用法

DB的主要方法有:

Query 执行数据库的Query操作,例如一个Select语句,返回*Rows

QueryRow 执行数据库至多返回1行的Query操作,返回*Row

PrePare 准备一个数据库query操作,返回一个*Stmt,用于后续query或执行。这个Stmt可以被多次执行,或者并发执行

Exec 执行数不返回任何rows的据库语句,例如delete操作

Stmt的主要方法:

代码语言:javascript
复制
Exec
Query
QueryRow
Close

用法与DB类似

Rows的主要方法:

代码语言:javascript
复制
Cloumns//返回[]string,column names
Scan
Next
Close

二、源码分析

1,初始化

golang的源码包里database/sql只定义了连接池和常用接口、数据类型

具体到mysql 的协议实现在

代码语言:javascript
复制
github.com/go-sql-driver/mysql

因此我们需要在使用的时候这样导入依赖

代码语言:javascript
复制
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)

这个import做了什么呢

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

我们可以在driver.go里看到下面这个函数

代码语言:javascript
复制
func init() {
  sql.Register("mysql", &MySQLDriver{})
}

向sql的驱动里注入了mysql 的实现。

先看下golang 源码中驱动相关的代码,定义在这个文件中:src/database/sql/sql.go

代码语言:javascript
复制
var   drivers   = make(map[string]driver.Driver)
func Register(name string, driver driver.Driver) {
drivers[name] = driver
}

注册的过程就是将驱动存入这个map

它的value是一个interface,定义在src/database/driver/driver.go这个文件中

代码语言:javascript
复制
type Driver interface {
    Open(name string) (Conn, error)
}

只有一个方法,Opne,返回是一个表示连接的interface

代码语言:javascript
复制
type Conn interface {
   Prepare(query string) (Stmt, error)
   Close() error
   Begin() (Tx, error)
}

连接里面有三个方法,其中Prepare返回的是一个interface stmt

代码语言:javascript
复制
type Stmt interface {
   Close() error
   NumInput() int
   Exec(args []Value) (Result, error)
   Query(args []Value) (Rows, error)
}

在stmt中我们常用到的是两个接口Exec和Query,分别返回了Result和Rows

代码语言:javascript
复制
type Result interface {
  LastInsertId() (int64, error)
  RowsAffected() (int64, error)
}
代码语言:javascript
复制
type Rows interface {
    Columns() []string
    Close() error
    Next(dest []Value) error
}

回到go-sql-driver,可以看到driver.go里

MySQLDriver实现了type Driver interface

代码语言:javascript
复制
func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
  cfg, err := ParseDSN(dsn)
 return c.Connect(context.Background())
}

而在connection.go,里实现了type Conn interface

代码语言:javascript
复制
type mysqlConn struct {
}

func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
   err := mc.writeCommandPacketStr(comStmtPrepare, query)
   stmt := &mysqlStmt{
    mc: mc,
   }
   columnCount, err := stmt.readPrepareResultPacket()
}

func (mc *mysqlConn) Begin() (driver.Tx, error) {
}
func (mc *mysqlConn) Close() (err error){
}

在statement.go里实现了type Stmt interface

代码语言:javascript
复制
type mysqlStmt struct {
  mc         *mysqlConn
  id         uint32
  paramCount int
}
func (stmt *mysqlStmt) Close() error 
func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
}
func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
}
func (stmt *mysqlStmt) NumInput() int 

在connector.go里初始化了mysqlConn

代码语言:javascript
复制
type connector struct {
  cfg *Config // immutable private copy.
}

func (c *connector) Connect(ctx context.Context) (driver.Conn, error){
   mc := &mysqlConn{
    maxAllowedPacket: maxPacketSize,
    maxWriteSize:     maxPacketSize - 1,
    closech:          make(chan struct{}),
    cfg:              c.cfg,
    }
   nd := net.Dialer{Timeout: mc.cfg.Timeout}
   mc.netConn, err = dial(dctx, mc.cfg.Addr)
   authResp, err := mc.auth(authData, plugin)
   
}

func (c *connector) Driver() driver.Driver {
  return &MySQLDriver{}
}

而在driver.go的Open方法里调用的正是这个方法c.Connect(context.Background())

代码语言:javascript
复制
func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
  cfg, err := ParseDSN(dsn)
 return c.Connect(context.Background())
}

上面就完成了整个初始化的过程,下面我们来看看连接的过程

2,连接

连接的时候我们调用的是golang源码中的Open函数

代码语言:javascript
复制
func Open(driverName, dataSourceName string) (*DB, error) {
 //获取驱动
 driveri, ok := drivers[driverName]
connector, err := driverCtx.OpenConnector(dataSourceName)
//连接数据库
return OpenDB(dsnConnector{dsn: dataSourceName, driver: driveri}), nil
}

func OpenDB(c driver.Connector) *DB {
   go db.connectionOpener(ctx)
}


// Runs in a separate goroutine, opens new connections when requested.
func (db *DB) connectionOpener(ctx context.Context) {
  for {
    select {
    case <-ctx.Done():
      return
    case <-db.openerCh:
      db.openNewConnection(ctx)
    }
  }
}

func (db *DB) openNewConnection(ctx context.Context) {
  ci, err := db.connector.Connect(ctx)
  db.maybeOpenNewConnections()
}

func (db *DB) maybeOpenNewConnections() {
     db.openerCh <- struct{}{}
}

func (dc *driverConn) finalClose() error {
dc.db.maybeOpenNewConnections()
}

func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn, error) {
    db.maybeOpenNewConnections()
}

func (db *DB) putConn(dc *driverConn, err error, resetSession bool) {
   db.maybeOpenNewConnections()
}

调用的是 ci, err := db.connector.Connect(ctx)

这里就对应了go-sql-driver里的实现

代码语言:javascript
复制
func (c *connector) Connect(ctx context.Context) (driver.Conn, error)

3,查询和执行

代码语言:javascript
复制
//查询
func (db *DB) query(ctx context.Context, query string, args []interface{}, strategy connReuseStrategy) (*Rows, error) {
dc, err := db.conn(ctx, strategy)
return db.queryDC(ctx, nil, dc, dc.releaseConn, query, args)
}

func (db *DB) queryDC(ctx, txctx context.Context, dc *driverConn, releaseConn func(error), query string, args []interface{}) (*Rows, error) {
  nvdargs, err = driverArgsConnLocked(dc.ci, nil, args)
  rowsi, err = ctxDriverQuery(ctx, queryerCtx, queryer, query, nvdargs)
}

database/sql/ctxutil.go

func ctxDriverQuery(ctx context.Context, queryerCtx driver.QueryerContext, queryer driver.Queryer, query string, nvdargs []driver.NamedValue) (driver.Rows, error) {
   return queryerCtx.QueryContext(ctx, query, nvdargs)
   return queryer.Query(query, dargs)
}
代码语言:javascript
复制
//执行
func (db *DB) exec(ctx context.Context, query string, args []interface{}, strategy connReuseStrategy) (Result, error) {
dc, err := db.conn(ctx, strategy)
return db.execDC(ctx, dc, dc.releaseConn, query, args)
}

也分别在go-sql-driver里有对应的实现

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-06-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 golang算法架构leetcode技术php 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档