<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          第三方庫 go-sql-driver 源碼淺析

          共 5824字,需瀏覽 12分鐘

           ·

          2021-07-27 06:50

          一、go-sql-driver使用過程

          1、建立連接

          首先是Open,

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

          db 是一個(gè)*sql.DB類型的指針,在后面的操作中,都要用到db

          open之后,并沒有與數(shù)據(jù)庫建立實(shí)際的連接,與數(shù)據(jù)庫建立實(shí)際的連接是通過Ping方法完成。此外,db應(yīng)該在整個(gè)程序的生命周期中存在,也就是說,程序一啟動(dòng),就通過Open獲得db,直到程序結(jié)束,再Close db,而不是經(jīng)常Open/Close。

          err = db.Ping()

          2、基本用法

          DB的主要方法有:

          Query 執(zhí)行數(shù)據(jù)庫的Query操作,例如一個(gè)Select語句,返回*Rows

          QueryRow 執(zhí)行數(shù)據(jù)庫至多返回1行的Query操作,返回*Row

          PrePare 準(zhǔn)備一個(gè)數(shù)據(jù)庫query操作,返回一個(gè)*Stmt,用于后續(xù)query或執(zhí)行。這個(gè)Stmt可以被多次執(zhí)行,或者并發(fā)執(zhí)行

          Exec 執(zhí)行數(shù)不返回任何rows的據(jù)庫語句,例如delete操作


          Stmt的主要方法:

          ExecQueryQueryRowClose

          用法與DB類似

          Rows的主要方法:

          Cloumns//返回[]string,column namesScanNextClose

          二、源碼分析

          1,初始化

          golang的源碼包里database/sql只定義了連接池和常用接口、數(shù)據(jù)類型

          具體到mysql 的協(xié)議實(shí)現(xiàn)在

          github.com/go-sql-driver/mysql

          因此我們需要在使用的時(shí)候這樣導(dǎo)入依賴

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

          這個(gè)import做了什么呢

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

          我們可以在driver.go里看到下面這個(gè)函數(shù)

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

          向sql的驅(qū)動(dòng)里注入了mysql 的實(shí)現(xiàn)。

          先看下golang 源碼中驅(qū)動(dòng)相關(guān)的代碼,定義在這個(gè)文件中:src/database/sql/sql.go

          var   drivers   = make(map[string]driver.Driver)func Register(name string, driver driver.Driver) {drivers[name] = driver}

          注冊的過程就是將驅(qū)動(dòng)存入這個(gè)map

          它的value是一個(gè)interface,定義在src/database/driver/driver.go這個(gè)文件中

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


          只有一個(gè)方法,Opne,返回是一個(gè)表示連接的interface

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

          連接里面有三個(gè)方法,其中Prepare返回的是一個(gè)interface stmt

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

          在stmt中我們常用到的是兩個(gè)接口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實(shí)現(xiàn)了type Driver interface

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


          而在connection.go,里實(shí)現(xiàn)了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里實(shí)現(xiàn)了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方法里調(diào)用的正是這個(gè)方法c.Connect(context.Background())

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

          上面就完成了整個(gè)初始化的過程,下面我們來看看連接的過程

          2,連接

          連接的時(shí)候我們調(diào)用的是golang源碼中的Open函數(shù)

          func Open(driverName, dataSourceName string) (*DB, error) { //獲取驅(qū)動(dòng) driveri, ok := drivers[driverName]connector, err := driverCtx.OpenConnector(dataSourceName)//連接數(shù)據(jù)庫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()}

          調(diào)用的是 ci, err := db.connector.Connect(ctx)

          這里就對應(yīng)了go-sql-driver里的實(shí)現(xiàn)

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

          3,查詢和執(zhí)行

          //查詢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)}
          //執(zhí)行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里有對應(yīng)的實(shí)現(xiàn)



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號 「polarisxu」,回復(fù) ebook 獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。


          瀏覽 75
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  俺去啦新网 | 操逼视频素材大全网站直接看 | 日韩性高潮视频 | 在线免费观看国产黄片 | 美女黄片网站 |