内容简介:登录MySQL数据库,创建数据库从示例结果看,执行SQL操作时如果不释放连接,则效率比释放连接要高。
Go语言开发(十八)、 Go 语言 MySQL 数据库操作
一、MySQL数据库驱动
1、MySQL数据库驱动简介
Go语言官方没有实现MySQL数据库驱动,常用的开源MySQL数据库驱动实现如下:
(1)Go MySQL Driver
Go MySQL Driver支持database/sql接口,全部采用Go语言实现。
官方网站:
https://github.com/go-sql-driver/mysql/
(2)MyMySQL
MyMySQL支持database/sql接口,也支持自定义的接口,全部采用Go语言实现。
官方网站:
https://github.com/ziutek/mymysql
(3)GoMySQL
GoMySQL不支持database/sql接口,采用自定义接口,全部采用Go语言实现。
官方网站:
https://github.com/Philio/GoMySQL2、Go-MySQL-Driver简介
Go-MySQL-Driver优点:
(1)维护比较好。
(2)完全支持database/sql接口。
(3)支持keepalive,保持长连接。
Go-MySQL-Driver安装如下:
go get github.com/go-sql-driver/mysql
导入包:
import "database/sql" import _ "github.com/go-sql-driver/mysql"
二、MySQL基本操作
1、MySQL数据库创建
登录MySQL数据库,创建数据库
create database student default character set utf8;
2、 sql 常用方法
func Open(driverName, dataSourceName string) (*DB, error)
driverName参数为数据库驱动名称。
dataSourceName是连接参数,参数格式如下:
user:password@tcp(host:port)/dbname?charset=utf8
func (db *DB) Prepare(query string) (*Stmt, error)
Prepare为后续查询或执行操作创建一个准备SQL
func (s *Stmt) Exec(args ...interface{}) (Result, error)
使用给定参数执行准备的SQL语句
func (s *Stmt) Query(args ...interface{}) (*Rows, error)
使用给定参数执行准备的SQL查询语句
func (db *DB) Exec(query string, args ...interface{}) (Result, error)
执行SQL操作,query为SQL语句,可以接收可变参数,用于填充SQL语句的某些字段值。
func (db *DB) Query(query string, args ...interface{}) (*Rows, error)
执行SQL查询操作,可以接收多个参数
3、MySQL常用操作
package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) func errorHandler(err error) { if err != nil { fmt.Println(err.Error()) } } var ( CREATE_TABLE = "CREATE TABLE student(" + "sid INT(10) NOT NULL AUTO_INCREMENT," + "sname VARCHAR(64) NULL DEFAULT NULL," + "age INT(10) DEFAULT NULL,PRIMARY KEY (sid))" + "ENGINE=InnoDB DEFAULT CHARSET=utf8;" ) // 建立数据库连接 func setupConnect() *sql.DB { db, err := sql.Open("mysql", "root:xxxxxx@tcp(118.24.159.133:3306)/student?charset=utf8") errorHandler(err) return db } // 创建表 func CreateTable(db *sql.DB, sql string) { _, err := db.Exec(sql) errorHandler(err) } var INSERT_DATA = `INSERT INTO student(sid,sname,age) VALUES(?,?,?);` // 插入数据 func Insert(db *sql.DB) { db.Exec(INSERT_DATA, 1, "唐僧", 30) } var UPDATE_DATA = `UPDATE student SET age=28 WHERE sname="唐僧";` // 修改数据 func Update(db *sql.DB) { db.Exec(UPDATE_DATA) } var DELETE_DATA = `DELETE FROM student WHERE age>=30` // 删除记录 func Delete(db *sql.DB) { db.Exec(DELETE_DATA) } var DELETE_TABLE = `DROP TABLE student;` // 删除表 func DeleteTable(db *sql.DB) { db.Exec(DELETE_TABLE) } var QUERY_DATA = `SELECT * FROM student;` // 查询数据 func Query(db *sql.DB) { rows, err := db.Query(QUERY_DATA) if err != nil { fmt.Println(err) } for rows.Next() { var name string var id int var age int if err := rows.Scan(&id, &name, &age); err != nil { fmt.Println(err) } fmt.Printf("%s is %d\n", name, age) } } func main() { // 建立数据连接 db := setupConnect() // 创建数据库表 CreateTable(db, CREATE_TABLE) // 插入数据 Insert(db) // 查询数据 Query(db) // 删除数据 Delete(db) // 插入数据 Insert(db) // 修改数据 Update(db) // 查询数据 Query(db) // 删除表 DeleteTable(db) // 关闭数据库连接 db.Close() }
三、MySQL事务操作
1、事务常用方法
func (db *DB) Begin() (*Tx, error)
开启事务,从连接池中取出一个 *TX
类型连接。使用TX类型连接可以进行回滚事务和提交事务。
func (tx *Tx) Commit() error
提交事务
func (tx *Tx) Rollback() error
回滚
func (tx *Tx) Exec(query string, args ...interface{}) (Result, error)
执行SQL操作
func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error)
执行SQL查询操作
2、事务示例
// 支持事务回滚机制的批量数据插入 func MultiInsert(db *sql.DB) { // 批量数据插入 tx, err := db.Begin() if err != nil { fmt.Println(err) } values := [][]interface{}{{2, "孙悟空", 500}, {3, "猪八戒", 200}, {4, "沙悟净", 100}} stmt, err := tx.Prepare("INSERT INTO student(sid,sname,age) VALUES(?,?,?);") for _, val := range values { _, err := stmt.Exec(val...) if err != nil { fmt.Printf("INSERT failed:%v", err) tx.Rollback() } } tx.Commit() }
四、MySQL操作的效率分析
1、sql接口效率分析
func sql.Open(driverName, dataSourceName string) (*DB, error)
sql.Open返回一个DB对象,DB对象对于多个goroutines并发使用是安全的,DB对象内部封装了连接池。Open函数并没有创建连接,只是验证参数是否合法,然后开启一个单独goroutine去监听是否需要建立新的连接,当有请求建立新连接时就创建新连接。
func (db *DB) Exec(query string, args ...interface{}) (Result, error)
执行不返回行(row)的查询,比如INSERT,UPDATE,DELETE
DB交给内部的exec方法负责查询。exec会首先调用DB内部的conn方法从连接池里面获得一个连接。然后检查内部的driver.Conn是否实现了Execer接口,如果实现了Execer接口,会调用Execer接口的Exec方法执行查询;否则调用Conn接口的Prepare方法负责查询。
func (db *DB) Query(query string, args ...interface{}) (*Rows, error)
用于查询,DB交给内部的query方法负责查询。query首先调用DB内部的conn方法从连接池里面获得一个连接,然后调用内部的queryConn方法负责查询。
func (db *DB) Prepare(query string) (*Stmt, error)
返回一个Stmt。Stmt对象可以执行Exec,Query,QueryRow等操作。DB交给内部的prepare方法负责查询。prepare首先调用DB内部的conn方法从连接池里面获得一个连接,然后调用driverConn的prepareLocked方法负责查询。
func (db *DB) Begin() (*Tx, error)
开启事务,返回Tx对象。调用Begin方法后,TX会与指定的连接绑定,一旦事务提交或者回滚,事务绑定的连接就还给DB的连接池。DB交给内部的begin方法负责处理。begin首先调用DB内部的conn方法从连接池里面获得一个连接,然后调用Conn接口的Begin方法获得一个TX。
进行MySQL数据库操作时,如果每次SQL操作都从DB对象的连接池中获取连接,则会在很大程度上损耗效率。因此,必须尽量在一个连接上执行SQL操作。
2、效率分析示例
package main import ( "database/sql" "fmt" "strconv" "time" _ "github.com/go-sql-driver/mysql" ) var db = &sql.DB{} func init() { db, _ = sql.Open("mysql", "root:123456@tcp(118.24.159.133:3306)/student?charset=utf8") CREATE_TABLE := "CREATE TABLE student(" + "sid INT(10) NOT NULL AUTO_INCREMENT," + "sname VARCHAR(64) NULL DEFAULT NULL," + "age INT(10) DEFAULT NULL,PRIMARY KEY (sid))" + "ENGINE=InnoDB DEFAULT CHARSET=utf8;" db.Exec(CREATE_TABLE) } func update() { //方式1 update start := time.Now() for i := 1001; i <= 1100; i++ { db.Exec("UPDATE student set age=? where sid=? ", i, i) } end := time.Now() fmt.Println("db.Exec update total time:", end.Sub(start).Seconds()) //方式2 update start = time.Now() for i := 1101; i <= 1200; i++ { stm, _ := db.Prepare("UPDATE student set age=? where sid=? ") stm.Exec(i, i) stm.Close() } end = time.Now() fmt.Println("db.Prepare 释放连接 update total time:", end.Sub(start).Seconds()) //方式3 update start = time.Now() stm, _ := db.Prepare("UPDATE student set age=? where sid=?") for i := 1201; i <= 1300; i++ { stm.Exec(i, i) } stm.Close() end = time.Now() fmt.Println("db.Prepare 不释放连接 update total time:", end.Sub(start).Seconds()) //方式4 update start = time.Now() tx, _ := db.Begin() for i := 1301; i <= 1400; i++ { tx.Exec("UPDATE student set age=? where sid=?", i, i) } tx.Commit() end = time.Now() fmt.Println("tx.Exec 不释放连接 update total time:", end.Sub(start).Seconds()) //方式5 update start = time.Now() for i := 1401; i <= 1500; i++ { tx, _ := db.Begin() tx.Exec("UPDATE student set age=? where sid=?", i, i) tx.Commit() } end = time.Now() fmt.Println("tx.Exec 释放连接 update total time:", end.Sub(start).Seconds()) } func delete() { //方式1 delete start := time.Now() for i := 1001; i <= 1100; i++ { db.Exec("DELETE FROM student WHERE sid=?", i) } end := time.Now() fmt.Println("db.Exec delete total time:", end.Sub(start).Seconds()) //方式2 delete start = time.Now() for i := 1101; i <= 1200; i++ { stm, _ := db.Prepare("DELETE FROM student WHERE sid=?") stm.Exec(i) stm.Close() } end = time.Now() fmt.Println("db.Prepare 释放连接 delete total time:", end.Sub(start).Seconds()) //方式3 delete start = time.Now() stm, _ := db.Prepare("DELETE FROM student WHERE sid=?") for i := 1201; i <= 1300; i++ { stm.Exec(i) } stm.Close() end = time.Now() fmt.Println("db.Prepare 不释放连接 delete total time:", end.Sub(start).Seconds()) //方式4 delete start = time.Now() tx, _ := db.Begin() for i := 1301; i <= 1400; i++ { tx.Exec("DELETE FROM student WHERE sid=?", i) } tx.Commit() end = time.Now() fmt.Println("tx.Exec 不释放连接 delete total time:", end.Sub(start).Seconds()) //方式5 delete start = time.Now() for i := 1401; i <= 1500; i++ { tx, _ := db.Begin() tx.Exec("DELETE FROM student WHERE sid=?", i) tx.Commit() } end = time.Now() fmt.Println("tx.Exec 释放连接 delete total time:", end.Sub(start).Seconds()) } func query() { //方式1 query start := time.Now() rows, _ := db.Query("SELECT sid,sname FROM student") defer rows.Close() for rows.Next() { var name string var id int if err := rows.Scan(&id, &name); err != nil { fmt.Println(err) } } end := time.Now() fmt.Println("db.Query query total time:", end.Sub(start).Seconds()) //方式2 query start = time.Now() stm, _ := db.Prepare("SELECT sid,sname FROM student") defer stm.Close() rows, _ = stm.Query() defer rows.Close() for rows.Next() { var name string var id int if err := rows.Scan(&id, &name); err != nil { fmt.Println(err) } } end = time.Now() fmt.Println("db.Prepare query total time:", end.Sub(start).Seconds()) //方式3 query start = time.Now() tx, _ := db.Begin() defer tx.Commit() rows, _ = tx.Query("SELECT sid,sname FROM student") defer rows.Close() for rows.Next() { var name string var id int if err := rows.Scan(&id, &name); err != nil { fmt.Println(err) } } end = time.Now() fmt.Println("tx.Query query total time:", end.Sub(start).Seconds()) } func insert() { //方式1 insert start := time.Now() for i := 1001; i <= 1100; i++ { //每次循环内部都会去连接池获取一个新的连接,效率低下 db.Exec("INSERT INTO student(sid,sname,age) values(?,?,?)", i, "student"+strconv.Itoa(i), i-1000) } end := time.Now() fmt.Println("db.Exec insert total time:", end.Sub(start).Seconds()) //方式2 insert start = time.Now() for i := 1101; i <= 1200; i++ { //Prepare函数每次循环内部都会去连接池获取一个新的连接,效率低下 stm, _ := db.Prepare("INSERT INTO student(sid,sname,age) values(?,?,?)") stm.Exec(i, "student"+strconv.Itoa(i), i-1000) stm.Close() } end = time.Now() fmt.Println("db.Prepare 释放连接 insert total time:", end.Sub(start).Seconds()) //方式3 insert start = time.Now() stm, _ := db.Prepare("INSERT INTO student(sid,sname,age) values(?,?,?)") for i := 1201; i <= 1300; i++ { //Exec内部并没有去获取连接,为什么效率还是低呢? stm.Exec(i, "user"+strconv.Itoa(i), i-1000) } stm.Close() end = time.Now() fmt.Println("db.Prepare 不释放连接 insert total time:", end.Sub(start).Seconds()) //方式4 insert start = time.Now() //Begin函数内部会去获取连接 tx, _ := db.Begin() for i := 1301; i <= 1400; i++ { //每次循环用的都是tx内部的连接,没有新建连接,效率高 tx.Exec("INSERT INTO student(sid,sname,age) values(?,?,?)", i, "user"+strconv.Itoa(i), i-1000) } //最后释放tx内部的连接 tx.Commit() end = time.Now() fmt.Println("tx.Exec 不释放连接 insert total time:", end.Sub(start).Seconds()) //方式5 insert start = time.Now() for i := 1401; i <= 1500; i++ { //Begin函数每次循环内部都会去连接池获取一个新的连接,效率低下 tx, _ := db.Begin() tx.Exec("INSERT INTO student(sid,sname,age) values(?,?,?)", i, "user"+strconv.Itoa(i), i-1000) //Commit执行后释放连接 tx.Commit() } end = time.Now() fmt.Println("tx.Exec 释放连接 insert total time:", end.Sub(start).Seconds()) } func main() { insert() query() update() query() delete() } // output: // db.Exec insert total time: 2.069104068 // db.Prepare 释放连接 insert total time: 1.869348813 // db.Prepare 不释放连接 insert total time: 1.447833105 // tx.Exec 不释放连接 insert total time: 1.098540307 // tx.Exec 释放连接 insert total time: 3.465670469 // db.Query query total time: 0.005803479 // db.Prepare query total time: 0.010966584 // tx.Query query total time: 0.011800843 // db.Exec update total time: 2.117122871 // db.Prepare 释放连接 update total time: 2.132430998 // db.Prepare 不释放连接 update total time: 1.523685366 // tx.Exec 不释放连接 update total time: 1.346163272 // tx.Exec 释放连接 update total time: 3.129312377 // db.Query query total time: 0.00848425 // db.Prepare query total time: 0.013472261 // tx.Query query total time: 0.012418198 // db.Exec delete total time: 2.100008271 // db.Prepare 释放连接 delete total time: 1.9821439490000001 // db.Prepare 不释放连接 delete total time: 1.429259466 // tx.Exec 不释放连接 delete total time: 1.103143464 // tx.Exec 释放连接 delete total time: 2.863670582
从示例结果看,执行SQL操作时如果不释放连接,则效率比释放连接要高。
以上所述就是小编给大家介绍的《Go语言开发(十八)、Go语言MySQL数据库操作》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Go 语言操作 MySQL 之 事务操作
- C语言的位操作
- C语言中点操作符(.)和箭头操作符(->)的不同之处
- Golang 语言之字符串操作
- R 语言:数据操作 dplyr 包
- C语言解析常用字符操作函数
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。