Golang Mysql筆記(一)--- 連接與連接池

database/sql

database/sql是golang的標(biāo)準(zhǔn)庫(kù)之一姓迅,它提供了一系列接口方法,用于訪問(wèn)關(guān)系數(shù)據(jù)庫(kù)它掂。它并不會(huì)提供數(shù)據(jù)庫(kù)特有的方法棺弊,那些特有的方法交給數(shù)據(jù)庫(kù)驅(qū)動(dòng)去實(shí)現(xiàn)。

database/sql庫(kù)提供了一些type鱼喉。這些類型對(duì)掌握它的用法非常重要秀鞭。

****DB**** 數(shù)據(jù)庫(kù)對(duì)象。 sql.DB類型代表了數(shù)據(jù)庫(kù)扛禽。和其他語(yǔ)言不一樣锋边,它并是數(shù)據(jù)庫(kù)連接。golang中的連接來(lái)自內(nèi)部實(shí)現(xiàn)的連接池编曼,連接的建立是惰性的豆巨,當(dāng)你需要連接的時(shí)候,連接池會(huì)自動(dòng)幫你創(chuàng)建掐场。通常你不需要操作連接池往扔。一切都有g(shù)o來(lái)幫你完成。

****Results**** 結(jié)果集刻肄。數(shù)據(jù)庫(kù)查詢的時(shí)候瓤球,都會(huì)有結(jié)果集。sql.Rows類型表示查詢返回多行數(shù)據(jù)的結(jié)果集敏弃。sql.Row則表示單行查詢結(jié)果的結(jié)果集卦羡。當(dāng)然,對(duì)于插入更新和刪除麦到,返回的結(jié)果集類型為sql.Result绿饵。

****Statements**** 語(yǔ)句。sql.Stmt類型表示sql查詢語(yǔ)句瓶颠,例如DDL拟赊,DML等類似的sql語(yǔ)句〈饬埽可以把當(dāng)成prepare語(yǔ)句構(gòu)造查詢吸祟,也可以直接使用sql.DB的函數(shù)對(duì)其操作瑟慈。

warming up

下面就開(kāi)始我們的sql數(shù)據(jù)庫(kù)之旅,我們使用mysql數(shù)據(jù)庫(kù)為例子屋匕,驅(qū)動(dòng)使用go-sql-driver/mysql葛碧。

對(duì)于其他語(yǔ)言,查詢數(shù)據(jù)的時(shí)候需要?jiǎng)?chuàng)建一個(gè)連接过吻,對(duì)于go而言則是需要?jiǎng)?chuàng)建一個(gè)數(shù)據(jù)庫(kù)抽象對(duì)象进泼。連接將會(huì)在查詢需要的時(shí)候,由連接池創(chuàng)建并維護(hù)纤虽。使用sql.Open函數(shù)創(chuàng)建數(shù)據(jù)庫(kù)對(duì)象乳绕。它的第一個(gè)參數(shù)是數(shù)據(jù)庫(kù)驅(qū)動(dòng)名,第二個(gè)參數(shù)是一個(gè)連接字串(符合DSN風(fēng)格逼纸,可以是一個(gè)tcp連接洋措,一個(gè)unix socket等)。

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

func main() {
    db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/test?parseTime=true")
    if err != nil{
        log.Fatal(err)
    }
    defer db.Close()
}

創(chuàng)建了數(shù)據(jù)庫(kù)對(duì)象之后樊展,在函數(shù)退出的時(shí)候呻纹,需要釋放連接,即調(diào)用sql.Close方法专缠。例子使用了defer語(yǔ)句設(shè)置釋放連接雷酪。

接下來(lái)進(jìn)行一些基本的數(shù)據(jù)庫(kù)操作,首先我們使用Exec方法執(zhí)行一條sql涝婉,創(chuàng)建一個(gè)數(shù)據(jù)表:

func main() {
    db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/test?parseTime=true")
    if err != nil{
        log.Fatal(err)
    }
    defer db.Close()

    _, err = db.Exec("CREATE TABLE IF NOT EXISTS test.hello(world varchar(50))")
    if err != nil{
        log.Fatalln(err)
    }
}

此時(shí)可以看見(jiàn)哥力,數(shù)據(jù)庫(kù)生成了一個(gè)新的表。接下來(lái)再插入一些數(shù)據(jù)墩弯。

func main() {
    db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/test?parseTime=true")

    ...

    rs, err := db.Exec("INSERT INTO test.hello(world) VALUES ('hello world')")
    if err != nil{
        log.Fatalln(err)
    }
    rowCount, err := rs.RowsAffected()
    if err != nil{
        log.Fatalln(err)
    }
    log.Printf("inserted %d rows", rowCount)
}

同樣使用Exec方法即可插入數(shù)據(jù)吩跋,返回的結(jié)果集對(duì)象是是一個(gè)sql.Result類型,它有一個(gè)LastInsertId方法渔工,返回插入數(shù)據(jù)后的id锌钮。當(dāng)然此例的數(shù)據(jù)表并沒(méi)有id字段,就返回一個(gè)0.

插入了一些數(shù)據(jù)引矩,接下來(lái)再簡(jiǎn)單的查詢一下數(shù)據(jù):

func main() {
    db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/test?parseTime=true")
    
    ... 
    
    rows, err := db.Query("SELECT world FROM test.hello")
    if err != nil{
        log.Fatalln(err)
    }

    for rows.Next(){
        var s string
        err = rows.Scan(&s)
        if err !=nil{
            log.Fatalln(err)
        }
        log.Printf("found row containing %q", s)
    }
    rows.Close()
}

我們使用了Query方法執(zhí)行select查詢語(yǔ)句梁丘,返回的是一個(gè)sql.Rows類型的結(jié)果集。迭代后者的Next方法旺韭,然后使用Scan方法給變量s賦值氛谜,以便取出結(jié)果。最后再把結(jié)果集關(guān)閉(釋放連接)区端。

通過(guò)上面一個(gè)簡(jiǎn)單的例子值漫,介紹了database/sql的基本數(shù)據(jù)查詢操作。而對(duì)于開(kāi)篇所說(shuō)的幾個(gè)結(jié)構(gòu)類型尚未進(jìn)行詳細(xì)的介紹织盼。下面我們?cè)籴槍?duì)database/sql庫(kù)的類型和數(shù)據(jù)庫(kù)交互做更深的探究杨何。

sql.DB

正如上文所言酱塔,sql.DB是數(shù)據(jù)庫(kù)的抽象,雖然通常它容易被誤以為是數(shù)據(jù)庫(kù)連接晚吞。它提供了一些跟數(shù)據(jù)庫(kù)交互的函數(shù)延旧,同時(shí)管理維護(hù)一個(gè)數(shù)據(jù)庫(kù)連接池谋国,幫你處理了單調(diào)而重復(fù)的管理工作槽地,并且在多個(gè)goroutines也是十分安全。

sql.DB表示是數(shù)據(jù)庫(kù)抽象芦瘾,因此你有幾個(gè)數(shù)據(jù)庫(kù)就需要為每一個(gè)數(shù)據(jù)庫(kù)創(chuàng)建一個(gè)sql.DB對(duì)象捌蚊。因?yàn)樗S護(hù)了一個(gè)連接池,因此不需要頻繁的創(chuàng)建和銷毀近弟。它需要長(zhǎng)時(shí)間保持缅糟,因此最好是設(shè)置成一個(gè)全局變量以便其他代碼可以訪問(wèn)。

創(chuàng)建數(shù)據(jù)庫(kù)對(duì)象需要引入標(biāo)準(zhǔn)庫(kù)database/sql祷愉,同時(shí)還需要引入驅(qū)動(dòng)go-sql-driver/mysql窗宦。使用_表示引入驅(qū)動(dòng)的變量,這樣做的目的是為了在你的代碼中不至于和標(biāo)注庫(kù)的函數(shù)變量namespace沖突二鳄。

連接池

只用sql.Open函數(shù)創(chuàng)建連接池赴涵,可是此時(shí)只是初始化了連接池,并沒(méi)有創(chuàng)建任何連接订讼。連接創(chuàng)建都是惰性的髓窜,只有當(dāng)你真正使用到連接的時(shí)候,連接池才會(huì)創(chuàng)建連接欺殿。連接池很重要寄纵,它直接影響著你的程序行為。

連接池的工作原來(lái)卻相當(dāng)簡(jiǎn)單脖苏。當(dāng)你的函數(shù)(例如Exec程拭,Query)調(diào)用需要訪問(wèn)底層數(shù)據(jù)庫(kù)的時(shí)候,函數(shù)首先會(huì)向連接池請(qǐng)求一個(gè)連接棍潘。如果連接池有空閑的連接恃鞋,則返回給函數(shù)。否則連接池將會(huì)創(chuàng)建一個(gè)新的連接給函數(shù)蜒谤。一旦連接給了函數(shù)山宾,連接則歸屬于函數(shù)。函數(shù)執(zhí)行完畢后鳍徽,要不把連接所屬權(quán)歸還給連接池资锰,要么傳遞給下一個(gè)需要連接的(Rows)對(duì)象,最后使用完連接的對(duì)象也會(huì)把連接釋放回到連接池阶祭。

請(qǐng)求一個(gè)連接的函數(shù)有好幾種绷杜,執(zhí)行完畢處理連接的方式稍有差別直秆,大致如下:

  • db.Ping() 調(diào)用完畢后會(huì)馬上把連接返回給連接池。
  • db.Exec() 調(diào)用完畢后會(huì)馬上把連接返回給連接池鞭盟,但是它返回的Result對(duì)象還保留這連接的引用圾结,當(dāng)后面的代碼需要處理結(jié)果集的時(shí)候連接將會(huì)被重用。
  • db.Query() 調(diào)用完畢后會(huì)將連接傳遞給sql.Rows類型齿诉,當(dāng)然后者迭代完畢或者顯示的調(diào)用.Clonse()方法后筝野,連接將會(huì)被釋放回到連接池。
  • db.QueryRow()調(diào)用完畢后會(huì)將連接傳遞給sql.Row類型粤剧,當(dāng).Scan()方法調(diào)用之后把連接釋放回到連接池歇竟。
  • db.Begin() 調(diào)用完畢后將連接傳遞給sql.Tx類型對(duì)象,當(dāng).Commit()或.Rollback()方法調(diào)用后釋放連接抵恋。

因?yàn)槊恳粋€(gè)連接都是惰性創(chuàng)建的焕议,如何驗(yàn)證sql.Open調(diào)用之后,sql.DB對(duì)象可用呢弧关?通常使用db.Ping()方法初始化:

db, err := sql.Open("driverName", "dataSourceName")
if err != nil{
    log.Fatalln(err)
}

defer db.Close()

err = db.Ping()
if err != nil{
   log.Fatalln(err)
}

調(diào)用了Ping之后盅安,連接池一定會(huì)初始化一個(gè)數(shù)據(jù)庫(kù)連接。當(dāng)然世囊,實(shí)際上對(duì)于失敗的處理别瞭,應(yīng)該定義一個(gè)符合自己需要的方式,現(xiàn)在為了演示茸习,簡(jiǎn)單的使用log.Fatalln(err)表示了畜隶。

連接失敗

關(guān)于連接池另外一個(gè)知識(shí)點(diǎn)就是你不必檢查或者嘗試處理連接失敗的情況。當(dāng)你進(jìn)行數(shù)據(jù)庫(kù)操作的時(shí)候号胚,如果連接失敗了籽慢,database/sql會(huì)幫你處理。實(shí)際上猫胁,當(dāng)從連接池取出的連接斷開(kāi)的時(shí)候箱亿,database/sql會(huì)自動(dòng)嘗試重連10次。仍然無(wú)法重連的情況下會(huì)自動(dòng)從連接池再獲取一個(gè)或者新建另外一個(gè)弃秆。好比去買雞蛋届惋,售貨員會(huì)從箱子里掏出雞蛋,如果發(fā)現(xiàn)是壞蛋則連續(xù)掏10次菠赚,仍然找不到合適的就會(huì)換一個(gè)箱子招脑豹,或者從別的庫(kù)房再拿一個(gè)給你。

連接池配置

無(wú)論哪一個(gè)版本的go都不會(huì)提供很多控制連接池的接口衡查。知道1.2版本以后才有一些簡(jiǎn)單的配置瘩欺。可是1.2版本的連接池有一個(gè)bug,請(qǐng)升級(jí)更高的版本俱饿。

配置連接池有兩個(gè)的方法:

  • db.SetMaxOpenConns(n int) 設(shè)置打開(kāi)數(shù)據(jù)庫(kù)的最大連接數(shù)歌粥。包含正在使用的連接和連接池的連接。如果你的函數(shù)調(diào)用需要申請(qǐng)一個(gè)連接拍埠,并且連接池已經(jīng)沒(méi)有了連接或者連接數(shù)達(dá)到了最大連接數(shù)失驶。此時(shí)的函數(shù)調(diào)用將會(huì)被block,直到有可用的連接才會(huì)返回。設(shè)置這個(gè)值可以避免并發(fā)太高導(dǎo)致連接mysql出現(xiàn)too many connections的錯(cuò)誤。該函數(shù)的默認(rèn)設(shè)置是0,表示無(wú)限制。
  • db.SetMaxIdleConns(n int) 設(shè)置連接池中的保持連接的最大連接數(shù)范嘱。默認(rèn)也是0,表示連接池不會(huì)保持釋放會(huì)連接池中的連接的連接狀態(tài):即當(dāng)連接釋放回到連接池的時(shí)候若贮,連接將會(huì)被關(guān)閉奈应。這會(huì)導(dǎo)致連接再連接池中頻繁的關(guān)閉和創(chuàng)建。

對(duì)于連接池的使用依賴于你是如何配置連接池账磺,如果使用不當(dāng)會(huì)導(dǎo)致下面問(wèn)題:

  1. 大量的連接空閑芹敌,導(dǎo)致額外的工作和延遲。
  2. 連接數(shù)據(jù)庫(kù)的連接過(guò)多導(dǎo)致錯(cuò)誤垮抗。
  3. 連接阻塞氏捞。
  4. 連接池有超過(guò)十個(gè)或者更多的死連接,限制就是10次重連冒版。

大多數(shù)時(shí)候液茎,如何使用sql.DB對(duì)連接的影響大過(guò)連接池配置的影響。這些具體問(wèn)題我們會(huì)再使用sql.DB的時(shí)候逐一介紹辞嗡。

掌握了database/sql關(guān)于數(shù)據(jù)庫(kù)連接池管理內(nèi)容捆等,下一步則是使用這些連接,進(jìn)行數(shù)據(jù)的交互操作啦续室。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末栋烤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子挺狰,更是在濱河造成了極大的恐慌明郭,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丰泊,死亡現(xiàn)場(chǎng)離奇詭異薯定,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)瞳购,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門话侄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人苛败,你說(shuō)我怎么就攤上這事满葛【恫荆” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵嘀韧,是天一觀的道長(zhǎng)篇亭。 經(jīng)常有香客問(wèn)我,道長(zhǎng)锄贷,這世上最難降的妖魔是什么译蒂? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮谊却,結(jié)果婚禮上柔昼,老公的妹妹穿的比我還像新娘。我一直安慰自己炎辨,他們只是感情好捕透,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著碴萧,像睡著了一般乙嘀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上破喻,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天虎谢,我揣著相機(jī)與錄音,去河邊找鬼曹质。 笑死婴噩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的羽德。 我是一名探鬼主播几莽,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼玩般!你這毒婦竟也來(lái)了银觅?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤坏为,失蹤者是張志新(化名)和其女友劉穎究驴,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體匀伏,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡洒忧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了够颠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片熙侍。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蛉抓,到底是詐尸還是另有隱情庆尘,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布巷送,位于F島的核電站驶忌,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏笑跛。R本人自食惡果不足惜付魔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望飞蹂。 院中可真熱鬧几苍,春花似錦、人聲如沸陈哑。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)芥颈。三九已至惠勒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間爬坑,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工涂臣, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盾计,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓赁遗,卻偏偏與公主長(zhǎng)得像署辉,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子岩四,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容