golang學習之旅:使用go語言操作mysql資料庫

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

1.下載並匯入資料庫驅動包

官方不提供實現,先下載第三方的實現,點擊這裡查看各種各樣的實現版本。
這裡選擇了Go-MySQL-Driver這個實現。地址是:https://github.com/go-sql-driver/mysql/。

然後按照裡面的說明下載驅動包:

$ go get github.com/go-sql-driver/mysql

最後匯入包即可:

import "database/sql"import _ "github.com/go-sql-driver/mysql"

2.串連至資料庫

db, err := sql.Open("mysql", "root:root@/uestcbook")

3.執行查詢

(1)Exec 

result, err := db.Exec(    "INSERT INTO users (name, age) VALUES (?, ?)",    "gopher",    27,)

 

(2)Query 

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)QueryRow

var age int64row := db.QueryRow("SELECT age FROM users WHERE name = ?", name)err := row.Scan(&age)

 

 

(4)Prepared statements 

age := 27stmt, err := db.Prepare("SELECT name FROM users WHERE age = ?")if err != nil {    log.Fatal(err)}rows, err := stmt.Query(age)// process rows

 

 

4. 事務

tx, err := db.Begin()if err != nil {    log.Fatal(err)}

 

 

5. 各種方式效率分析

問題:db.exec和statement.exec和tx.exec的區別?

執行個體如下:

package mainimport (    "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

 

6. 深入內部分析原因分析

(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的內部主要的資料結構

 

361 次點擊  
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.