前两天线上狂报"Error 1461: Can't create more than max_prepared_stmt_count statements".
调完MySQL的max_prepared_stmt_count参数并重启部分服务才消停。
调整参数只是暂时解决问题,
根子在哪?还需要抽出时间排查代码和监控数据库,我初步怀疑是哪个业务场景同一时间突然产生大量的Prepare直至超过上限,
或者哪段代码有Bug,Prepare没有执行关闭。
在查这个问题相关资料过程中,发现有些文章带着问题再读,感觉收获还是不一样的。
比如猎豹移动技术博客有篇 <<
database/sql: Stmt的使用以及坑>> ,这里面写了他们在使用mysql时
碰到的Stmt的坑,也是造成了max_prepared_stmt_count超过最大值。然后他们通过分析代码找到原因,以及最后
Brad Fitzpatrick大神的修复处理,整个过程收获不少。
原来在go1.4之前,标准库中还存在这种问题,通常很难第一反应会是这方面的坑,不过这个问题已经修复,目前用的1.7,说明问题并不在这。
不过里面关于Prepare的说明,刷新了下我的概念,原来先声明Prepare再Exec与直接执行Exec:
stmt, err := tx.Prepare("sql")
stmt.Exec("args")
stmt.Close()
与
tx.Exec("sql","args")
这两种方式:
都会执行"dc.ci.Prepare",即其实都会生成Prepare,我之前都想当然认为后者不会生成Prepare.
这两者的代码实现区别在于:
tx.Papare 中,当"dc.ci.Prepare(query)"执行完后,会存到Stmt.txsi中,后续使用这个stmt来执行SQL就无须
再Prepare,减少了Prepare次数,所以除非有批量操作需求,否则,如果只是普通的单条SQL,两者效果是一样的,手工声明Prepare的方式还
白白多生成一个stmt。
还这篇<<Golang的垃圾回收>>,里面说到"Can't create more than max_prepared_stmt_count statements (current value: 16382)"这个错误造
成的原因是:大促,访问量过大,数据库访问的时候导致GC阈值变大,回收频率变低,内存不足造成的。
作者提到的GC的优化方式可以借鉴:
所以目前GC的优化方式原则就是尽可能少的声明临时变量:
局部变量尽量复用;
如果局部变量过多,可以把这些变量放到一个大结构体里面,这样扫描的时候可以只扫描一个变量,回收掉它包含的很多内存;
先记到这,还是要抽时间再扫下代码。
BLOG:http://blog.csdn.net/xcl168