Go源码分析系列:sql驱动加载

对于开发人员来说,编程语言或是框架都只是工具,重要的是我们的思维方式,所以,我们有些东西,我们不仅要会用,更要知晓其原理和本质。

这也是这个(Go|源码分析)系列文章存在的意义,记录的同时,也希望对大家有所帮助。

话不多说,进入正题:

-------------------------------

 

服务端开发,少不了数据库操作,就从最基础的开始,那就先说一下go语言中提供的一个sql框架(database/sql)。

这个框架很轻量,轻量到只提供了一个架子,没有任何具体实现,我们可以通过插件的方式注册自己想要使用的数据库,比如MySQL。

下面看一段基础的使用代码:

package main

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

var db *sql.DB // 连接池对象

func initDB() (err error) {
	dsn := "user:pwd@tcp(ip:port)/db" // 用户名:密码@tcp(IP:端口)/数据库名
	db, err = sql.Open("mysql", dsn) // 只会校验格式
	if err != nil {
		fmt.Printf("dsn:%s invalid, err = %v\n", dsn, err)
		return
	}
	err = db.Ping() // 连接服务器校验登录信息
	if err != nil {
		fmt.Printf("open:%s failed, err = %v\n", dsn, err)
		return
	}
	fmt.Printf("open:%s success\n", dsn)
	return
}

代码很简单,就是建立一个mysql的数据库连接池对象,这段代码里面最重要的就是这一行代码:

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

如果不加这一句,在“sql.Open("mysql", dsn)”的时候,就会告知你没有mysql的驱动,至于原因,我们看一下“sql.Open”的源码:

package sql

var (
	...
	drivers   = make(map[string]driver.Driver)
)

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

func Open(driverName, dataSourceName string) (*DB, error) {
	...
	driveri, ok := drivers[driverName]
	...
	return OpenDB(dsnConnector{dsn: dataSourceName, driver: driveri}), nil
}

drivers默认是空的,也就是说,只能通过Register注册驱动后,才能使用,这就是关键点了。

为什么要导“github.com/go-sql-driver/mysql”包呢?因为mysql包下的driver.go做的就是初始化注册驱动的操作:

package mysql

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

这也就解释了,为什么不导包,“sql.Open("mysql", dsn)”就会报错,也能充分体现出插件化的灵活性。
 

如果大家有兴趣,我再多说一点关于数据库连接池的知识点,看下调用的代码:

func main() {
	err := initDB()
	if err != nil {
		fmt.Printf("init db failed, err = %v\n", err)
		return
	}
	defer db.Close()

	var u user
	sql := `select id,name from user where id=?;`
	row := db.QueryRow(sql, 2)
	err = row.Scan(&u.id, &u.name)
	if err != nil {
		fmt.Printf("Scan failed, err = %v\n", err)
		return
	}
	fmt.Printf("Scan success, u = %v\n", u)
}

关键代码在“db.QueryRow”和“row.Scan”这两行,在“db.QueryRow”之后,如果没有调用“row.Scan”,连接池就会处于一个保持连接的状态,占用资源,达到一定数量线程将会阻塞,看一下“row.Scan”中的代码实现就明白了:


func (r *Row) Scan(dest ...interface{}) error {
	...
	defer r.rows.Close()
	...
}

Scan操作将会关闭当前QueryRow建立的连接池,所以,为了避免此情况,也有很多人会采用以下写法:

db.QueryRow(sql, 2).Scan(&u.id, &u.name)

至于连接池的大小,可以通过以下方式设置(一般放在初始化的时候):

db.SetMaxOpenConns(10) 

文章主题就到这里。

 

这是我写的第一篇源码分析的文章,这些源码都很简单,但是我相信,本文的主题会有很多人听到“必须导入这个驱动才能连接mysql”这句话,就结束了,但这只是结论,即便记住了这个结论,对自己未来的成长用处也不大,但如果你看了这些源码,你能了解的不仅仅是为什么会有这个结论,还会学习到,别人的编程风格和程序设计的方式,当然每个人看到的点可能都会不太一样,但可以确定的是,这些东西随着你的不断积累,会潜移默化的带给你一些思维方式的改变,所以,保持这个好奇心,持续学下去。

 

如果您有问题或者其他建议,欢迎留言讨论。

 

------------

作者寄语:

我们缺乏的常常不是编码能力,而是思考能力。
既然热爱编程,那就静下心来,要知道,每一个台阶,都可以是终点,也可以是新的起点。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值