1. 程式人生 > >Go連接MySql數據庫Error 1040: Too many connections錯誤解決

Go連接MySql數據庫Error 1040: Too many connections錯誤解決

next cal err 情況 為什麽 語句 就是 sel com


原文:https://my.oschina.net/waknow/blog/205654


摘要: 使用Go鏈接數據庫時,由於連接釋放不當會在一段時間以後產生too many connections的錯誤。因此需要適當的選擇函數和及時的釋放數據庫連接。

這幾天用Go寫了個簡陋的服務器,連接Mysql數據庫,提供api給其他程序調用來實現增刪改產等服務。Go的版本是1.2,使用的驅動是go-sql-driver/mysql。但是在有一定量的查詢結果以後,會出先too many connection的錯誤。

google了一下,很多文章都建議修改MySql的配置文件:my.ini。文章是這樣解釋的:MySql的默認連接數是100,當查詢數過多時,就會出現這個錯誤。所以把配置修改:

max_connections=1000

這個字段後面的數字就是MySql允許的連接數,改的大一些就會解決問題。於是在電腦上直接改成10000,重啟MySql。然後電腦就卡的要崩潰了。看了相關的文檔大約知道,這個參數是控制MySql建立的線程數的。改成10000就會有一萬個線程,電腦自然就會卡的。但是即使是這個樣子,在一定的時間後還是會出現too many connections這樣的錯誤,只是出現的時間會晚一些罷了。所以說這個也只是治標而不治本,根本沒有解決問題。

翻看Go的sql文檔,其中有個func (*DB) SetMaxOpenConns的函數,看名字是可以控制最大的連接數的。很開心的在程序裏設置了個可以接受的數字,然後編譯運行。問題仍然沒有解決,還是會報同樣的錯誤。只能通過不斷的重啟服務器來解決。一時真的不知道該怎麽解決了。甚至懷疑是不是使用的驅動包有問題。

無意間看到這篇文章Go‘s database/sql,文章裏解釋了Go中連接數據庫的連接池:當你需要和數據庫通信時,就會從連接池裏面取出一個連接,和數據庫交互。使用完的閑置的連接會回到連接池,等待下一次的調用。如果連接池裏面沒有閑置的連接,會自動創建一個新的連接出來。其中有一段:

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.

從上面可以看到,sql.Row如果不遍歷完或者直接調用Close()方法,執行這次查詢的連接就會一直存在!當連接池裏的可用連接用光後,就開始創建新的連接。這就是為什麽調用SetMaxOpenConns沒有用的原因,因為這個函數只是設置連接池裏的連接數而已!如果因為不及時釋放連接而讓連接池幹掉了,還是會不斷的創建新的連接,直到用光MySql所有的連接,報錯。明白以後,在所有調用DB.Query的函數裏加上了:

defer row.Close()

這樣查詢連接就能在函數結束或者異常的情況下被關閉,就不會持續創建新的連接了。滿以為這樣就可以解決問題了,但是服務器運行了以後,過段時間仍然會出現相同的錯誤。在phpMyadmin裏的監控頁面,可以看到程序運行以後MySql的連接數猛增。問題又變得無解了,只能重新一行行檢查代碼。

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.

也就是說Ping和Exec方法在調用完之後,會自動釋放連接。把代碼中所有不需要返回值的語句改成由Exce方法執行,go run 一下,ok,連接數終於正常了!

問題是解決了,總起來以後要註意一下的東西:

  • 程序連接數據庫會有連接泄漏的情況,需要及時釋放連接

  • Go sql包中的Query和QueryRow(@qgymje 在評論中提到,QueryRow通過調用Scan方法,會自動關閉連接的)兩個方法的連接不會自動釋放連接,只有在遍歷完結果或者調用close方法才會關閉連接

  • Go sql中的Ping和Exec方法在調用結束以後就會自動釋放連接

  • 忽略了函數的某個返回值不代表這個值就不存在了,如果該返回值需要close才會釋放資源,直接忽略了就會導致資源的泄漏。

  • 有close方法的變量,在使用後要及時調用該方法,釋放資源

Go連接MySql數據庫Error 1040: Too many connections錯誤解決