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:%[email protected](%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,多次执行,不执行defer stat.Close()代码,也不会出现连接超过最大连接数的情况,说明连接数应该是有超时时间(经过验证和initDB里面的三个超时时间没关系)
测试case2:测试SetMaxOpenConns函数功能
步骤一:将最大连接数SetMaxOpenConns的参数设置为2,最大空闲连接数SetMaxIdleConns参数设置为0。在执行Exec会出现阻塞的情况,即如果要创建10个连接,目前最大连接数为2,则需要2的最大连接数空闲出来之后,才能给后一个连接使用。不会报错,只会阻塞。
测试case3:将最大连接数SetMaxOpenConns的参数设置为2,最大空闲连接数SetMaxIdleConns参数设置为0。调用execSql()时,可以看到每次打印的connection_id都不相同。
结论:如果不设置最大空闲连接数,则每次连接都创建新的connectid
测试case4:将最大连接数SetMaxOpenConns的参数设置为10,最大空闲连接数SetMaxIdleConns参数设置为2。每隔1s调用execSql()时,可以看到每次打印的connection_id相同的2个。如果调用过快,最大空闲连接数不够用,同样会创建新的连接。
结论:如果连接不用了,最大空闲连接数还有空闲,则放入最大空闲连接数,以备下次使用。
case4的测试结果:
综上:
错误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这种。或者没有泄露的情况下,同时连接数据库的操作是不是超过了数据库的最大连接数。
错误4:设置最大连接数为50000,超过mysql的最大连接数,则如果某一个mysql处理的慢,会堆在数据库中,后续进来的请求会全部变慢。导致超过最大连接数。
解决办法:将最大连接数设置小些,比如:50,如果此时进来80个请求,其他三十个则会堆在程序阻塞等待。
参考连接:https://blog.****.net/lanyang123456/article/details/101947421