在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
Go没有内置的驱动支持任何的数据库,但是Go定义了database/sql接口,用户可以基于驱动接口开发相应数据库的驱动。 目前NOSQL已经成为Web开发的一个潮流,很多应用采用了NOSQL作为数据库,而不是以前的缓存,后面将介绍MongoDB和Redis两种NOSQL数据库。 详细分析一下Go都定义了哪些标准接口: sql.Register 这个存在于database/sql的函数是用来注册数据库驱动的,当第三方开发者开发数据库驱动时,都会实现init函数,在init里面会调用这个Register(name string, driver driver.Driver)完成本驱动的注册。 我们来看一下mymysql、sqlite3的驱动里面都是怎么调用的: //https://github.com/mattn/go-sqlite3驱动 func init() { sql.Register("sqlite3", &SQLiteDriver{}) } //https://github.com/mikespook/mymysql驱动 // Driver automatically registered in database/sql var d = Driver{proto: "tcp", raddr: "127.0.0.1:3306"} func init() { Register("SET NAMES utf8") sql.Register("mymysql", &d) } 我们看到第三方数据库驱动都是通过调用这个函数来注册自己的数据库驱动名称以及相应的driver实现。在database/sql内部通过一个map来存储用户定义的相应驱动。 var drivers = make(map[string]driver.Driver) drivers[name] = driver 因此通过database/sql的注册函数可以同时注册多个数据库驱动,只要不重复。 import ( "database/sql" _ "github.com/mattn/go-sqlite3" ) 这儿使用_的意思是引入后面的包名而不直接使用这个包中定义的函数,变量等资源。 包在引入的时候会自动调用包的init函数以完成对包的初始化。因此,我们引入上面的数据库驱动包之后不用手动去调用init函数,然后在init函数里面注册这个数据库驱动,这样我们就可以在接下来的代码中直接使用这个数据库驱动了。 driver.Driver Driver是一个数据库驱动的接口,他定义了一个method: Open(name string),这个方法返回一个数据库的Conn接口。 type Driver interface { Open(name string) (Conn, error) } 返回的Conn只能用来进行一次goroutine的操作,也就是说不能把这个Conn应用于Go的多个goroutine里面。 第三方驱动都会定义这个函数,它会解析name参数来获取相关数据库的连接信息,解析完成后,它将使用此信息来初始化一个Conn并返回它。 driver.Conn Conn是一个数据库连接的接口定义,他定义了一系列方法,这个Conn只能应用在一个goroutine里面 type Conn interface { Prepare(query string) (Stmt, error) Close() error Begin() (Tx, error) } Prepare函数返回与当前连接相关的执行Sql语句的准备状态,可以进行查询、删除等操作。 driver.Stmt Stmt是一种准备好的状态,和Conn相关联,而且只能应用于一个goroutine中 type Stmt interface { Close() error NumInput() int Exec(args []Value) (Result, error) Query(args []Value) (Rows, error) } Close函数关闭当前的链接状态,但是如果当前正在执行query,query还是有效返回rows数据。 driver.Tx 事务处理一般就两个过程,递交或者回滚。数据库驱动里面也只需要实现这两个函数就可以 type Tx interface { Commit() error Rollback() error } driver.Execer 这是一个Conn可选择实现的接口。如果这个接口没有定义,那么在调用DB.Exec,就会首先调用Prepare返回Stmt,然后执行Stmt的Exec,然后关闭Stmt。
type Execer interface { Exec(query string, args []Value) (Result, error) } driver.Result 这个是执行Update/Insert等操作返回的结果接口定义 type Result interface { LastInsertId() (int64, error) RowsAffected() (int64, error) } LastInsertId函数返回由数据库执行插入操作得到的自增ID号。 driver.Rows Rows是执行查询返回的结果集接口定义 type Rows interface { Columns() []string Close() error Next(dest []Value) error } Columns函数返回查询数据库表的字段信息,这个返回的slice和sql查询的字段一一对应,而不是返回整个表的所有字段。 driver.RowsAffected RowsAffested其实就是一个int64的别名,但是他实现了Result接口,用来底层实现Result的表示方式 type RowsAffected int64 func (RowsAffected) LastInsertId() (int64, error) func (v RowsAffected) RowsAffected() (int64, error) driver.Value Value其实就是一个空接口,他可以容纳任何的数据 type Value interface{} drive的Value是驱动必须能够操作的Value,Value要么是nil,要么是下面的任意一种int64、float64、bool、[]byte、string 、time.Time driver.ValueConverter ValueConverter接口定义了如何把一个普通的值转化成driver.Value的接口 type ValueConverter interface { ConvertValue(v interface{}) (Value, error) } 在开发的数据库驱动包里面实现这个接口的函数在很多地方会使用到,这个ValueConverter有很多好处: driver.Valuer Valuer接口定义了返回一个driver.Value的方式 type Valuer interface { Value() (Value, error) } 很多类型都实现了这个Value方法,用来自身与driver.Value的转化。 通过上面这些,对于驱动的开发有了一个基本的了解,一个驱动只要实现了这些接口就能完成增删查改等基本操作了,剩下的就是与相应的数据库进行数据交互等细节问题了。 database/sql database/sql在database/sql/driver提供的接口基础上定义了一些更高阶的方法,用以简化数据库操作,同时内部还建议性地实现一个conn pool。 type DB struct { driver driver.Driver dsn string mu sync.Mutex // protects freeConn and closed freeConn []driver.Conn closed bool } 我们可以看到Open函数返回的是DB对象,里面有一个freeConn,它就是那个简易的连接池。它的实现相当简单或者说简陋,就是当执行Db.prepare的时候会defer db.putConn(ci, err),也就是把这个连接放入连接池,每次调用conn的时候会先判断freeConn的长度是否大于0,大于0说明有可以复用的conn,直接拿出来用就是了,如果不大于0,则创建一个conn,然后再返回之。 MySQL驱动 Go中支持MySQL的驱动目前比较多,有如下几种,有些是支持database/sql标准,而有些是采用了自己的实现接口,常用的有如下几种: package main import ( "database/sql" "fmt" _ "github.com/Go-SQL-Driver/MySQL" ) func main() { connection, err := sql.Open("mysql", "aries1991:admin123@/aries?charset=utf8") panicErr(err) //插入数据 stmt, err := connection.Prepare("INSERT userinfo SET username=?,departname=?,created=?") panicErr(err) res, err := stmt.Exec("astaxie", "研发部门", "2012-12-09") panicErr(err) id, err := res.LastInsertId() panicErr(err) fmt.Println(id) //查询数据 rows, err := connection.Query("SELECT * FROM userinfo") panicErr(err) for rows.Next() { var uid int var username string var department string var created string err = rows.Scan(&uid, &username, &department, &created) panicErr(err) fmt.Println(uid, username, department, created) } connection.Close() } func panicErr(err error) { if err != nil { panic(err) } } 解释一下关键几个函数: 第二个参数是DNS(Data Source Name),它是Go-MySQL-Driver定义的一些数据库链接和配置信息。支持如下格式: user:password@tcp(localhost:5555)/dbname?charset=utf8 user:password@/dbname user:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname db.Prepare()函数用来返回准备要执行的sql操作,然后返回准备完毕的执行状态。 db.Query()函数用来直接执行Sql返回Rows结果。 stmt.Exec()函数用来执行stmt准备好的SQL语句 NOSQL数据库操作 redis redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)和zset(有序集合)。 Go目前支持redis的驱动有如下 : https://github.com/alphazero/Go-Redis 、http://code.google.com/p/tideland-rdc、https://github.com/simonz05/godis、https://github.com/astaxie/goredis //TODO #笔记内容来自 《Go Web编程》
|
请发表评论