Go语言MySQL数据库操作

栏目: IT技术 · 发布时间: 6年前

内容简介: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/GoMySQL


2、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:xxxxxx@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操作时如果不释放连接,则效率比释放连接要高。


本文转自51CTO博客作者天山老妖S,

原文链接:http://blog.51cto.com/9291927/2344802



Go语言MySQL数据库操作

探探Gopher China 2019大会全面启动


Gopher China 2019  早鸟票仅剩有限名额,最后几天,大家抓紧啦~~


点击下方“ 阅读原文 ”即可报名



以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Web Analytics 2.0

Web Analytics 2.0

Avinash Kaushik / Sybex / 2009-10-26 / USD 39.99

The bestselling book Web Analytics: An Hour A Day was the first book in the analytics space to move beyond clickstream analysis. Web Analytics 2.0 will significantly evolve the approaches from the fir......一起来看看 《Web Analytics 2.0》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具