暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

go-sql-driver 源码分析

一、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的主要方法:

    Exec
    Query
    QueryRow
    Close

    用法与DB类似

    Rows的主要方法:

      Cloumns//返回[]string,column names
      Scan
      Next
      Close

      二、源码分析

      1,初始化

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

      具体到mysql 的协议实现在

        github.com/go-sql-driver/mysql

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

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

          这个import做了什么呢

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

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

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

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

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

              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这个文件中

                type Driver interface {
                Open(name string) (Conn, error)
                }


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

                  type Conn interface {
                  Prepare(query string) (Stmt, error)
                  Close() error
                  Begin() (Tx, error)
                  }

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

                    type Stmt interface {
                    Close() error
                    NumInput() int
                    Exec(args []Value) (Result, error)
                    Query(args []Value) (Rows, error)
                    }

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

                      type Result interface {
                      LastInsertId() (int64, error)
                      RowsAffected() (int64, error)
                      }
                        type Rows interface {
                        Columns() []string
                        Close() error
                        Next(dest []Value) error
                        }

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

                        MySQLDriver实现了type Driver interface

                          func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
                          cfg, err := ParseDSN(dsn)
                          return c.Connect(context.Background())
                          }


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

                            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

                              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

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

                                  func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
                                  cfg, err := ParseDSN(dsn)
                                  return c.Connect(context.Background())
                                  }

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

                                  2,连接

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

                                    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里的实现

                                      func (c *connector) Connect(ctx context.Context) (driver.Conn, error)

                                      3,查询和执行

                                        //查询
                                        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)
                                        }
                                          //执行
                                          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里有对应的实现


                                          文章转载自golang算法架构leetcode技术php,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                          评论