• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            Fork me on GitHub
            隨筆 - 215  文章 - 13  trackbacks - 0
            <2017年4月>
            2627282930311
            2345678
            9101112131415
            16171819202122
            23242526272829
            30123456


            專注即時通訊及網游服務端編程
            ------------------------------------
            Openresty 官方模塊
            Openresty 標準模塊(Opm)
            Openresty 三方模塊
            ------------------------------------
            本博收藏大部分文章為轉載,并在文章開頭給出了原文出處,如有再轉,敬請保留相關信息,這是大家對原創作者勞動成果的自覺尊重!!如為您帶來不便,請于本博下留言,謝謝配合。

            常用鏈接

            留言簿(1)

            隨筆分類

            隨筆檔案

            相冊

            Awesome

            Blog

            Book

            GitHub

            Link

            搜索

            •  

            積分與排名

            • 積分 - 215511
            • 排名 - 118

            最新評論

            閱讀排行榜

            http://www.cnblogs.com/tsiangleo/p/4483657.html

            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 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
            復制代碼

             

             

            4. 事務

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

             

             

            5. 各種方式效率分析

            問題: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

             

            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的內部主要的數據結構

             

            參考資料

            https://github.com/golang/go/wiki/SQLInterface

            https://github.com/go-sql-driver/mysql/

            http://golang.org/pkg/database/sql/

            posted on 2017-04-18 14:11 思月行云 閱讀(923) 評論(1)  編輯 收藏 引用 所屬分類: Golang

            FeedBack:
            # re: Golang操作MySql數據庫 2017-04-18 16:29 思月行云
            select多個字段時的寫法:

            //查詢數據
            rows, err := db.Query("SELECT * FROM userinfo")
            checkErr(err)

            for rows.Next() {
            var uid int
            var username string
            var department string
            var created string
            err = rows.Scan(&uid, &username, &department, &created)
            checkErr(err)
            fmt.Println(uid)
            fmt.Println(username)
            fmt.Println(department)
            fmt.Println(created)
            }   回復  更多評論
              
            久久发布国产伦子伦精品| 无码8090精品久久一区| 日韩人妻无码精品久久免费一| 亚洲精品国精品久久99热一| 国产成人精品久久一区二区三区| 亚洲精品高清国产一久久| 久久久久亚洲国产| 国产AⅤ精品一区二区三区久久| 18岁日韩内射颜射午夜久久成人| 久久国产精品久久精品国产| 狠狠色丁香婷婷久久综合| 久久被窝电影亚洲爽爽爽| 久久久久久久97| 国产精品九九久久免费视频 | 久久亚洲中文字幕精品有坂深雪 | 亚洲国产精品无码久久一区二区| 久久精品视频网| 亚洲国产精品无码久久98| 久久精品成人| 成人精品一区二区久久| 成人妇女免费播放久久久| 亚洲级αV无码毛片久久精品| 日韩欧美亚洲综合久久影院Ds | 久久精品中文无码资源站| 久久99精品久久久久久野外| 精品久久无码中文字幕| 亚洲AV无码久久精品狠狠爱浪潮| 久久青青国产| 久久国产热这里只有精品| 久久被窝电影亚洲爽爽爽| 国产成人无码久久久精品一| 亚洲狠狠婷婷综合久久蜜芽| 麻豆精品久久久久久久99蜜桃| 国产成人无码精品久久久免费| 久久精品无码一区二区无码| 中文精品久久久久人妻不卡| 国产欧美久久久精品影院| 久久久久久精品无码人妻| 伊人色综合久久天天人手人婷 | 国产高清美女一级a毛片久久w| 97久久国产亚洲精品超碰热|