Can’t create more than max_prepared_stmt_count statements (current value: 16382)
现网中出现这个错误,经过观察mysql的prepared_stmt_count参数并查看go源码,最后发现go数据库操作的sdk中有个bug。
- 打开mysql的general_log开关
show variables where Variable_name like "general_log%";
set global general_log=on;
这个开关打开可以记录mysql执行过的所有的sql语句,便于观察。
- 测试用例
func main() {
var e error
DB, e := sql.Open("mysql", "[use]:[passwd]@tcp(127.0.0.1:3306)/[db]")
if e != nil {
panic(e)
}
DB.SetMaxOpenConns(5)
DB.SetMaxIdleConns(1)
DB.Ping()
stm, e := DB.Prepare("select * from test where id = ?")
if e != nil {
return
}
for i:=0; i<10000;i++ {
go func() {
result, err := stm.Exec(i)
if err != nil {
}
fmt.Println(result)
}()
}
<- time.After(time.Duration(5 * time.Second))
fmt.Printf("++++++++++++++++++++==")
}
- 打开mysql日志文件
show variables where Variable_name like "general_log%";
上述语句可以看到文件位置。
tail -f /var/lib/mysql/xxx.log | grep Pre
这里只查看Prepare命令的语句。
- 执行上面的测试用例
发现会执行最大连接数个Prepare。
2018-05-07T11:22:04.294438Z 5529 Prepare select * from test where id = ?
2018-05-07T11:22:04.335158Z 5530 Prepare select * from test where id = ?
2018-05-07T11:22:04.364443Z 5531 Prepare select * from test where id = ?
2018-05-07T11:22:04.364444Z 5533 Prepare select * from test where id = ?
2018-05-07T11:22:04.364489Z 5532 Prepare select * from test where id = ?
-
问题分析
经过上面的测试发现问题,如果并发量特别大的时候,同一个sql语句会多次执行Prepare,这样就存在一个问题,mysql中记录Prepared语句的参数Prepared_stmt_count会一直增大直到达到最大值max_prepared_stmt_count,最后就会报本文第一行的错误。 -
源码分析
这里贴几个比较重要的方法出来分析一下。
// Prepare creates a prepared statement for later queries or executions.
// Multiple queries or executions may be run concurrently from the
// returned stat