database/sql代码浅读

读了两遍database/sql,脑袋还是一团浆糊,这里只简单的聊聊,不做细节讨论,后续如果发现好的设计技巧,会再写。

database/sql包的主要文件是driver.go和sql.go,driver.go定义interface,提供给第三方driver接入,golang标准库不实现特定db的driver。可以参考go-sql-driver

driver.go中interface的设计体现了整个sql.go需要用到的各个角色该实现什么样的方法,可以设想一下,作为app想要连接db时,会按照以下步骤:

  • 获取数据库对象,利用对象封装好的func和db沟通,主要是封装优化性能和tcp交互的代码。database/sql使用了比较普遍的方式map+lock允许app连接多个不同db
  • driver和db之间会维护conn pool,有db操作请求,通过conn与db沟通,之前用java有第三方pool实现,但database/sql把他统一在sql.go中,我想这也是golang加入了软件工程元素的体现,避免了像java面对conn pool决策时付出的代价。当然为了提升对于程序的可控性,仍旧需要读一下database/sql中pool的实现,甚至可以参与到这个开源的community中,根据自己的业务情况,帮助golang标准库的作者优化这块
  • 接上面的话题,拿到db对象后,就可以根据db提供的包装后func进行db操作,database/sql把操作大体分两种Query&Exec,这里不详细描述两种操作的区别
  • 值得讨论的是,database/sql也封装了driver<->database、app<->driver在数据传递时的转化功能,和conn pool一样,如果这个东西是一定要有的,为什么不放到标准库中
  • 向db发送操作,db执行,很直观的业务使用方式,但stmt的引入给app层的使用引入了一定的灵活度,database/sql把与语句相关的逻辑拆分出来叫stmt,当语句不变的情况下有大量操作时,可以提前prepare语句,然后直接用stmt提供的query和exec更加节省资源
  • 补充一下,sql.go实现了一个conn request的缓冲队列,有goroutine监控db的maxConn和freeConn等参数,及时消耗队列中的请求,这块也值得coder借鉴

最近在对比团队内部封装的xdb和sqlx,所以粗浅的读了一下两个lib,注意lib不是driver,lib是在driver+database/sql上层封装新特性,刚开始看的时候发现功能都差不多,主要是优化app的使用体验和代码可读性,但在了解database/sql可以支持的使用方式后,还是发现xdb在封装上的优劣:

  • 对app封装了Find、FindAll等方法,直接将sql.Result写入到app的struct中,并引入beedb(beego框架orm实现)进一步增加app使用便利性(好)
  • 也对database/sql native操作进行了封装,添加了统计类的逻辑,但是这块感觉开发人员对database/sql理解度不够,仅仅从简单业务角度封装,没有支持stmt(坏)
  • 不能支持多db,类似pg,如果引入pg,就需要增加兼容性处理模块(sqlx处理了)(坏)
  • 性能上raw>sqlx>>xdb,这里并没有细致的分析代码细节,需要xdb作者自己不断的测试和推敲

最后说一句,database/sql中对于conn pool和依赖管理的实现值得一看。

下面可能补充一些技术细节:

  • sql.DB中有maxIdlemaxOpen两个变量,而且先定maxIdle<=maxOpenmaxIdle更倾向于资源控制,合理的idle conn数量即避免没有意义的紫原占用,又能有效的reuse资源。maxOpen用于控制与database之间位置的conn数量,倾向于对database的过载保护问题。所以maxOpen肯定是idle的上限。
  • sql.DB通过connRequest维护一个缓冲队列,操作来就会通过conn给这个req分配conn资源,先查看是否freeConn,没有且不能打开新conn了就会入队,否则创建新conn满足需求。
database/sql代码浅读
Share this