成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

關于Golang中database/sql包的學習筆記

elisa.yang / 2335人閱讀

摘要:上面代碼的過程為表示向數(shù)據(jù)庫發(fā)送一個,非常重要,遍歷使用,把遍歷到的數(shù)據(jù)存入變量使用遍歷完成后檢查。有幾點需要注意檢查遍歷是否有結(jié)果集未關閉前,底層的連接處于繁忙狀態(tài)。包有自動重試等功能。

  

因為最近在學習Go,所以找了revel這個框架來學習,感覺和php的面向?qū)ο笥泻艽蟛煌evel沒有提供db mapping的組件,所以在github上搜了很多ORM來學習,在jmoiron/sqlx中發(fā)現(xiàn)了一篇比較詳細介紹database/sql這個包的文章,拿來和大家分享。本文并不是按字句的翻譯,如果哪里表述不清楚建議閱讀原文 原文地址

概述

sql.DB不是一個連接,它是數(shù)據(jù)庫的抽象接口。它可以根據(jù)driver打開關閉數(shù)據(jù)庫連接,管理連接池。正在使用的連接被標記為繁忙,用完后回到連接池等待下次使用。所以,如果你沒有把連接釋放回連接池,會導致過多連接使系統(tǒng)資源耗盡。

使用DB 導入driver

這里使用的是MySQL drivers

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)
連接DB
func main() {
    db, err := sql.Open("mysql",
        "user:password@tcp(127.0.0.1:3306)/hello")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
}

sql.Open的第一個參數(shù)是driver名稱,第二個參數(shù)是driver連接數(shù)據(jù)庫的信息,各個driver可能不同。DB不是連接,并且只有當需要使用時才會創(chuàng)建連接,如果想立即驗證連接,需要用Ping()方法,如下:

err = db.Ping()
if err != nil {
    // do something here
}

sql.DB的設計就是用來作為長連接使用的。不要頻繁O(jiān)pen, Close。比較好的做法是,為每個不同的datastore建一個DB對象,保持這些對象Open。如果需要短連接,那么把DB作為參數(shù)傳入function,而不要在function中Open, Close。

讀取DB

如果方法包含Query,那么這個方法是用于查詢并返回rows的。其他情況應該用Exec()

var (
    id int
    name string
)
rows, err := db.Query("select id, name from users where id = ?", 1)
if err != nil {
    log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
    err := rows.Scan(&id, &name)
    if err != nil {
        log.Fatal(err)
    }
    log.Println(id, name)
}
err = rows.Err()
if err != nil {
    log.Fatal(err)
}

上面代碼的過程為:db.Query()表示向數(shù)據(jù)庫發(fā)送一個query,defer rows.Close()非常重要,遍歷rows使用rows.Next(), 把遍歷到的數(shù)據(jù)存入變量使用rows.Scan(), 遍歷完成后檢查error。有幾點需要注意:

檢查遍歷是否有error

結(jié)果集(rows)未關閉前,底層的連接處于繁忙狀態(tài)。當遍歷讀到最后一條記錄時,會發(fā)生一個內(nèi)部EOF錯誤,自動調(diào)用rows.Close(),但是如果提前退出循環(huán),rows不會關閉,連接不會回到連接池中,連接也不會關閉。所以手動關閉非常重要。rows.Close()可以多次調(diào)用,是無害操作。

單行Query

err在Scan后才產(chǎn)生,所以可以如下寫:

var name string
err = db.QueryRow("select name from users where id = ?", 1).Scan(&name)
if err != nil {
    log.Fatal(err)
}
fmt.Println(name)
修改數(shù)據(jù),事務

一般用Prepared Statements和Exec()完成INSERT, UPDATE, DELETE操作。

stmt, err := db.Prepare("INSERT INTO users(name) VALUES(?)")
if err != nil {
    log.Fatal(err)
}
res, err := stmt.Exec("Dolly")
if err != nil {
    log.Fatal(err)
}
lastId, err := res.LastInsertId()
if err != nil {
    log.Fatal(err)
}
rowCnt, err := res.RowsAffected()
if err != nil {
    log.Fatal(err)
}
log.Printf("ID = %d, affected = %d
", lastId, rowCnt)
事務

db.Begin()開始事務,Commit()Rollback()關閉事務。Tx從連接池中取出一個連接,在關閉之前都是使用這個連接。Tx不能和DB層的BEGIN, COMMIT混合使用。

如果你需要通過多條語句修改連接狀態(tài),你必須使用Tx,例如:

創(chuàng)建僅對單個連接可見的臨時表

設置變量,例如SET @var := somevalue

改變連接選項,例如字符集,超時

Prepared Statements Prepared Statements and Connection

在數(shù)據(jù)庫層面,Prepared Statements是和單個數(shù)據(jù)庫連接綁定的??蛻舳税l(fā)送一個有占位符的statement到服務端,服務器返回一個statement ID,然后客戶端發(fā)送ID和參數(shù)來執(zhí)行statement。

在GO中,連接不直接暴露,你不能為連接綁定statement,而是只能為DB或Tx綁定。database/sql包有自動重試等功能。當你生成一個Prepared Statement

自動在連接池中綁定到一個空閑連接

Stmt對象記住綁定了哪個連接

執(zhí)行Stmt時,嘗試使用該連接。如果不可用,例如連接被關閉或繁忙中,會自動re-prepare,綁定到另一個連接。

這就導致在高并發(fā)的場景,過度使用statement可能導致statement泄漏,statement持續(xù)重復prepare和re-prepare的過程,甚至會達到服務器端statement數(shù)量上限。

某些操作使用了PS,例如db.Query(sql, param1, param2), 并在最后自動關閉statement。

有些場景不適合用statement:

數(shù)據(jù)庫不支持。例如Sphinx,MemSQL。他們支持MySQL wire protocol, 但不支持"binary" protocol。

statement不需要重用很多次,并且有其他方法保證安全。例子

在Transaction中使用PS

PS在Tx中唯一綁定一個連接,不會re-prepare。

Tx和statement不能分離,在DB中創(chuàng)建的statement也不能在Tx中使用,因為他們必定不是使用同一個連接使用Tx必須十分小心,例如下面的代碼:

tx, err := db.Begin()
if err != nil {
    log.Fatal(err)
}
defer tx.Rollback()
stmt, err := tx.Prepare("INSERT INTO foo VALUES (?)")
if err != nil {
    log.Fatal(err)
}
defer stmt.Close() // danger!
for i := 0; i < 10; i++ {
    _, err = stmt.Exec(i)
    if err != nil {
        log.Fatal(err)
    }
}
err = tx.Commit()
if err != nil {
    log.Fatal(err)
}
// stmt.Close() runs here!

*sql.Tx一旦釋放,連接就回到連接池中,這里stmt在關閉時就無法找到連接。所以必須在Tx commit或rollback之前關閉statement。

處理Error 循環(huán)Rows的Error

如果循環(huán)中發(fā)生錯誤會自動運行rows.Close(),用rows.Err()接收這個錯誤,Close方法可以多次調(diào)用。循環(huán)之后判斷error是非常必要的。

for rows.Next() {
    // ...
}
if err = rows.Err(); err != nil {
    // handle the error here
}
關閉Resultsets時的error

如果你在rows遍歷結(jié)束之前退出循環(huán),必須手動關閉Resultset,并且接收error。

for rows.Next() {
    // ...
    break; // whoops, rows is not closed! memory leak...
}
// do the usual "if err = rows.Err()" [omitted here]...
// it"s always safe to [re?]close here:
if err = rows.Close(); err != nil {
    // but what should we do if there"s an error?
    log.Println(err)
}
QueryRow()的error
var name string
err = db.QueryRow("select name from users where id = ?", 1).Scan(&name)
if err != nil {
    log.Fatal(err)
}
fmt.Println(name)

如果id為1的不存在,err為sql.ErrNoRows,一般應用中不存在的情況都需要多帶帶處理。此外,Query返回的錯誤都會延遲到Scan被調(diào)用,所以應該寫成如下代碼:

var name string
err = db.QueryRow("select name from users where id = ?", 1).Scan(&name)
if err != nil {
    if err == sql.ErrNoRows {
        // there were no rows, but otherwise no error occurred
    } else {
        log.Fatal(err)
    }
}
fmt.Println(name)

把空結(jié)果當做Error處理是為了強行讓程序員處理結(jié)果為空的情況

分析數(shù)據(jù)庫Error

各個數(shù)據(jù)庫處理方式不太一樣,mysql為例:

if driverErr, ok := err.(*mysql.MySQLError); ok { 
    // Now the error number is accessible directly
    if driverErr.Number == 1045 {
        // Handle the permission-denied error
    }
}

MySQLError, Number都是DB特異的,別的數(shù)據(jù)庫可能是別的類型或字段。這里的數(shù)字可以替換為常量,例如這個包 MySQL error numbers maintained by VividCortex

連接錯誤 NULL值處理

簡單說就是設計數(shù)據(jù)庫的時候不要出現(xiàn)null,處理起來非常費力。Null的type很有限,例如沒有sql.NullUint64; null值沒有默認零值。

for rows.Next() {
    var s sql.NullString
    err := rows.Scan(&s)
    // check err
    if s.Valid {
       // use s.String
    } else {
       // NULL value
    }
}
未知Column

rows.Columns()的使用,用于處理不能得知結(jié)果字段個數(shù)或類型的情況,例如:

cols, err := rows.Columns()
if err != nil {
    // handle the error
} else {
    dest := []interface{}{ // Standard MySQL columns
        new(uint64), // id
        new(string), // host
        new(string), // user
        new(string), // db
        new(string), // command
        new(uint32), // time
        new(string), // state
        new(string), // info
    }
    if len(cols) == 11 {
        // Percona Server
    } else if len(cols) > 8 {
        // Handle this case
    }
    err = rows.Scan(dest...)
    // Work with the values in dest
}
cols, err := rows.Columns() // Remember to check err afterwards
vals := make([]interface{}, len(cols))
for i, _ := range cols {
    vals[i] = new(sql.RawBytes)
}
for rows.Next() {
    err = rows.Scan(vals...)
    // Now you can check each element of vals for nil-ness,
    // and you can use type introspection and type assertions
    // to fetch the column into a typed variable.
}
關于連接池

避免錯誤操作,例如LOCK TABLE后用 INSERT會死鎖,因為兩個操作不是同一個連接,insert的連接沒有table lock。

當需要連接,且連接池中沒有可用連接時,新的連接就會被創(chuàng)建。

默認沒有連接上限,你可以設置一個,但這可能會導致數(shù)據(jù)庫產(chǎn)生錯誤“too many connections”

db.SetMaxIdleConns(N)設置最大空閑連接數(shù)

db.SetMaxOpenConns(N)設置最大打開連接數(shù)

長時間保持空閑連接可能會導致db timeout

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/17475.html

相關文章

  • 開發(fā)了5年android,我開始了go學習之旅

    摘要:基于優(yōu)雅的語法和其強大的并發(fā)性,我開啟我的學習之旅。男女河南省商水縣等城鎮(zhèn)林村插入數(shù)據(jù)成功創(chuàng)建表創(chuàng)建表成功啦第二種方式總結(jié)今天的總結(jié)開發(fā)的冰山一角,接下來還需要學習很多。 奧術大師 做了近5年的android開發(fā),最近項目也是不怎么忙,空閑的時候總會思考一些事情,不過作為移動開發(fā),我個人覺得很有必要學習后臺開發(fā),由于公司是Go語言開發(fā)的,了解go語言一段時間后,我發(fā)現(xiàn)go語言的強大。基...

    JinB 評論0 收藏0
  • Golang數(shù)據(jù)庫編程之GORM庫入門

    摘要:在上一篇文章中我們講解了使用語言的標準庫包操作數(shù)據(jù)庫的過程,雖然使用包操作數(shù)據(jù)也是挺方便的,但是需要自己寫每一條語句,因此我們可能會自己再度進行封裝,以便更好地使用,而使用現(xiàn)有語言開源框架則是代替自己封裝的一個更好的方式。在上一篇文章中我們講解了使用Go語言的標準庫sql/database包操作數(shù)據(jù)庫的過程,雖然使用sql/database包操作數(shù)據(jù)也是挺方便的,但是需要自己寫每一條SQL語...

    番茄西紅柿 評論0 收藏0
  • Golang數(shù)據(jù)庫編程之GORM庫入門

    摘要:在上一篇文章中我們講解了使用語言的標準庫包操作數(shù)據(jù)庫的過程,雖然使用包操作數(shù)據(jù)也是挺方便的,但是需要自己寫每一條語句,因此我們可能會自己再度進行封裝,以便更好地使用,而使用現(xiàn)有語言開源框架則是代替自己封裝的一個更好的方式。在上一篇文章中我們講解了使用Go語言的標準庫sql/database包操作數(shù)據(jù)庫的過程,雖然使用sql/database包操作數(shù)據(jù)也是挺方便的,但是需要自己寫每一條SQL語...

    Fourierr 評論0 收藏0

發(fā)表評論

0條評論

elisa.yang

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<