golang 調用mysql 連接數泄露的問題以及最大連接數和最大空閒連接數解釋


1:golang mysql時,Prepare報錯:dial tcp 127.0.0.1:3306: getsockopt: connection refused' 
解決辦法:查看mysql初始化時候的用戶名密碼是否正確

2:mysql最大連接數和最大空閒連接數測試
測試程序:
package main

import (
  "fmt"
  "database/sql"
  _"github.com/go-sql-driver/mysql" //下劃線爲只引入,不調用其裏面的任何函數,用到了裏面的init函數進行驅動初始化
  "errors"
  "time"
)

var db *sql.DB
func initDB() (*sql.DB, error) {
    connectStr := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?timeout=%dms&readTimeout=%dms&writeTimeout=%dms&charset=utf8", "用戶名", "密碼", "hostip", 端口, "庫名", 1000, 500, 500)//後面三個分別爲連接超時,讀超時,寫超時
    db, err := sql.Open("mysql", connectStr)
    if err != nil {
        fmt.Println("open mysql err:", err)
        return nil, err
    }
    if db == nil {
        fmt.Println("mysql connection err:")
        return nil, errors.New("Mysql Connection error")
    }
    db.SetMaxOpenConns(20)
    db.SetMaxIdleConns(0)
    db.Ping()
    return db, nil
}

func execSql() {
        var connection_id int
        err := db.QueryRow("select CONNECTION_ID()").Scan(&connection_id)
        if err != nil {
                fmt.Println("query connection id failed:", err)
                return
        }

        fmt.Println("connection id:", connection_id)
}

func UpdateStatus(status int, length float64) error {
    stat, err := db.Prepare(fmt.Sprintf("update %s set `status` = ?, `length` = ?, `finish_time` = ? where `requestId` = ?", "saas_video_req_list"))
    fmt.Println("stat:", stat)
    if err != nil {
        fmt.Println("prepareerr:", err)
        return err
    }
    defer stat.Close()
    requestId := "8888888888888888888"
    _, errExec := stat.Exec(status, length, time.Now().Format("2006-01-02 15:04:05"), requestId)
    if errExec != nil {
        fmt.Println("execerr:", errExec)
        return errExec
    }
    fmt.Println(length)
    return nil
}

func main() {

    var err error
    if db, err = initDB(); err != nil {
        fmt.Println("init db err:", err)
    }

    for i:=0; i< 15000; i++ {
        go UpdateStatus(2, float64(i))
        //execSql()

        //time.Sleep(time.Second*1)
    }
    time.Sleep(time.Second*10)
}

測試case1:測試連接泄露的情況
步驟1:查看一下mysql中設置的最大連接數
mysql> show variables like '%max_connections%';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 10000 |
+-----------------+-------+
1 row in set (0.00 sec)

mysql>

可以看到最大連接數爲10000,所以將調用UpdateStatus函數的地方設置爲15000個,並且註釋掉函數中的defer stat.Close()一行,手動讓連接數泄露,即我打開連接之後不關閉。即便打開之後關閉,同時打開的也不能超過數據庫中的最大連接數,否則還是報如下信息。

可以看到在執行mysql建立新的連接的時候,會出現錯誤信息:
prepareerr: Error 1461: Can't create more than max_prepared_stmt_count statements (current value: 16382)
prepareerr: Error 1461: Can't create more than max_prepared_stmt_count statements (current value: 16382)
prepareerr: Error 1461: Can't create more than max_prepared_stmt_count statements (current value: 16382)
execerr: Error 1461: Can't create more than max_prepared_stmt_count statements (current value: 16382)
execerr: Error 1461: Can't create more than max_prepared_stmt_count statements (current value: 16382)
prepareerr: Error 1461: Can't create more than max_prepared_stmt_count statements (current value: 16382)
execerr: Error 1461: Can't create more than max_prepared_stmt_count statements (current value: 16382)

結論:1:在有連接泄露的情況下,如果當時同時連接的個數超過了數據庫中的最大連接數,則會出現再進來的mysql執行失敗的情況。很危險,如果線上同時出現的連接數超過的mysql設置的最大的連接數,則後面的mysql語句會執行失敗。
      2:將main函數中的for循環改成小於10000時,即使不執行defer stat.Close(),也不會出現上面的mysql的錯誤信息。因爲即便全不關,也不會超過10000,等超時時間(這個怎麼看超時時間?)後,會自動關閉。
      3:將for循環改成2000,最大連接數爲100(如果此值大於mysql的最大連接數,一樣會出現連接數過多的錯誤),多次執行,不執行defer stat.Close()代碼,也不會出現連接超過最大連接數的情況,說明連接數應該是會自動關閉,出了函數作用域或者程序結束的時候,連接會自動關閉。但是還是強烈建議手動關閉,因爲如果線上線程數和機器數比較多,同時達到了最大連接數,一樣會出事故(超時時間應該和initDB裏面設置的超時時間沒關係,超時時間的設置爲去鏈接數據庫的超時時間,參考鏈接:https://www.cnblogs.com/lanyangsh/p/11749270.html。)

測試case2:測試SetMaxOpenConns函數功能
步驟一:將最大連接數SetMaxOpenConns的參數設置爲2,最大空閒連接數SetMaxIdleConns參數設置爲0。在執行Exec會出現阻塞的情況,即如果要創建10個連接,目前最大連接數爲2,則需要2的最大連接數空閒出來之後,才能給後一個連接使用。不會報錯,只會阻塞。

例如如下代碼中:如果設置最大連接數爲1,第二個query語句會阻塞,第二個打印打印不出來,除非調研close先關閉第一個query

func querySql() {

    db.Query("select * from saas_video_req_list")

    fmt.Println("111111")

    _, err := db.Query("select * from saas_video_req_list") //此操作將一直阻塞

    fmt.Println("queryerr:", err)

}

但是調用Prepare卻不會??按道理Prepare也會佔有連接數。但是卻沒有阻塞?

     
測試case3:將最大連接數SetMaxOpenConns的參數設置爲2,最大空閒連接數SetMaxIdleConns參數設置爲0。調用execSql()時,可以看到每次打印的connection_id都不相同。

結論:如果不設置最大空閒連接數,則每次連接都創建新的connectid,可以通過打印的connection_id不同解釋該結論
      
測試case4:將最大連接數SetMaxOpenConns的參數設置爲10,最大空閒連接數SetMaxIdleConns參數設置爲2。每隔1s調用execSql()時,可以看到每次打印的connection_id相同的2個。如果調用過快,最大空閒連接數不夠用,同樣會創建新的連接。

結論:如果連接不用了,最大空閒連接數還有空閒,則放入最大空閒連接數,以備下次使用。

case4的測試結果:

 

綜上:

現象1:當程序中的最大連接數的值大於數據庫中最大連接數的值時,如果程序中存在沒有close連接的情況,程序執行一段時間後會出現最大連接數過多的錯誤。

現象2:當程序中的最大連接數的值小於於數據庫中最大連接數的值時,即使程序中存在沒有close連接的情況,也不會出現最大連接數過多的情況(應該是大於程序中設置的最大連接數的連接會自動釋放?)

1:程序中養成手動Close的習慣。即使全部Close,但是程序中同時連接數大於mysql的連接數,一樣出現最大連接數錯誤,解決參看下面一條。

2:設置程序中的最大連接數小於mysql的最大連接數。這樣連接數過多會阻塞在程序中,不會影響mysql。

3:設置最大空閒連接數,可以重複利用連接。

 

常見的幾種錯誤和解決辦法:

錯誤1:如果設置的最大連接數過大,有可能會報prepareerr: dial tcp 10.141.0.234:3306: socket: too many open files,表示你超過了機器可以創建的最大文件描述符,

解決辦法:使用ulimit -n 65535修改一下可以創建的最大文件描述符。

錯誤2:Error 1040: Too many connections這個錯誤,則是你程序設置的最大連接數超過了數據庫的最大連接數。

解決辦法:增大數據庫最大連接數,或者減少程序設置的最大連接數,一般程序中設置的最大連接數沒必要太大。

錯誤3:Error 1461: Can't create more than max_prepared_stmt_count statements (current value: 16382)

解決辦法:檢查程序中是否有連接泄露,比如prepare了沒有close這種。或者沒有泄露的情況下,同時連接數據庫的操作是不是超過了數據庫的最大連接數。


參考連接:https://blog.csdn.net/lanyang123456/article/details/101947421

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章