官方不提供實現,先下載第三方的實現,點擊這裏查看各類各樣的實現版本。
這裏選擇了Go-MySQL-Driver這個實現。地址是:https://github.com/go-sql-driver/mysql/。css
而後按照裏面的說明下載驅動包:mysql
$ go get github.com/go-sql-driver/mysql
最後導入包便可:git
import "database/sql" import _ "github.com/go-sql-driver/mysql"
db, err := sql.Open("mysql", "root:root@/uestcbook")
(1)Exec github
result, err := db.Exec( "INSERT INTO users (name, age) VALUES (?, ?)", "gopher", 27, )
(2)Query golang
rows, err := db.Query("SELECT name FROM users WHERE age = ?", age) if err != nil { log.Fatal(err) } for rows.Next() { var name string if err := rows.Scan(&name); err != nil { log.Fatal(err) } fmt.Printf("%s is %d\n", name, age) } if err := rows.Err(); err != nil { log.Fatal(err) }
(3)QueryRowsql
var age int64 row := db.QueryRow("SELECT age FROM users WHERE name = ?", name) err := row.Scan(&age)
(4)Prepared statements 數據庫
age := 27 stmt, err := db.Prepare("SELECT name FROM users WHERE age = ?") if err != nil { log.Fatal(err) } rows, err := stmt.Query(age) // process rows
tx, err := db.Begin() if err != nil { log.Fatal(err) }
問題:db.exec和statement.exec和tx.exec的區別?安全
實例以下:數據結構
package main import ( "strconv" "database/sql" _ "github.com/go-sql-driver/mysql" "fmt" "time" "log" ) var db = &sql.DB{} func init(){ db,_ = sql.Open("mysql", "root:root@/book") } func main() { insert() query() update() query() delete() } func update(){ //方式1 update start := time.Now() for i := 1001;i<=1100;i++{ db.Exec("UPdate user set age=? where uid=? ",i,i) } end := time.Now() fmt.Println("方式1 update total time:",end.Sub(start).Seconds()) //方式2 update start = time.Now() for i := 1101;i<=1200;i++{ stm,_ := db.Prepare("UPdate user set age=? where uid=? ") stm.Exec(i,i) stm.Close() } end = time.Now() fmt.Println("方式2 update total time:",end.Sub(start).Seconds()) //方式3 update start = time.Now() stm,_ := db.Prepare("UPdate user set age=? where uid=?") for i := 1201;i<=1300;i++{ stm.Exec(i,i) } stm.Close() end = time.Now() fmt.Println("方式3 update total time:",end.Sub(start).Seconds()) //方式4 update start = time.Now() tx,_ := db.Begin() for i := 1301;i<=1400;i++{ tx.Exec("UPdate user set age=? where uid=?",i,i) } tx.Commit() end = time.Now() fmt.Println("方式4 update total time:",end.Sub(start).Seconds()) //方式5 update start = time.Now() for i := 1401;i<=1500;i++{ tx,_ := db.Begin() tx.Exec("UPdate user set age=? where uid=?",i,i) tx.Commit() } end = time.Now() fmt.Println("方式5 update total time:",end.Sub(start).Seconds()) } func delete(){ //方式1 delete start := time.Now() for i := 1001;i<=1100;i++{ db.Exec("DELETE FROM USER WHERE uid=?",i) } end := time.Now() fmt.Println("方式1 delete total time:",end.Sub(start).Seconds()) //方式2 delete start = time.Now() for i := 1101;i<=1200;i++{ stm,_ := db.Prepare("DELETE FROM USER WHERE uid=?") stm.Exec(i) stm.Close() } end = time.Now() fmt.Println("方式2 delete total time:",end.Sub(start).Seconds()) //方式3 delete start = time.Now() stm,_ := db.Prepare("DELETE FROM USER WHERE uid=?") for i := 1201;i<=1300;i++{ stm.Exec(i) } stm.Close() end = time.Now() fmt.Println("方式3 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 USER WHERE uid=?",i) } tx.Commit() end = time.Now() fmt.Println("方式4 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 USER WHERE uid=?",i) tx.Commit() } end = time.Now() fmt.Println("方式5 delete total time:",end.Sub(start).Seconds()) } func query(){ //方式1 query start := time.Now() rows,_ := db.Query("SELECT uid,username FROM USER") defer rows.Close() for rows.Next(){ var name string var id int if err := rows.Scan(&id,&name); err != nil { log.Fatal(err) } //fmt.Printf("name:%s ,id:is %d\n", name, id) } end := time.Now() fmt.Println("方式1 query total time:",end.Sub(start).Seconds()) //方式2 query start = time.Now() stm,_ := db.Prepare("SELECT uid,username FROM USER") 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 { log.Fatal(err) } // fmt.Printf("name:%s ,id:is %d\n", name, id) } end = time.Now() fmt.Println("方式2 query total time:",end.Sub(start).Seconds()) //方式3 query start = time.Now() tx,_ := db.Begin() defer tx.Commit() rows,_ = tx.Query("SELECT uid,username FROM USER") defer rows.Close() for rows.Next(){ var name string var id int if err := rows.Scan(&id,&name); err != nil { log.Fatal(err) } //fmt.Printf("name:%s ,id:is %d\n", name, id) } end = time.Now() fmt.Println("方式3 query total time:",end.Sub(start).Seconds()) } func insert() { //方式1 insert //strconv,int轉string:strconv.Itoa(i) start := time.Now() for i := 1001;i<=1100;i++{ //每次循環內部都會去鏈接池獲取一個新的鏈接,效率低下 db.Exec("INSERT INTO user(uid,username,age) values(?,?,?)",i,"user"+strconv.Itoa(i),i-1000) } end := time.Now() fmt.Println("方式1 insert total time:",end.Sub(start).Seconds()) //方式2 insert start = time.Now() for i := 1101;i<=1200;i++{ //Prepare函數每次循環內部都會去鏈接池獲取一個新的鏈接,效率低下 stm,_ := db.Prepare("INSERT INTO user(uid,username,age) values(?,?,?)") stm.Exec(i,"user"+strconv.Itoa(i),i-1000) stm.Close() } end = time.Now() fmt.Println("方式2 insert total time:",end.Sub(start).Seconds()) //方式3 insert start = time.Now() stm,_ := db.Prepare("INSERT INTO user(uid,username,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("方式3 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 user(uid,username,age) values(?,?,?)",i,"user"+strconv.Itoa(i),i-1000) } //最後釋放tx內部的鏈接 tx.Commit() end = time.Now() fmt.Println("方式4 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 user(uid,username,age) values(?,?,?)",i,"user"+strconv.Itoa(i),i-1000) //Commit執行後鏈接也釋放了 tx.Commit() } end = time.Now() fmt.Println("方式5 insert total time:",end.Sub(start).Seconds()) }
程序輸出結果:併發
方式1 insert total time: 3.7952171
方式2 insert total time: 4.3162468
方式3 insert total time: 4.3392482
方式4 insert total time: 0.3970227
方式5 insert total time: 7.3894226
方式1 query total time: 0.0070004
方式2 query total time: 0.0100006
方式3 query total time: 0.0100006
方式1 update total time: 7.3394198
方式2 update total time: 7.8464488
方式3 update total time: 6.0053435
方式4 update total time: 0.6630379000000001
方式5 update total time: 4.5402597
方式1 query total time: 0.0070004
方式2 query total time: 0.0060004
方式3 query total time: 0.008000400000000001
方式1 delete total time: 3.8652211000000003
方式2 delete total time: 3.8582207
方式3 delete total time: 3.6972114
方式4 delete total time: 0.43202470000000004
方式5 delete total time: 3.7972172
(1)sql.Open("mysql", "username:pwd@/databasename")
功能:返回一個DB對象,DB對象對於多個goroutines併發使用是安全的,DB對象內部封裝了鏈接池。
實現:open函數並無建立鏈接,它只是驗證參數是否合法。而後開啓一個單獨goroutines去監聽是否須要創建新的鏈接,當有請求創建新鏈接時就建立新鏈接。
注意:open函數應該被調用一次,一般是不必close的。
(2)DB.Exec()
功能:執行不返回行(row)的查詢,好比INSERT,UPDATE,DELETE
實現:DB交給內部的exec方法負責查詢。exec會首先調用DB內部的conn方法從鏈接池裏面得到一個鏈接。而後檢查內部的driver.Conn實現了Execer接口沒有,若是實現了該接口,會調用Execer接口的Exec方法執行查詢;不然調用Conn接口的Prepare方法負責查詢。
(3)DB.Query()
功能:用於檢索(retrieval),好比SELECT
實現:DB交給內部的query方法負責查詢。query首先調用DB內部的conn方法從鏈接池裏面得到一個鏈接,而後調用內部的queryConn方法負責查詢。
(4)DB.QueryRow()
功能:用於返回單行的查詢
實現:轉交給DB.Query()查詢
(5)db.Prepare()
功能:返回一個Stmt。Stmt對象能夠執行Exec,Query,QueryRow等操做。
實現:DB交給內部的prepare方法負責查詢。prepare首先調用DB內部的conn方法從鏈接池裏面得到一個鏈接,而後調用driverConn的prepareLocked方法負責查詢。
Stmt相關方法:
st.Exec()
st.Query()
st.QueryRow()
st.Close()
(6)db.Begin()
功能:開啓事務,返回Tx對象。調用該方法後,這個TX就和指定的鏈接綁定在一塊兒了。一旦事務提交或者回滾,該事務綁定的鏈接就還給DB的鏈接池。
實現:DB交給內部的begin方法負責處理。begin首先調用DB內部的conn方法從鏈接池裏面得到一個鏈接,而後調用Conn接口的Begin方法得到一個TX。
TX相關方法:
//內部執行流程和上面那些差很少,只是沒有先去獲取鏈接的一步,由於這些操做是和TX關聯的,Tx創建的時候就和一個鏈接綁定了,因此這些操做內部共用一個TX內部的鏈接。
tx.Exec()
tx.Query()
tx.QueryRow()
tx.Prepare()
tx.Commit()
tx.Rollback()
tx.Stmt()//用於將一個已存在的statement和tx綁定在一塊兒。一個statement能夠不和tx關聯,好比db.Prepare()返回的statement就沒有和TX關聯。
例子:
updateMoney, err := db.Prepare("UPDATE balance SET money=money+? WHERE id=?") ... tx, err := db.Begin() ... res, err := tx.Stmt(updateMoney).Exec(123.45, 98293203)
(7)源碼中Stmt的定義
// Stmt is a prepared statement. Stmt is safe for concurrent use by multiple goroutines. type Stmt struct { // Immutable: db *DB // where we came from query string // that created the Stmt stickyErr error // if non-nil, this error is returned for all operations closemu sync.RWMutex // held exclusively during close, for read otherwise. // If in a transaction, else both nil: tx *Tx txsi *driverStmt mu sync.Mutex // protects the rest of the fields closed bool // css is a list of underlying driver statement interfaces // that are valid on particular connections. This is only // used if tx == nil and one is found that has idle // connections. If tx != nil, txsi is always used. css []connStmt }
(7)幾個主要struct的內部主要的數據結構
https://github.com/golang/go/wiki/SQLInterface