sql
总结
golang只实现了框架,用于如何连接数据库,具体的数据库连接的驱动,由各类数据库实现,如postgresql为pg,mysql为mysql,整体实现为driver目录下定义各种接口,接口定义得比较细致,可能一个函数就对应一个接口,sql目录下实现整体框架,DB结构体为整体入口,Open函数会创建DB,使用DB调用各类操作函数
接口
接口定义在driver目录下
- driver.Driver,具体驱动实现该接口
- Open(name string) (Conn, error), 用于新开一个连接
- driver.DriverContext
- OpenConnector(name string) (Connector, error),用于新开一个Connector
- driver.Connector
- Connect(context.Context) (Conn, error), 用于新开一个Conn
- Driver() Driver 返回Connector对应的Driver
- struct dsnConnect为Connector接口的默认实现
- driver.Conn
- Prepare(query string) (Stmt, error)
- Close() error
- Begin() (Tx, error)
- driver.Stmt,具体数据库操作实现的接口
- Close() error,关闭statement
- NumInput() int,返回参数个数
- Exec(args []Value) (Result, error),该接口已经Deprecated,Driver应该实现StmtExecContext
- Query(args []Value) (Rows, error),该接口已经Deprecated,Driver应该实现StmtQueryContext
- driver.Tx
- Commit() error,事务提交
- Rollback() error,事务回滚
- driver.Pinger,发送ping消息,保持连接存活
- Ping(ctx context.Context) error
- driver.Execer,定义Exec的接口
- Exec
- driver.ExecerContext
- ExecContext(ctx context.Context, query string, args []NamedValue) (Result, error)
- driver.Queryer, 查询接口定义,已经Deprecated
- Query(Query string, args []Value) (Rows, error)
- driver.QueryerContext
- QueryContext(ctx context.Context, query string, args []NamedValue) (Rows, error)
- driver.ConnPrepareContext
- PrepareContext(ctx context.Context. query string) (stmt, error)
- Driver.ConnBeginTx
- BeginTx(ctx context.Context, opts TxOptions) (Tx, error)
- SessionResetter,重设连接的session
- ResetSession
框架实现函数
- sql.Open函数
- 从drivers里面获取注册的驱动,如果没有返回err
- 如果对应的驱动实现了driver.DriverContext接口,然后调用该接口获取Connector,最后调用OpenDB函数
- 如果没有实现,使用默认实现的dsnConnector,最后调用OpenDB
- sql.Register函数,各个驱动注册自己的driver.Driver实现到drivers(map存储),如果重复,会panic
- OpenDB函数,返回sql.DB结构体,主要启动一个后台deamon,用于管理connection
- 生成待cancel的context,cancel放到sql.DB中
- context传入connectionOpener,并且通过go异步出去,这个是个connection的deamon,用于在出现错误的时候,新建connection
- connectionOpener是一个for循环,通过ctx.Done退出循环
- 通过openerCh来触发openNewConnection
- openerCh的输入方为maybeOpenNewConnections函数,如果numRequests(请求数)大于0,则新建一个
- openNewConnection
- 通过connector新建一个Connector ci
- 把新建的ci放到driverConn里,如果connRequests大于0,通过从connRequests(map[uint64] chan connRequest)的随机获取一个chan,把driverConn给对应的请求
- 如果没有请求,则放到freeConn里面去,如果没有超过maxIDleConns
- sql.DB里的具体函数实现,用于数据库请求
- PrepareContext函数,Prepare函数调用PrepareContext,返回Stmt
- 会重试,大小为maxBadConnRetries(值为2)
- 调用prepare->conn->prepareDC
- prepare函数
- 调用conn函数-> prepareDC函数
- conn函数,返回driverConn,跟openNewConnection实现类似
- 判断当前DB是否已经被关闭
- 如果strategy是cachedOrNewConn,且freeConn里面还有空闲conn,则直接使用,如果获取的conn已经过期了,直接返回driver.ErrBadConn
- 如果maxOpen大于0,且当前打开的numOpen不小于maxOpen,则新建一个connRequest,放到connRequests里去,然后等待putConnDBLocked里往chan里写入connRequest
- 下面是创建的场景,connector调用Connect函数,返回Connector ci,然后封装到driverConn里面,并放到Dep里面去
- prepareDC,将query、driverConn、release封装到Stmt(支持并发,底层绑定的是同一个connection)
- 里层调用ctxDriverPrepare函数,返回driverStmt
- 如果driver.Conn实现了driver.ConnPrepareContext函数,则调用该PrepareContext函数
- 如果没有实现,则调用Conn的Prepare函数
- 将driverStmt、db、query等封装到Stmt里
- 里层调用ctxDriverPrepare函数,返回driverStmt
- ExecContext,有retry,默认2次,执行exec函数
- exec
- 先调用conn函数,获取driverConn
- 调用execDC函数
- execDC
- 如果driverConn.ci Connector实现了ExecerContext,或者driver.Execer,则调用对应的函数
- 如果没有实现,则调用ctxDriverPrepare函数和resultFromStatement,大体跟上面差不多
- QueryContext,执行查询,上面exec,sql都执行,tx除外,几种类型的命名风格都类似,先调用conn,然后执行*DC
- query函数实现,先调用conn获取连接
- 再调用queryDC
- queryDC函数
- 看Connector是否实现dirver.QueryerContext或者Queryer接口,如果是,直接调用对应的实现
- 没有实现,则是默认实现,调用ctxDriverPrepare和rowsiFromStatement返回结果
- BeginTx
- 失败会重试1次
- 调用begin函数
- begin,开始事务
- conn,获取driverConn
- 调用beginDC,返回sql.Tx
- driverConn的ci(Conn)实现了ConnBeginTx,则调用对应的实现
- 否则调用Begin函数
- Driver函数,返回DB存的Driver
- PrepareContext函数,Prepare函数调用PrepareContext,返回Stmt
- Conn结构体,集成到DB中,DB的Conn函数,返回一个可用的Conn
- PingContext函数,调用DB的pingDC函数,发送ping命令,实则为Pinger接口,具体为各个数据库驱动实现