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的测试结果:

golang 调用mysql 连接数泄露的问题以及最大连接数和最大空闲连接数解释

 

综上:

错误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个请求,其他三十个则会堆在程序阻塞等待。