本文记录了我在实际工作中关于数据库操作上一些小经验,也是新手入门golang时我认为一定会碰到问题,没有什么高大上的东西,所以希望能抛砖引玉,也算是对这个问题的一次总结。其实我也是一个新手,机缘巧合几个月前开始做golang开发,以前一直是以.NET技术栈为主,文章如有错误不吝指正。

访问数据库

相信大家第一次碰到这个问题的时候应该和我一样,去网上找个例子参考一下。没错,这样的例子一搜一大把,于是我们很容易(抄)写了如下一段代码:import (

"fmt"

"database/sql"

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

)

func main() {

db, err := sql.Open("mysql","root:111111@tcp(127.0.0.1:3306)/testdb")

if err != nil {

panic(err)

}

err = db.Ping()

if err != nil {

panic(err)

}

fmt.Println("Successfully connected!")

}

把程序运行起来一看,成功地输出了想看到的东西,内心一阵暗喜“so easy"。于是把这段代码封装成一个公共方法供其他地方调用:func GetDbContext() *sql.DB {

db, err := sql.Open("mysql","root:111111@tcp(127.0.0.1:3306)/testdb")

if err != nil {

panic(err)

}

err = db.Ping()

if err != nil {

panic(err)

}

return db

}

func DoSomething(){

db := GetDbContext()

rows,_ := db.Query("select * from table1")

}

没错我最早就是这么干的,然后开始愉快地转头写CRUD了,不过事情可没这么简单。

很快, 编码五分钟捉虫两小时开场了。

慢慢的我就发现,在连续多次操作数据库后就偶尔发生程序卡死的情况,请求一直是pending状态,只能杀死进程重启才可以。刚开始没在意,也没有怀疑是数据库操作有问题,但后来越来越频繁严重影响到程序开发,没办法就记log加断点调试看是哪里出了问题。经过反复验证后确定问题就出在执行SQL语句这里,这下懵了,我看网上大家都是这么写的怎么会有问题??

连接池问题

根据多年开发经验,大胆猜测SQL执行失败最大的可能性就是数据库连接不上,在确认数据库没有崩掉的情况下开始研究代码哪里写的不对,但是前后也就那么几行代码实在看不出什么毛病,只能开始深入了研究database/sql包的知识点。

通过查资料发现open完数据库后的返回对象sql.DB实际上是一个连接池对象,并不是单纯的某一个连接。它是一个抽象的数据访问接口,和数据库类型无关,当然也就和具体的数据库Schema无关。我们要实现某一个数据库的访问单纯用这个包是不够的,还要引入具体的数据库驱动包,这个驱动才是真正实现数据库访问的东西。

现在再回过头来看代码,既然open创建了连接池,那用完把它销毁不就好了,于是参考官网文档稍加改进:func GetDbContext() *sql.DB {

db, err := sql.Open("mysql","root:111111@tcp(127.0.0.1:3306)/testdb")

if err != nil {

panic(err)

}

err = db.Ping()

if err != nil {

panic(err)

}

return db

}

func DoSomething(){

db := GetDbContext()

defer db.Close()

rows,_ := db.Query("select * from table1")

}

看似行得通,但是估计没人愿意这样做。原因很明显,别的先不谈,创建和销毁连接池开销太大了,你这样对它于心何忍,拿着屠龙刀去砍柴。

使用连接池的好处就是不需要开发者频繁地创建和销毁连接,这两项工作都交给了连接池去做,我们只需要在使用前找它要一个可用的连接,用完还回去就可以了。

这里引用一段官方文档中的原话:Although it’s idiomatic to Close() the database when you’re finished with it, the sql.DB object is designed to be long-lived. Don’t Open() and Close() databases frequently. Instead, create one sql.DB object for each distinct datastore you need to access, and keep it until the program is done accessing that datastore. Pass it around as needed, or make it available somehow globally, but keep it open. And don’t Open() and Close() from a short-lived function. Instead, pass the sql.DB into that short-lived function as an argument.

核心意思就是sql.DB是一个长生命周期对象,你不要随便打开和关闭,并且建议你在程序中为每一个数据库创建唯一的sql.DB。

那么现在的问题就是如何保证程序中只有一个连接池呢?

很简单,使用一个全局变量即可,有点类似C#和java中static的味道,在Golang中可以使用如下方法声明一个全局对象:package demo

import (

"database/sql"

)

var mydb,_ =  sql.Open("mysql","connection_string")

不过我们的业务场景比较特殊,系统中有很多个数据库,要根据不同参数去连不同数据库,那么上面这种声明赋值方式就不行了,我稍加改进,结合map实现了连接池动态管理:var envdbMap map[string]*sql.DB

func GetEnvDbContext(connector config.DbConnector) *sql.DB {

if envdbMap == nil {

envdbMap = make(map[string]*sql.DB)

}

db, ok := envdbMap[connector.ID]

if ok {

return db

} else {

connStr := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", connector.Host, connector.Port, connector.UserName, connector.Password, connector.DatabaseName)

db, err := sql.Open("postgres", connStr)

envdbMap[connector.ID] = db

return db

}

}

原理很简单,就是用map装池子,池子装连接。

有借有还

到这里连接池已经准备好了,那么如何从池子中取一个可用的连接呢?这点池子已经帮大家考虑的很周到了,大家不需要写额外代码去获取连接,直接拿起池子用就可以了,内部会有一系列机制帮你弄到一个连接去执行SQL,以后有机会对池子的原理来做个解析。

但是用完要记得还回去,这个必须你手动去做,例如:rows,_ := db.Query("select * from table1")

defer rows.Close()

// do sth...

最好不要在do sth之后做Close,因为一旦你这个过程中发生异常,导致后面的Close无法执行,那么这个连接就一直被占用,日积月累TCP连接就被你耗光了。

官方文档说了,rows.Close()是一种无害(harmless)操作,你可以做多次,但是不能忘了做。

这里有个特殊情况要注意,对于那种没有返回结果的SQL语句,千万不要使用Query方法去执行,这会导致无法回收连接,这时候推荐使用Exec方法去执行。

配置连接池

默认情况下连接池没有数量限制,但是我们的机器有TCP的数量限制,不要因为一个程序拖死一台机器,所以不推荐无限量的去使用。database/sql包提供了几个连接池配置参数,主要包含:db.SetMaxIdleConns(N) 设置空闲连接的数量

db.SetMaxOpenConns(N) 设置打开的连接数量

db.SetConnMaxLifetime(duration) 设置连接的生存时间

详细的介绍大家可以参考官方文档。

总结

经过以上分析,可以清晰的知道最开始的bug就是因为错误地使用了连接池导致数据库连接被耗光从而无法执行SQL语句,其实说简单也很简单。

以上就是工作中使用golang访问数据库的踩坑历程,希望能帮到新接触golang的朋友,如有错误的地方欢迎指出,以免误导他人。

有疑问加站长微信联系(非本文作者)

mysql pmt函数怎么用_在Golang中如何正确地使用database/sql包访问数据库相关推荐

  1. mysql pmt函数怎么用_制作C/C++动态链接库(dll)若干注意事项

    一.C\C++ 运行时库编译选项简单说明 问题:我的dll别人没法用 运行时库是个很复杂的东西,作为开发过程中dll制作需要了解的一部分,这里主要简单介绍一下如何选择编译选项. 在我们的开发过程中时常 ...

  2. golang database/sql包 简介

    概述 sql.DB不是一个连接,它是数据库的抽象接口.它可以根据driver打开关闭数据库连接,管理连接池.正在使用的连接被标记为繁忙,用完后回到连接池等待下次使用.所以,如果你没有把连接释放回连接池 ...

  3. onclick如何调用含参函数_在 golang 中如何调用私有函数(绑定隐藏的标识符)

    名字在 golang 中的重要性和在其他任何一种语言是一样的.他们甚至含有语义的作用:在一个包的外部某个名字的可见性是由这个名字首字母是否是大写来决定的. 有时为了更好的组织代码或者在其他包使用某些隐 ...

  4. mysql自定义函数的优缺点_浅谈MySQL创建自定义函数漏洞的利用和防止

    前一阵子网上风靡的MySQL的udf.dll提权我有所了解-近日由于不再在IDC行业工作了-所以也有所淡忘- 只是最近实在手痒,就决定对我的站点所在的服务器下手--.正好用上这招了- 站点的服务器是W ...

  5. golang 函数传多个参数_关于Golang中方法参数的传递

    结构体声明 为了说明函数以及方法调用的过程,这里先定义一个struct,在下面的描述中会使用到它. type Person struct { Name string Age uint16 } 普通函数 ...

  6. golang 包含 数组_在 Golang 中如何快速判断字符串是否在一个数组中

    在使用 Python 的时候,如果要判断一个字符串是否在另一个包含字符串的列表中,可以使用in 关键词,例如: name_list= ['pm', 'kingname', '青南'] if 'king ...

  7. go语言的iota是什么意思_关于Golang中的iota

    快速一览 iota是Golang中提供的一个简化常量和枚举编程的标识符,合理的使用这个标识符可以让代码变得更简洁,省去大量的不必要的代码. 比如下面的这个常量定义 const ( a = 1 b = ...

  8. go语言os.exit(1)_在Golang中各种永远阻塞的姿势

    在Golang中各种永远阻塞的姿势 Go的运行时的当前设计,假定程序员自己负责检测何时终止一个goroutine以及何时终止该程序. 可以通过调用os.Exit或从main()函数的返回来以正常方式终 ...

  9. java js中 function函数报错_浅析JS中对函数function的理解(基础篇)

    正文:我们知道,在js中,函数实际上是一个对象,每个函数都是Function类型的实例,并且都与其他引用类型一样具有属性和方法.因此,函数名实际上是指向函数对象的指针,不与某个函数绑定.在常见的两种定 ...

最新文章

  1. 也许是东半球直接底气的分库分表实践了
  2. FTP同步的另类解决办法——NetDrive
  3. java object转list_这份Java开发规范,让你100%受益!
  4. Git 命令大全整理
  5. MyTask - old implementation - getEntitySet
  6. UIImagePickerController和UIAlertController结合使用
  7. Atitit.  c# 语法新特性 c#2.0 3.0 4.0 4.5 5.0 6.0   attilax总结
  8. 逸出 java_【java】知识系谱-基础篇-线程-发布、逸出
  9. 计算机三级信息安全技术考什么2019,2019计算机三级信息安全技术精品练习8
  10. 计算机台式右上角三个灯作用,键盘右上角的三个灯是什么?有什么用
  11. 信息奥赛一本通(1120:同行列对角线的格)
  12. php计算昨天,php时间计算,明天,昨天,前天,上周,本周,上月等等
  13. Android的TextView部分文字点击切换颜色(ClickableSpan)
  14. Rhino导入Revit生成体量幕墙的方法和操作要点
  15. 四旋翼飞行器数学模型
  16. 安装windows XP或者2003时提示找不到硬盘驱动器的解决办法(总结)转载
  17. Mac使用-键盘符号对照
  18. mysql里一个中文汉字占多少字节数?
  19. python里的英文歌叫什么_Python告诉你:从《入海》到《消愁》毛不易的歌里都在唱些什么?...
  20. 后端开发——Java

热门文章

  1. 工程总承包(EPC)高级项目经理,未来可期!
  2. arcgis教学视频
  3. 球状空心介孔硫化铋/二氧化硅纳米微球/全无机铯铅卤化物钙钛矿纳米晶修饰二氧化硅微球相关制备
  4. 计算机科学班(原acm班),计算机科学与技术 培养计划(ACM班)
  5. 前端切图要选择png和jpg呢?
  6. 区块链:数据要素市场化配置背后的“推手”
  7. 火狐浏览器各版本的下载地址
  8. 云课堂植入计算机机房,锐捷云课堂增强版,做高校云机房解决方案
  9. 幼儿园科学机器人教案
  10. [Python]生成 txt 文件