文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载数据库几乎可以存储 Web 应用想查询和修改的任何信息——用户资料、产品目录、新闻列表等因此对多数 Web 应用而言数据库就是其核心所在。本指南基于开源项目 build-web-application-with-golang 的第五章内容系统梳理 Go 语言访问数据库的完整技术路线从 Go 官方定义的database/sql标准驱动接口到 MySQL / SQLite / PostgreSQL 三种主流关系型数据库驱动的使用再到基于该接口自研的 beego orm 库最后覆盖 Redis 与 MongoDB 两类 NoSQL 数据库的实操。读完本文你将掌握如何用 Go 以统一接口操作不同数据库的设计思想以及可直接落地的增删改查代码。为什么数据库是 Web 应用的核心对于任何需要持久化数据的 Web 应用数据库都是不可或缺的组件。用户信息、产品目录、新闻列表等业务数据都需要可靠的存储、查询与修改能力。Go 语言与 PHP 等语言不同官方并未内置任何数据库驱动而是定义了database/sql标准接口开发者包括第三方库作者可以基于该接口开发各自的数据库驱动。这样设计有一个直接好处——只要代码是依据标准接口编写的日后迁移数据库时几乎不需要修改业务代码只需更换驱动即可。正是基于这一设计第五章围绕四条主线展开见章节总览5.1 接口设计介绍 Go 如何设计数据库驱动接口sql.Register、driver.Driver等这是理解全部后续内容的基础5.25.4 关系型驱动分别讲解目前使用较多的 MySQL、SQLite、PostgreSQL 驱动及具体用法5.5 ORM 开发讲解基于database/sql标准接口自研的 beego orm 库兼容几乎所有支持database/sql的驱动可以用 Go 风格struct 操作进行数据库开发5.6 NoSQL 操作讲解 MongoDB 与 Redis 两类 NoSQL 数据库的接入与使用。各小节详细内容可依次阅读 database/sql 接口、使用 MySQL 数据库、使用 SQLite 数据库、使用 PostgreSQL 数据库、使用 Beego orm 库进行 ORM 开发、NOSQL 数据库操作 以及本章小结。database/sql 接口Go 的数据库驱动设计思想database/sql是 Go 官方标准库中负责统一数据库访问的接口层它并不关心底层具体是哪种数据库而是通过database/sql/driver包定义的一套接口契约让第三方驱动按标准接入。本节逐个拆解这套接口的构成与含义这也是理解后续所有驱动用法和 ORM 原理的钥匙。sql.Register驱动的注册入口sql.Register(name string, driver driver.Driver)是database/sql包中用于注册数据库驱动的函数。第三方驱动开发者在实现驱动时都会定义一个init函数并在其中调用Register完成本驱动的注册。例如 go-sqlite3 驱动与 mymysql 驱动的注册方式// go-sqlite3 驱动 func init() { sql.Register(sqlite3, SQLiteDriver{}) } // mymysql 驱动Driver 自动注册到 database/sql var d Driver{proto: tcp, raddr: 127.0.0.1:3306} func init() { Register(SET NAMES utf8) sql.Register(mymysql, d) }database/sql内部通过一个 map 存储已注册的驱动var drivers make(map[string]driver.Driver) drivers[name] driver因此同一进程内可以同时注册多个数据库驱动只要驱动名不重复。这里有一个新手常见困惑使用database/sql与第三方驱动时经常看到_导入例如import ( database/sql _ github.com/mattn/go-sqlite3 )_在变量赋值中是忽略赋值的占位符在包导入中作用类似引入该包但不在代码中直接使用其定义的函数或变量。正如书中 2.3 节流程与函数一节所讲包在引入时会自动调用其init函数完成初始化。因此引入驱动包后init内会自动执行sql.Register完成驱动注册接下来的代码就能直接使用该驱动了。核心驱动接口族driver包定义了一组接口任何驱动只要实现这些接口即可完成基本的增删改查操作剩下的是与具体数据库交互的细节问题。driver.Driver—— 驱动入口接口定义了一个Open(name string)方法返回一个数据库Conn接口type Driver interface { Open(name string) (Conn, error) }驱动会解析name参数连接信息来初始化并返回一个Conn。注意返回的Conn只能用于一次 goroutine 的操作不能跨多个 goroutine 共享否则会出现结果错乱go goroutineA(Conn) // 执行查询操作 go goroutineB(Conn) // 执行插入操作这样的代码可能让 Go 无法判断某个操作究竟由哪个 goroutine 发起例如把 goroutineA 的查询结果返回给 goroutineB导致 B 错误地将其当成自己执行的插入结果。driver.Conn—— 数据库连接接口同样只能应用于一个 goroutinetype Conn interface { Prepare(query string) (Stmt, error) Close() error Begin() (Tx, error) }Prepare返回与当前连接相关的、处于准备状态的 SQL 语句Stmt可执行查询、删除等操作Close关闭当前连接并释放连接资源。由于驱动实现了database/sql建议的连接池conn pool无需自行缓存连接自行缓存反而容易引发问题Begin返回一个代表事务处理的Tx通过它可进行查询、更新操作也可对事务回滚或提交。driver.Stmt—— 预编译语句状态与Conn相关联同样只能用于一个 goroutinetype Stmt interface { Close() error NumInput() int Exec(args []Value) (Result, error) Query(args []Value) (Rows, error) }Close关闭当前语句状态若当前正在执行 queryquery 仍会有效返回 rows 数据NumInput返回预留参数的个数返回0时驱动会智能检查调用者传入的参数当驱动不知道预留参数个数时返回-1Exec执行Prepare准备好的 SQL传入参数执行 update/insert 等操作返回ResultQuery执行Prepare准备好的 SQL传入参数执行 select 操作返回Rows结果集。driver.Tx—— 事务接口只需实现提交与回滚两个操作type Tx interface { Commit() error Rollback() error }driver.Execer——Conn可选择实现的接口type Execer interface { Exec(query string, args []Value) (Result, error) }若驱动未实现此接口调用DB.Exec时会先调用Prepare返回Stmt执行Stmt.Exec再关闭Stmt。driver.Result—— 执行 Update/Insert 等操作返回的结果接口type Result interface { LastInsertId() (int64, error) RowsAffected() (int64, error) }LastInsertId返回数据库执行插入操作得到的自增 ID 号RowsAffected返回执行 Update/Insert 等操作影响的数据条数。driver.Rows—— 执行查询返回的结果集接口type Rows interface { Columns() []string Close() error Next(dest []Value) error }Columns返回查询数据库表的字段信息与 SQL 查询的字段一一对应而非返回整张表的所有字段Close关闭 Rows 迭代器Next返回下一条数据并赋值给dest。dest中的元素必须是driver.Value的值除 string 外返回数据中的所有 string 都必须转换为[]byte。当数据读取完毕时Next返回io.EOF。driver.RowsAffected—— 一个int64别名实现了Result接口用于底层表示 Resulttype RowsAffected int64 func (RowsAffected) LastInsertId() (int64, error) func (v RowsAffected) RowsAffected() (int64, error)driver.Value—— 一个空接口可以容纳任何数据但驱动必须能操作的值仅限以下类型type Value interface{} // Value 必须是 nil 或以下任意一种 // int64 // float64 // bool // []byte // string Rows.Next 返回的不能是 string // time.Timedriver.ValueConverter—— 定义如何把普通值转换成driver.Value的接口type ValueConverter interface { ConvertValue(v interface{}) (Value, error) }该接口在驱动包中被广泛使用主要价值体现在三处把driver.Value转化为数据库表相应字段如int64如何转化成uint16字段把数据库查询结果转化成driver.Value在Scan函数中把driver.Value转化成用户自定义的值。driver.Valuer—— 定义返回一个driver.Value的方式type Valuer interface { Value() (Value, error) }很多类型都实现了这个Value方法用于自身与driver.Value的互相转换。database/sql 与连接池在database/sql/driver提供的接口之上database/sql还定义了一些更高阶的方法以简化数据库操作同时在内部建议性地实现了一个连接池。其核心数据结构如下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→db.prepareDC时会defer dc.releaseConn然后调用db.putConn把连接放回连接池每次调用db.conn时先判断freeConn的长度是否大于 0——大于 0 说明有可复用的连接直接取出使用否则新建一个连接再返回。理解了这套接口与连接池机制下面三节的关系型驱动使用就水到渠成了。关系型数据库驱动实战MySQL、SQLite 与 PostgreSQLMySQL最常用的 Web 后端数据库目前互联网上流行的 LAMP 网站构架中M 即 MySQL。它以免费、开源、使用方便的优势成为很多 Web 应用的后端存储引擎。Go 中支持 MySQL 的驱动较多常用的有如下几种go-sql-driver/mysql支持database/sql全部采用 Go 编写书中示例与作者项目均采用此驱动也是推荐选择ziutek/mymysql支持database/sql也支持自定义接口全部采用 Go 编写Philio/GoMySQL不支持database/sql自定义接口全部采用 Go 编写。推荐 go-sql-driver/mysql 的理由是该驱动比较新、维护较好完全支持database/sql接口底层支持 keepalive 保持长连接相比之下 mikespook fork 的 mymysql 虽也支持 keepalive但不是线程安全的。后续章节的示例统一采用同一套表结构数据库test、用户表userinfo与关联用户信息表userdetailCREATE TABLE userinfo ( uid INT(10) NOT NULL AUTO_INCREMENT, username VARCHAR(64) NULL DEFAULT NULL, department VARCHAR(64) NULL DEFAULT NULL, created DATE NULL DEFAULT NULL, PRIMARY KEY (uid) ); CREATE TABLE userdetail ( uid INT(10) NOT NULL DEFAULT 0, intro TEXT NULL, profile TEXT NULL, PRIMARY KEY (uid) )下面是使用database/sql接口对userinfo表执行增删改查的完整示例仓库中 de/code/src/apps/ch.5.2/main.go 提供了对应可运行实现de/code/src/apps/ch.5.2/schema.sql 提供建表语句package main import ( database/sql fmt _ github.com/go-sql-driver/mysql ) func main() { db, err : sql.Open(mysql, astaxie:astaxie/test?charsetutf8) checkErr(err) // 插入数据 stmt, err : db.Prepare(INSERT INTO userinfo SET username?,department?,created?) checkErr(err) res, err : stmt.Exec(astaxie, 研发部门, 2012-12-09) checkErr(err) id, err : res.LastInsertId() checkErr(err) fmt.Println(id) // 更新数据 stmt, err db.Prepare(update userinfo set username? where uid?) checkErr(err) res, err stmt.Exec(astaxieupdate, id) checkErr(err) affect, err : res.RowsAffected() checkErr(err) fmt.Println(affect) // 查询数据 rows, err : db.Query(SELECT * FROM userinfo) checkErr(err) for rows.Next() { var uid int var username string var department string var created string err rows.Scan(uid, username, department, created) checkErr(err) fmt.Println(uid, username, department, created) } // 删除数据 stmt, err db.Prepare(delete from userinfo where uid?) checkErr(err) res, err stmt.Exec(id) checkErr(err) affect, err res.RowsAffected() checkErr(err) fmt.Println(affect) db.Close() } func checkErr(err error) { if err ! nil { panic(err) } }关键函数说明sql.Open()用于打开一个已注册的数据库驱动go-sql-driver 中注册了mysql第二个参数是 DSNData Source Name数据源名称是 go-sql-driver 定义的连接与配置信息支持如下格式userunix(/path/to/socket)/dbname?charsetutf8 user:passwordtcp(localhost:5555)/dbname?charsetutf8 user:password/dbname user:passwordtcp([de:ad:be:ef::ca:fe]:80)/dbnamedb.Prepare()返回准备执行的 SQL 操作及准备完毕的执行状态db.Query()直接执行 SQL 并返回Rows结果集stmt.Exec()执行stmt准备好的 SQL 语句。示例中所有传入参数均以?占位符对应的数据形式给出这种预编译 占位符的方式可以在一定程度上防止 SQL 注入。SQLite零配置的嵌入式数据库SQLite 是一个开源的嵌入式关系数据库实现自包容、零配置、支持事务的 SQL 数据库引擎特点是高度便携、使用方便、结构紧凑、高效、可靠。与其他数据库管理系统不同SQLite 的安装和运行非常简单大多数情况下只要确保 SQLite 二进制文件存在即可开始创建、连接和使用数据库可以看作开源的 Access。Go 支持 SQLite 的驱动较多但很多不支持database/sql接口mattn/go-sqlite3支持database/sql接口基于 cgo 编写书中作者项目采用此驱动标准接口便于未来迁移到更好的驱动feyeleanor/gosqlite3不支持database/sql接口基于 cgo 编写phf/go-sqlite3不支持database/sql接口基于 cgo 编写。SQLite 版本对应的建表 SQLCREATE TABLE userinfo ( uid INTEGER PRIMARY KEY AUTOINCREMENT, username VARCHAR(64) NULL, department VARCHAR(64) NULL, created DATE NULL ); CREATE TABLE userdetail ( uid INT(10) NULL, intro TEXT NULL, profile TEXT NULL, PRIMARY KEY (uid) );操作示例与 MySQL 版本几乎一模一样唯一变化是导入的驱动包改为_ github.com/mattn/go-sqlite3且sql.Open采用 SQLite 方式打开数据文件为本地文件./foo.dbdb, err : sql.Open(sqlite3, ./foo.db)查询时created字段可直接扫描为time.Time类型。仓库中 de/code/src/apps/ch.5.3/schema.sql 提供了对应建表语句完整可运行代码位于同目录的main.go。SQLite 管理工具如 SQLiteAdmin可以方便地新建和管理数据库。PostgreSQL面向企业级应用的对象-关系数据库PostgreSQL 是一个自由的对象-关系数据库服务器在灵活的 BSD 风格许可证下发行是对 MySQL、Firebird 等开源系统以及 Oracle、Sybase、IBM DB2、Microsoft SQL Server 等专有系统的一种选择。与 MySQL 相比PostgreSQL 更加庞大因为它是为替代 Oracle 而设计的因此在企业应用中采用 PostgreSQL 是常见选择。原书还提到MySQL 被 Oracle 收购后逐步封闭自 MySQL 5.5.31 以后的所有版本不再遵循 GPL 协议这也是部分团队转向 PostgreSQL 的原因。Go 实现的支持 PostgreSQL 的驱动同样很多lib/pq支持database/sql纯 Go 编写使用人数最多、GitHub 上比较活跃示例采用此驱动jbarham/gopgsqldriver支持database/sql纯 Go 编写lxn/go-pgsql支持database/sql纯 Go 编写。建表语句CREATE TABLE userinfo ( uid serial NOT NULL, username character varying(100) NOT NULL, department character varying(500) NOT NULL, Created date, CONSTRAINT userinfo_pkey PRIMARY KEY (uid) ) WITH (OIDSFALSE); CREATE TABLE userdetail ( uid integer, intro character varying(100), profile character varying(100) ) WITH(OIDSFALSE);CRUD 示例的关键差异点完整代码见使用 PostgreSQL 数据库db, err : sql.Open(postgres, userastaxie passwordastaxie dbnametest sslmodedisable) // 插入使用 $1、$2 占位符并通过 RETURNING uid 取回自增主键 stmt, err : db.Prepare(INSERT INTO userinfo(username,department,created) VALUES($1,$2,$3) RETURNING uid) var lastInsertId int err db.QueryRow(INSERT INTO userinfo(username,departname,created) VALUES($1,$2,$3) returning uid;, astaxie, 研发部门, 2012-12-09).Scan(lastInsertId) fmt.Println(最后插入id , lastInsertId)与 MySQL 相比PostgreSQL 有两个明显差异务必注意参数占位符不同PostgreSQL 通过$1、$2方式指定传入参数而不是 MySQL 的?DSN 格式不同PostgreSQL 的 DSN 采用user... password... dbname... sslmode...的键值对形式与 MySQL 驱动不同不支持LastInsertIdPostgreSQL 内部没有实现类似 MySQL 的自增 ID 返回需要通过RETURNING uidQueryRow().Scan()的方式取回新插入记录的主键。仓库中 de/code/src/apps/ch.5.4/schema.sql 提供了 PostgreSQL 建表语句。三个驱动的横向对比对比维度MySQLSQLitePostgreSQL推荐驱动go-sql-driver/mysqlmattn/go-sqlite3lib/pq实现方式纯 Gocgo纯 Go占位符??$1、$2…DSN 示例user:passtcp(host:3306)/db?charsetutf8./foo.db本地文件user... password... dbname... sslmodedisable自增主键回取res.LastInsertId()res.LastInsertId()RETURNING uidQueryRow().Scan()适用场景传统 Web 后端存储嵌入式、单机、零配置企业级、高可靠应用beego orm基于 database/sql 的 Go Style ORM 库beego orm 是原书作者基于database/sql标准接口开发的 ORM 库采用 Go 风格对数据库进行操作实现了 struct 到数据表记录的映射。它是一个轻量级的 Go ORM 框架开发初衷是降低复杂 ORM 的学习曲线在运行效率与功能之间寻求平衡。由于 beego orm 支持database/sql标准接口理论上只要数据库驱动支持database/sql即可无缝接入。书中作者测试过的驱动包括github.com/go-sql-driver/mysqlMySQL、github.com/lib/pqPostgreSQL、github.com/mattn/go-sqlite3SQLite、github.com/ziutek/mymysql/godrvMySQL当时暂未支持的包括 go-mssqldbMsSQL、go-adodbMS ADODB、go-oci8Oracle、mgodbcODBC等。安装与初始化beego orm 支持go get方式安装go get github.com/astaxie/beego初始化需要依次完成导入驱动包、注册驱动、注册默认数据库、注册 model、创建表import ( database/sql github.com/astaxie/beego/orm _ github.com/go-sql-driver/mysql ) func init() { // 注册驱动 orm.RegisterDriver(mysql, orm.DRMySQL) // 设置默认数据库最后一个参数为最大空闲连接数 orm.RegisterDataBase(default, mysql, root:root/my_db?charsetutf8, 30) // 注册定义的 model orm.RegisterModel(new(User)) // 创建 table orm.RunSyncdb(default, false, true) }三种数据库的配置方式对比// PostgreSQL 配置 orm.RegisterDriver(postgres, orm.DR_Postgres) orm.RegisterDataBase(default, postgres, userpostgres passwordzxxx dbnametest host127.0.0.1 port5432 sslmodedisable) // MySQL 配置 orm.RegisterDriver(mysql, orm.DR_MySQL) orm.RegisterDataBase(default, mysql, root:zxxx/test?charsetutf8) // SQLite 配置 orm.RegisterDriver(sqlite, orm.DR_Sqlite) orm.RegisterDataBase(default, sqlite3, ./datas/test.db)连接池与调试配置// 按数据库别名设置最大空闲连接 orm.SetMaxIdleConns(default, 30) // 按数据库别名设置最大数据库连接go 1.2 orm.SetMaxOpenConns(default, 30) // 开启调试日志 orm.Debug trueModel 定义与关系映射beego orm 通过 struct tag 声明字段约束与表间关系type Userinfo struct { Uid int orm:PK // 表主键不是 id 时需显式加上 pk 注释 Username string Departname string Created time.Time } type User struct { Uid int orm:PK Name string Profile *Profile orm:rel(one) // 一对一关系 Post []*Post orm:reverse(many) // 一对多反向关系 } type Profile struct { Id int Age int16 User *User orm:reverse(one) // 一对一反向关系可选 } type Post struct { Id int Title string User *User orm:rel(fk) Tags []*Tag orm:rel(m2m) // 多对多关系 } type Tag struct { Id int Name string Posts []*Post orm:reverse(many) } func init() { orm.RegisterModel(new(Userinfo), new(User), new(Profile), new(Post), new(Tag)) }注意beego orm 对驼峰命名会自动转换成下划线字段。例如 struct 名为UserInfo底层实现时映射为user_info字段命名也遵循该规则。CRUD 操作插入数据——操作的是 struct 对象而非原生 SQLo : orm.NewOrm() var user User user.Name zxxx user.Departname zxxx id, err : o.Insert(user) if err nil { fmt.Println(id) } // 插入成功后 user.Uid 即为自增 ID批量插入——InsertMulti对应insert into table (name, age) values(...),(...),...形式的 SQL第一个参数bulk为并列插入数量第二个参数为对象 slice返回成功插入的数量users : []User{ {Name: slene}, {Name: astaxie}, {Name: unknown}, } successNums, err : o.InsertMulti(100, users) // bulk 为 1 时将顺序插入 slice 中的数据更新数据——主键有值时beego orm 会自动执行 update 而非插入o : orm.NewOrm() user : User{Uid: 1} if o.Read(user) nil { user.Name MyName if num, err : o.Update(user); err nil { fmt.Println(num) } } // Update 默认更新所有字段也可只更新指定字段 o.Update(user, Name) // o.Update(user, Field1, Field2, ...)Where用于设置条件支持多个参数第一个参数如果为整数相当于调用Where(主键?, 值)。查询数据——查询接口比较灵活// 例1根据主键获取数据 o : orm.NewOrm() user : User{Id: 1} err o.Read(user) if err orm.ErrNoRows { fmt.Println(查询不到) } else if err orm.ErrMissPK { fmt.Println(找不到主键) } else { fmt.Println(user.Id, user.Name) } // 例2QuerySeter 链式过滤 o : orm.NewOrm() var user User qs : o.QueryTable(user) // 返回 QuerySeter qs.Filter(id, 1) // WHERE id 1 qs.Filter(profile__age, 18) // WHERE profile.age 18 // 例3WHERE IN qs.Filter(profile__age__in, 18, 20) // WHERE profile.age IN (18, 20) // 例4更复杂的条件组合 qs.Filter(profile__age__in, 18, 20).Exclude(profile__lt, 1000) // WHERE profile.age IN (18, 20) AND NOT profile_id 1000 // 多行获取age17 的数据从第 20 条开始取 10 条 var allusers []User qs.Filter(profile__age__gt, 17) // WHERE profile.age 17 qs.Limit(10, 20) // LIMIT 10 OFFSET 20 —— 注意参数顺序与 SQL 相反删除数据——Delete操作会对反向关系进行处理。此例中 Post 拥有到 User 的外键删除 User 时若on_delete设置为默认的级联操作将删除对应的 Posto : orm.NewOrm() if num, err : o.Delete(User{Id: 1}); err nil { fmt.Println(num) }关联查询、GroupBy 与原生 SQL关联查询基于 struct 的 jointype Post struct { Id int orm:auto Title string orm:size(100) User *User orm:rel(fk) } var posts []*Post qs : o.QueryTable(post) num, err : qs.Filter(User__Name, slene).All(posts)排序与分组qs.OrderBy(id, -profile__age) // ORDER BY id ASC, profile.age DESC qs.OrderBy(-profile__age, profile) // ORDER BY profile.age DESC, profile_id ASC // GroupBy指定 group by 的字段Having指定 having 条件使用原生 SQL// 简单示例 o : orm.NewOrm() var r orm.RawSeter r o.Raw(UPDATE user SET name ? WHERE name ?, testing, slene) // 复杂原生 SQL rs o.Raw(SELECT * FROM user WHERE name? AND uid10 ORDER BY uid DESC LIMIT 100, name) num, err : rs.QueryRows(user)NoSQL 数据库操作Redis 与 MongoDBNoSQLNot Only SQL指非关系型数据库。随着 Web 2.0 兴起传统关系数据库在应付超大规模和高并发的 SNS 类纯动态网站时已力不从心而非关系型数据库因其自身特点得到迅速发展。Go 对 NoSQL 的支持很好当时流行的 NoSQL 主要有 redis、mongoDB、Cassandra 和 Membase 等均以高性能、高并发读写著称。本章重点讲解 redis 和 mongoDB 的操作。Rediskey-value 存储系统Redis 是一个 key-value 存储系统与 Memcached 类似但它支持更多 value 类型string字符串、list链表、set集合和 zset有序集合。当时应用 Redis 最广泛的包括新浪微博平台以及 Facebook 收购的图片社交网站 Instagram。Go 支持 Redis 的驱动有github.com/gomodule/redigo推荐、github.com/go-redis/redis、github.com/hoisie/redis、github.com/alphazero/Go-Redis、github.com/simonz05/godis。以 redigo 驱动为例通过连接池管理 Redis 连接package main import ( fmt os os/signal syscall time github.com/gomodule/redigo/redis ) var Pool *redis.Pool func init() { redisHost : :6379 Pool newPool(redisHost) close() } func newPool(server string) *redis.Pool { return redis.Pool{ MaxIdle: 3, // 最大空闲连接数 IdleTimeout: 240 * time.Second, // 空闲连接超时回收 Dial: func() (redis.Conn, error) { c, err : redis.Dial(tcp, server) if err ! nil { return nil, err } return c, err }, TestOnBorrow: func(c redis.Conn, t time.Time) error { // 借出连接前 PING 校验连接可用性 _, err : c.Do(PING) return err }, } } func close() { c : make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) signal.Notify(c, syscall.SIGTERM) signal.Notify(c, syscall.SIGKILL) go func() { -c Pool.Close() os.Exit(0) }() } func Get(key string) ([]byte, error) { conn : Pool.Get() defer conn.Close() var data []byte data, err : redis.Bytes(conn.Do(GET, key)) if err ! nil { return data, fmt.Errorf(error get key %s: %v, key, err) } return data, err } func main() { test, err : Get(test) fmt.Println(test, err) }此外原书作者还 fork 了astaxie/goredis修复了一些 bug曾用于其短域名服务项目中并演示了该驱动的 string/list 操作。从示例可以看出client的命令与 Redis 原生命令基本保持一致操作非常方便与原生态操作 Redis 非常类似var client goredis.Client client.Addr 127.0.0.1:6379 // 字符串操作 client.Set(a, []byte(hello)) val, _ : client.Get(a) fmt.Println(string(val)) client.Del(a) // list 操作 vals : []string{a, b, c, d, e} for _, v : range vals { client.Rpush(l, []byte(v)) } dbvals, _ : client.Lrange(l, 0, 4) for i, v : range dbvals { println(i, :, string(v)) } client.Del(l)MongoDB文档型 NoSQL 数据库MongoDB 是一个高性能、开源、无模式的文档型数据库介于关系数据库和非关系数据库之间是非关系数据库当中功能最丰富、最像关系数据库的产品。它支持的数据结构非常松散采用类似 JSON 的 bson 格式存储数据因此可以存储比较复杂的数据类型。MongoDB 最大的特点是其查询语言非常强大语法类似面向对象的查询语言几乎可以实现关系数据库单表查询的绝大部分功能还支持对数据建立索引。下图展示了 MySQL 与 MongoDB 之间的对应关系可以直观看出两者概念映射的便利性图 5.1 MongoDB 和 MySQL 的操作对比图当时 Go 支持 MongoDB 最好的驱动是 mgo最有希望成为官方 pkg 的候选驱动安装方式go get gopkg.in/mgo.v2操作示例package main import ( fmt log gopkg.in/mgo.v2 gopkg.in/mgo.v2/bson ) type Person struct { Name string Phone string } func main() { session, err : mgo.Dial(server1.example.com,server2.example.com) if err ! nil { panic(err) } defer session.Close() // 可选将会话切换为单调monotonic行为模式 session.SetMode(mgo.Monotonic, true) c : session.DB(test).C(people) err c.Insert(Person{Ale, 55 53 8116 9639}, Person{Cla, 55 53 8402 8510}) if err ! nil { log.Fatal(err) } result : Person{} err c.Find(bson.M{name: Ale}).One(result) if err ! nil { log.Fatal(err) } fmt.Println(Phone:, result.Phone) }可以看到mgo 的操作方式与 beego orm 几乎类似都是基于 struct 的操作方式——这正是 Go Style。小结从接口设计到数据存储的完整闭环通过第五章的学习可以梳理出 Go 数据库访问的完整知识链路标准接口先行database/sqldriver包定义了一套驱动契约sql.Register、Driver、Conn、Stmt、Tx、Result、Rows、Value等第三方驱动按标准实现即可接入业务代码因此具备跨数据库迁移能力关系型驱动即插即用MySQL、SQLite、PostgreSQL 的 CRUD 代码高度一致差异集中在驱动名、DSN 格式、参数占位符与自增主键回取方式ORM 提升开发效率beego orm 基于database/sql实现 struct ↔ 表记录映射支持关系定义、链式查询、批量操作与原生 SQL 混用NoSQL 补足扩展场景Rediskey-value与 MongoDB文档型以高性能、高并发读写能力支撑 Web 2.0 时代的应用需求。数据存储是 Web 应用最重要的一环深入理解database/sql的设计思想对后续会话管理、缓存、部署等章节的学习大有裨益。下一章将进入 session 和数据存储继续 Web 开发核心能力的构建。附原书还推荐了 Go database/sql tutorial 作为惯例用法与详细说明的延伸阅读本章内容对应源码可参考仓库 de/code/src/apps/ch.5.2、de/code/src/apps/ch.5.3、de/code/src/apps/ch.5.4 等目录下的main.go与schema.sql文件。赞分享文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载相关推荐用 Go 构建 Web 应用数据库开发实战指南——从 database/sql 接口到 SQL 驱动、beedb ORM 与 NoSQL用 Go 构建 Web 应用数据库开发实战指南——从 database/sql 接口到 SQL 驱动、beedb ORM 与 NoSQL 数据库是 Web 开文档教程Go Web 开发实战database/sql 接口、主流数据库驱动与 ORM 全解析Go Web 开发实战database/sql 接口、主流数据库驱动与 ORM 全解析 数据库是 Web 开发的核心基础设施。本指南基于开源电子书《Build文档教程Go 数据库开发全景build-web-application-with-golang 第 5 章 database/sql 接口、SQL 驱动、beedb ORM 与 NoSQL 实战指南Go 数据库开发全景build web application with golang 第 5 章 database/sql 接口、SQL 驱动、beedb文档教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考