系统突然保存不了数据库,查看日志,有报错

Error: Can't create more than max_prepared_stmt_count statements (current value: 16382)

一脸蒙逼,从没见过该错误啊。

网文最多的解决方法

mysql> show global status like 'com_stmt%';
查看如下3个参数值:
Com_stmt_close             prepare语句关闭的次数
Com_stmt_execute           prepare语句执行的次数
Com_stmt_prepare           prepare语句创建的次数

请确认Com_stmt_close的值是否接近于Com_stmt_prepare。
实际情况是
Com_stmt_prepare=16382
Com_stmt_close=0

也不知道这个参数是做什么的,查看max_prepared_stmt_count的数值用如下命令

mysql> show global variables like 'max_prepared_stmt_count';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| max_prepared_stmt_count | 16382 |
+-------------------------+-------+
1 row in set (0.01 sec)

然后把max_prepared_stmt_count数值加大

mysql> set global max_prepared_stmt_count=1048576;

或是把写到mysql的配置文件里
max_prepared_stmt_count=1048576

把mysql重启,Com_stmt这些参数全都能归0,也是个临时解决方法

然后再试试程序,是没问题了。但这也不是个办法啊,总不能无限制扩大啊。刚刚解决了没两天,又报相同的错误了。

必须把这个max_prepared_stmt_count搞清楚才是正道。

Prepared Statement 预处理/预编译

刚开始以为max_prepared_stmt_count这个是程序里连接池无限制连接造成的。做了很多试验,发现Com_stmt_close永远是0

查了后才发现,prepared statement在MySQL4.1中新引进的。
预处理的优势:
(1)预处理sql能一定程度上防止sql注入
(2)sql预编译效率更高
(3)二进制包协议让sql预处理更加高效。

既然是预处理了这么多又不关闭,那是不是可以手工关闭。
查了资料是这么写的

qlString = updateSQLString;
conn.execute(sqlString).then(()=>{conn.unprepare(sqlString);resolve(sqlString);}).catch((err)=>{reject(err);});

每次都unprepare一下,那程序改动挺大的,也总感觉还没找到点子上。

继续检查问题

SELECT count(*) AS 重复数,
GROUP_CONCAT(STATEMENT_ID) as STATEMENT_IDS,
SQL_TEXT
FROM performance_schema.prepared_statements_instances
GROUP BY SQL_TEXT
ORDER BY 重复数;

发现重复最多的语句

DELETE FROM goods_bom WHERE part_id = ? AND goods_id = ? AND id NOT IN (0,0,0)
DELETE FROM goods_bom WHERE part_id = ? AND goods_id = ? AND id NOT IN (0,0,0,0)
DELETE FROM goods_bom WHERE part_id = ? AND goods_id = ? AND id NOT IN (83,84,85)
......

看到这里,这下有点明白预处理的作用了。就是把这些经常使用的sql语法校验啥的先做好存好,下次再调用,执行前的七七八八事情就不用做了,直接就可以调用执行了。
那问题是为什么会有这么多的相同语句,按道理是应该只有一条的。
程序里是这么写的

await mysql.execute(`DELETE FROM goods_bom WHERE part_id = ? AND goods_id = ? AND id NOT IN (${part.goods_bom.map(v => v.id || 0).join(',') || 0})`, [part.id, id]);

问题就出在IN这段,每次组织好,sql语句都不一样,导致预处理后的语句每次都不一样。这里这么写,主要是之前不知道IN里的占位符该怎么写。那就改IN里的内容,改来改去发现IN是一个大坑。

第一次改成如下:

await mysql.execute(`DELETE FROM goods_bom WHERE part_id = ? AND goods_id = ? AND id NOT IN (?)`, [part.id, id, part.goods_bom.length > 0 ? part.goods_bom.map(v => v.id || 0) : [0]]);

报错:

(node:34895) UnhandledPromiseRejectionWarning: Error: Truncated incorrect DOUBLE value: '["0"]'

按道理是没错的,把拼接后的sql输出看一下

console.log(mysql.format(`DELETE FROM goods_bom WHERE part_id = ? AND goods_id = ? AND id NOT IN (?)`, [part.id, id, part.goods_bom.length > 0 ? part.goods_bom.map(v => v.id || 0) : [0]]));

输出完全正确,没有错误。
再试试把execute改成query

await mysql.query(`DELETE FROM goods_bom WHERE part_id = ? AND goods_id = ? AND id NOT IN (?)`, [part.id, id, part.goods_bom.length > 0 ? part.goods_bom.map(v => v.id || 0) : [0]]);

也完全正确,没错。感觉好像是excute没有把参数正确的解析。

execute()和query()之间的区别:

  1. query是在node装SQL语句,而 execute 则是利用MySQL 的 PreparedStatement 机制来预编译SQL语句
  2. execute 的优势是数据库原生支持的预编译机制,性能更高
  3. query 的优势是更灵活,例如可以用??代替表名、字段、索引名;用?代替数据

想了好一会明白了,execute的时候,数组[1,2,3]会转义成’[1,2,3]‘传给mysql,然后mysql直接就把’[1,2,3]‘替换占位符?了。
query是前端调用,前端会把数组[1,2,3]会转义成(1,2,3)。
之前开发mysql,没细看文档,还以为查询数据用query,例如select;执行语句用execute,例如:update,insert,delete。现在才发现理解有错。query和execute是不是用预处理的区别。

总结

  • execute时,sql语句不要动态拼接,全用占位符
  • 执行sql遇到in时,就query吧,不要用execute,方便

mysql报错max_prepared_stmt_count的解决过程相关推荐

  1. 登录mysql报错2059_navicat连接mysql报错2059的解决方法

    navicat连接mysql报错2059的解决方法 发布时间:2020-12-21 10:27:37 来源:亿速云 阅读:82 作者:小新 栏目:数据库 这篇文章给大家分享的是有关navicat连接m ...

  2. 登录mysql报错2059,navicat连接mysql报错2059如何解决

    navicat连接mysql报错2059,如下图所示: 网上查询过后,发现这个错误出现的原因是在mysql8之前的版本中加密规则为mysql_native_password,而在mysql8以后的加密 ...

  3. navicat连接 mysql 报错1251如何解决

    mysql8.0版本连接navicat,出现连接错误,报1251 error,如下图 这是因为mysql8.0版本和5.0版本加密方式不一样,所以会报错 解决方法: 1.打开Win+R,输入cmd启动 ...

  4. mysql 报错 sql_mode=only_full_group_by 解决方法

    在服务器数据库查询使用了 GROUP BY 居然报出了 1055 - Expression #1 of SELECT list is not in GROUP BY clause and contai ...

  5. Navicat无法连接MySQL报错1251的解决方法

    日期:2023年2月10日 作者:Commas 签名:(ง •_•)ง 积跬步以致千里,积小流以成江海-- 注释:如果您觉得有所帮助,帮忙点个赞,也可以关注我,我们一起成长:如果有不对的地方,还望各位 ...

  6. MySQL报错端口3306解决方法

    安装MySQL时候回报错一些错误,比如文件中文名,但是比较最常见的就是端口号3306的占用. 在之前的MySQL安装:https://blog.csdn.net/qq_41879385/article ...

  7. 终端连接mysql是出现error 2003_远程连接MySQL报错ERROR 2003解决办法

    转自https://blog.csdn.net/hjwang1/article/details/51669223 问题代码代码 ERROR 2003 (HY000): Can't connect to ...

  8. mysql1846错误_远程连接MySQL报错ERROR 2003解决办法

    转自https://blog.csdn.net/hjwang1/article/details/51669223 问题代码代码 ERROR 2003 (HY000): Can't connect to ...

  9. mysql full group by_MySql报错only_full_group_by的解决办法

    前段时间我在一个新环境里部署程序时遇到MySql报错only_full_group_by,之前已经遇到过一次同样的问题,当时没有总结经验,导致这次解决时耗费了不少时间,这里把本次的处理过程进行记录总结 ...

最新文章

  1. 拯救你的久坐不起!用树莓派改造站立式办公桌:在随机时间升降,还有阻力检测功能...
  2. Epoll在LT和ET模式下的读写方式
  3. centos7 安装owncloud-10 自测部署安装
  4. Java中使用Jedis连接池连接Redis数据库流程
  5. CSDN如何居中对齐
  6. linux查看本机所有预设的系统变量,如何设置与查看Linux系统中的环境变量?
  7. c++详解【new和delete】
  8. redlock java_Redlock分布式锁
  9. 点到曲线的距离_如何从“圆锥”曲线到圆锥曲线
  10. python函数参数的作用是_python函数参数的不同
  11. Pytorch专题实战——数据转换(Dataset Transforms)
  12. 数据库关键字引起的,ORA-00904: TABLE.column: 标识符无效
  13. 凭实力蝉联第一!Flink 又双叒叕上榜啦
  14. android google地图点聚合样式修改
  15. 硬盘接口类型简洁区别及SCSI设备和SCSI磁盘的概念区别
  16. Telegram支付接口接入
  17. 三大跨境电商平台开店必备的材料
  18. python代码解释4个作用域_Python中作用域的深入讲解
  19. Keep It for Mac(专业笔记工具)
  20. 解读SM, SP和Warp

热门文章

  1. 开发一个 App 有多难?说出来你可能不信!
  2. java containsany_判断字符串中是否存在的几种方案:string.indexof、string.contains、list.contains、list.any几种方式效率对比...
  3. 为什么python注释不能中文_python注释中文
  4. 限制textarea文本域中输入字符个数(防粘贴)
  5. SpringBoot知识点总结-DX的笔记
  6. Penetration Testing ***测试
  7. 计算机组成原理——关于原码、补码、移码运算及浮点数运算的总结
  8. ascp下载ebi ncbi数据库大文件
  9. CSS中的 overflow 属性
  10. 微信新功能,刷脸就能绑卡!告别繁琐