Go中鏈接數據庫的鏈接池:當你須要和數據庫通訊時,就會從鏈接池裏面取出一個鏈接,和數據庫交互。使用完的閒置的鏈接會回到鏈接池,等待下一次的調用。若是鏈接池裏面沒有閒置的鏈接,會自動建立一個新的鏈接出來。其中有一段:php
An sql.Row returns the connection when Scan() is called, sql.Rows returns either when Close() is called or all rows have been iterated over with Next(), and sql.Tx will return when Commit or Rollback() are called. If you forget to completely iterate an sql.Rows and you forget to Close it, that connection will never go back to the pool.golang
從上面能夠看到,sql.Row若是不遍歷完或者直接調用Close()方法,執行此次查詢的鏈接就會一直存在!當鏈接池裏的可用鏈接用光後,就開始建立新的鏈接。這就是爲何調用SetMaxOpenConns沒有用的緣由,由於這個函數只是設置鏈接池裏的鏈接數而已!若是由於不及時釋放鏈接而讓鏈接池幹掉了,仍是會不斷的建立新的鏈接,直到用光MySql全部的鏈接,報錯。明白之後,在全部調用DB.Query的函數里加上了:web
defer row.Close()
這樣查詢鏈接就能在函數結束或者異常的狀況下被關閉,就不會持續建立新的鏈接了。滿覺得這樣就能夠解決問題了,可是服務器運行了之後,過段時間仍然會出現相同的錯誤。在phpMyadmin裏的監控頁面,能夠看到程序運行之後MySql的鏈接數猛增。問題又變得無解了,只能從新一行行檢查代碼。
sql
Go中的函數能夠有多個返回值,使用下劃線能夠忽略不須要的返回值:
數據庫
_, err := m.DB.Query("sql")
程序中update和del之類的sql語句不須要返回值,就直接忽略了。猜測這樣也是無法釋放鏈接的,由於即便你不接受返回值,不表明這個變量就不存在了。也就是說返回的sql.Row仍是存在的,只是你沒有接收而已。沒接收,就更談不上釋放鏈接了,因此最後產生了大量的鏈接繼續報錯。回頭看看那篇文章,看到這麼一段:
服務器
Ping and Exec will release the connection right before returning, but the others will pass ownership of the connection to the result, whether that's an sql.Row, sql.Rows, or sql.Tx.app
也就是說Ping和Exec方法在調用完以後,會自動釋放鏈接。把代碼中全部不須要返回值的語句改爲由Exce方法執行,go run 一下,ok,鏈接數終於正常了!
函數
問題是解決了,總起來之後要注意一下的東西:
ui