歡迎光臨
每天分享高質量文章

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

 

贊(0)

分享創造快樂