go database/sql 源码分析(四)sql.Stmt数据结构
来源:互联网 发布:java中aes加密算法 编辑:程序博客网 时间:2024/05/24 06:57
#sql.Stmt是sql包暴露给程序调用者的可见实体,一般通过db.Open函数获得DB实例后的下一步就是调用func (db *DB) Prepare 方法的的Stmt
#其内部通过 css []connStmt 来绑定相关的连接和驱动层driver.Stmt
#其内部不是引用driverConn,而是引用一个css []connStmt
#sql包中有两个方式能够创建Stmt实例,一个是DB Prepare() 一个是Tx的Prepare(),二者是有区别
#Tx创建的Stmt通过Tx关联的driverConn绑定到固定的网络连接上
#DB创建的Stmt时初始化过程
1.会从连接池拿一个空闲连接,然后创建connStmt实例加到Stmt的css切片里
2.创建过程是调用DB的conn获取一个可用的driverConn实例,然后调用driverConn 的driver.Conn的Prepare()创建driver.Stmt实例,将该实例加到driverConn 的openStmt map中,标记一下。
#拿到 DB 创建的Stmt实例后,下次使用时就需要一个获取连接,重新绑定driver.Conn和一个driver.Stmt的过程。
#Stmt的method Exec,Query 内部都会调用func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.Stmt, err error)函数来拿到
#重新绑定driver.Conn和一个driver.Stmt实例,这个获取的过程异常曲折:
1.判断Stmt的状态是否关闭
2.判断Stmt是否是由Tx创建的,如果是,直接从Tx实例中取得driverConn和driver.Stmt返回
3.注意css中缓存的连接有可能因为各种原因关闭了,需要调用removeClosedStmtLocked()做一次清理
4.调用Stmt关联的DB实例s.db.conn(cachedOrNewConn) 获取一个连接*driverConn
5.判断Stmt css是否已经缓存里该连接,如果已经缓存则说明之前在css中已经缓存了driverConn实例和driver.Stmt,则可以直接拿来使用
6.如果Stmt css没有缓存该连接,说明该Stmt的sql语句之前没有绑定到到该连接上,需要重新绑定:通过driverConn实例和sql语句创建driver.Stmt实例,然后初始化connStmt实例,加入css中,并将
driverConn实例和driver.Stmt返还
#第一个连接创立过程
#其内部通过 css []connStmt 来绑定相关的连接和驱动层driver.Stmt
#其内部不是引用driverConn,而是引用一个css []connStmt
#sql包中有两个方式能够创建Stmt实例,一个是DB Prepare() 一个是Tx的Prepare(),二者是有区别
#Tx创建的Stmt通过Tx关联的driverConn绑定到固定的网络连接上
#DB创建的Stmt时初始化过程
1.会从连接池拿一个空闲连接,然后创建connStmt实例加到Stmt的css切片里
2.创建过程是调用DB的conn获取一个可用的driverConn实例,然后调用driverConn 的driver.Conn的Prepare()创建driver.Stmt实例,将该实例加到driverConn 的openStmt map中,标记一下。
3.将获取的driverConn实例和driver.Stmt实例初始化connStmt,然后加入css中
#原因是sql包的作者想把Stmt和具体的连接解耦,为什么要解耦,原因是想让Stmt可以长久的使用(而不是频繁的创建和销毁),但是又不想让其长久的占用一个连接,而导致连接数的暴增,以及增加连接回收的困难性,这样也会导致一个问题就是在过多的连接上创建driver.Stmt实例,这个控制不好容易导致mysql 服务端的问题(导致Prepared_stmt_count值暴增)
database/sql: Stmt的使用以及坑
#拿到 DB 创建的Stmt实例后,下次使用时就需要一个获取连接,重新绑定driver.Conn和一个driver.Stmt的过程。
#Stmt的method Exec,Query 内部都会调用func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.Stmt, err error)函数来拿到
#重新绑定driver.Conn和一个driver.Stmt实例,这个获取的过程异常曲折:
1.判断Stmt的状态是否关闭
2.判断Stmt是否是由Tx创建的,如果是,直接从Tx实例中取得driverConn和driver.Stmt返回
3.注意css中缓存的连接有可能因为各种原因关闭了,需要调用removeClosedStmtLocked()做一次清理
4.调用Stmt关联的DB实例s.db.conn(cachedOrNewConn) 获取一个连接*driverConn
5.判断Stmt css是否已经缓存里该连接,如果已经缓存则说明之前在css中已经缓存了driverConn实例和driver.Stmt,则可以直接拿来使用
6.如果Stmt css没有缓存该连接,说明该Stmt的sql语句之前没有绑定到到该连接上,需要重新绑定:通过driverConn实例和sql语句创建driver.Stmt实例,然后初始化connStmt实例,加入css中,并将
driverConn实例和driver.Stmt返还
7.拿到driverConn实例和driver.Stmt后就可以直接调用驱动提供的method进行处理了。
#调用逻辑#通过driverName获取driver,通过driver的Open()方法获得到DB的原始连接func Open(driverName, dataSourceName string) (*DB, error)=>#生成Stmtfunc (db *DB) Prepare(query string) (*Stmt, error)=>#内部func (db *DB) prepare(query string, strategy connReuseStrategy) (*Stmt, error) =>#生成driverConnfunc (db *DB) conn(strategy connReuseStrategy) (*driverConn, error)=>func (s *Stmt) Exec(args ...interface{}) (Result, error)func (s *Stmt) Query(args ...interface{}) (*Rows, error)=>#有可能使用Prepare时分配的连接,也有可能重新绑定连接func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.Stmt, err error) =>#注意这里仅仅是将Stmt实例的状态置为closed,对于TX会将连接关闭func (s *Stmt) Close() error
#第一个连接创立过程
func Open(driverName, dataSourceName string) (*DB, error){ 475 db := &DB{ 476 driver: driveri, 477 dsn: dataSourceName, 478 openerCh: make(chan struct{}, connectionRequestQueueSize), 479 lastPut: make(map[*driverConn]string), 480 } go db.connectionOpener()}#此时并没有创建连接,只是初始化DB部分数据结构#最简单的ping函数看看连接怎么建立 func (db *DB) Ping() error {491 dc, err := db.conn(cachedOrNewConn) 492 if err != nil { 493 return err 494 } 495 db.putConn(dc, nil) }#conn并不直接创建连接先到db中寻找是否有空闲连接,没有则创建driverConn实例func (db *DB) conn(strategy connReuseStrategy) (*driverConn, error){ ... 708 db.numOpen++ // optimistically 709 db.mu.Unlock() 710 ci, err := db.driver.Open(db.dsn) 711 if err != nil { 712 db.mu.Lock() 713 db.numOpen-- // correct for earlier optimism 714 db.mu.Unlock() 715 return nil, err 716 } 717 db.mu.Lock() 718 dc := &driverConn{ 719 db: db, 720 ci: ci, 721 } 722 db.addDepLocked(dc, dc) 723 dc.inUse = true 724 db.mu.Unlock() 725 return dc, nil}#将使用完的driverConn实例放到db数据结构中func (db *DB) putConn(dc *driverConn, err error) =>func (db *DB) putConnDBLocked(dc *driverConn, err error) bool #另外sql包启动单独一个goroutine负责创建连接 go db.connectionOpener() 633 func (db *DB) connectionOpener() { 634 for range db.openerCh { 635 db.openNewConnection() 636 } 637 } 639 // Open one new connection 640 func (db *DB) openNewConnection() { 641 ci, err := db.driver.Open(db.dsn) 642 db.mu.Lock() 643 defer db.mu.Unlock() 644 if db.closed { 645 if err == nil { 646 ci.Close() 647 } 648 return 649 } 650 db.pendingOpens-- 651 if err != nil { 652 db.putConnDBLocked(nil, err) 653 return 654 } 655 dc := &driverConn{ 656 db: db, 657 ci: ci, 658 } 659 if db.putConnDBLocked(dc, err) { 660 db.addDepLocked(dc, dc) 661 db.numOpen++ 662 } else { 663 ci.Close() 664 } 665 }
########################################################Stmt初始化过程########################################################生成Stmtfunc (db *DB) Prepare(query string) (*Stmt, error)=>#内部调用func (db *DB) prepare(query string, strategy connReuseStrategy) (*Stmt, error) {#创建driverConndc, err := db.conn(strategy)#创建driver.Stmtsi, err := dc.prepareLocked(query)#driverConn和driver.Stmt被添加到css中 878 stmt := &Stmt{ 879 db: db, 880 query: query, 881 css: []connStmt{{dc, si}}, 882 lastNumClosed: atomic.LoadUint64(&db.numClosed), } 883 }#Stmt的初始化和执行是分开的,再次拿到Stmt运行时需要重新绑定,以Eexc()为例分析下func (s *Stmt) Exec(args ...interface{}) (Result, error) {#最核心的connStmt()函数获取绑定Stmt的重新绑定dc, releaseConn, si, err := s.connStmt() #func resultFromStatement(ds driverStmt, args ...interface{}) (Result, error) 调用驱动程序的driver.Stmt执行res, err = resultFromStatement(driverStmt{dc, si}, args...)}func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.Stmt, err error) { #拿到一个连接*driverConndc, err := s.db.conn(cachedOrNewConn)#查询s.css中缓存的connStmt的*driverConn 是否和连接池拿到的连接是同一个,如果是同一个,则直接返回#说明Stmt实例中的sql已经完成prepare初始化,可以直接使用了1453 for _, v := range s.css {1454 if v.dc == dc {1455 s.mu.Unlock()1456 return dc, dc.releaseConn, v.si, nil1457 }1458 }#如果新拿到的连接没有缓存Stmt对应的sql的driver.Stmt数据结构#重新生成driver.Stmtsi, err = dc.prepareLocked(s.query)#创建connStmt实例,将其插入到Stmt的css 切片中cs := connStmt{dc, si}s.css = append(s.css, cs)}#如果连接池连接过多,Stmt执行时取到的连接是最初绑定的连接的概率会很低,这就会导致某个sql执行一次就要绑定一次,并且导致Stmt的 css绑定过多的连接。#这个控制内部有个机制来去func (s *Stmt) removeClosedStmtLocked()
0 0
- go database/sql 源码分析(四)sql.Stmt数据结构
- go database/sql 源码分析(三)sql.DB数据结构
- go database/sql 源码分析 -题外篇
- go database/sql 源码分析(一)sql包设计哲学
- go database/sql 源码分析(二)driver包设计哲学
- go-database-sql-tutorial
- go-mysql: database/sql 接口适配
- 数据库分库分表中间件 Sharding-JDBC 源码分析 —— SQL 解析(四)之插入SQL
- go database/sql包sql.Open不是长连接
- Golang SQL Server 数据库 stmt使用
- stmt.setDate(columnIndex, java.sql.Date);
- 原理分析之四:一次SQL查询的源码分析
- 原理分析之四:一次SQL查询的源码分析
- 原理分析之四:一次SQL查询的源码分析
- 原理分析之四:一次SQL查询的源码分析
- 原理分析之四:一次SQL查询的源码分析
- sql 数据库(database)
- 【mybatis源码分析】原理分析之四:一次SQL查询的源码分析
- Kafka集群搭建01-Zookeeper 集群部署
- java i/o流-----转换流
- 数据结构实验之栈五:下一较大值(一)
- avalon和JQ之生死求荣
- (NYoj 219)An problem about date ——日期万能公式(基姆拉尔森计算公式)
- go database/sql 源码分析(四)sql.Stmt数据结构
- 《组合变身小宠物游戏》UIManager(修改更新中)【初学者】
- HDOJ-----1869最短路
- javascript 中的 this
- org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.wei
- 【HDU】1598 - find the most comfortable road(并查集 & 暴力)
- Prism框架(一)—— 概述
- Python 爬虫6——Scrapy的安装和使用
- 用户权限管理模块的数据库设计