记一次mysql的preparedStatement使用超限问题

【现象&背景】

本服务是个为数据库的分库分表提供路由规则计算,sql过滤执行的中间服务。即上游将请求发给本服务,本服务根据分库分表规则将相应的sql执行发送给对应的分库、分表去执行,并整理结果返回给上游。

2016-07-14下午,上游流量切换后,mysql大量报出" Can‘t create more than max_prepared_stmt_count statements (current value: 16382)",mysql不能工作导致本层数据库路由服务不能工作,大量请求失败。

大概意思就是说,同一时间在mysqld上所有session中preparedStatement语句超过了mysql的限制,导致新建preparedStatement失败,产生错误。

临时解决:上游回退。

【原因分析】

  • 为什么会建立这么多的preparedStatement?

1)其中有个接口由于bug导致会对所有分库、分表进行全表sql执行操作,而每个sql执行前都会新建一个preparedStatement

2)上游切换流量后使用的新库扩大了分库数和分表数,导致一个请求对应分表数量成倍数增加,那一个请求对应preparedStatement数量也成倍数增加。

  • preparedStatement是为何物?

preparedStatement是在多次重复执行只用参数值不同的sql语句时,先发一个请求去mysql服务端将sql语句编译好,并将其预编译结果存储在preparedStatement对象中,以后便可以使用该对象高效地多次执行该语句而不用预编译,直接使用数据库的缓冲区,提高数据库访问的效率。

【preparedStatement】

  • 原理

上图为mysql查询执行过程,包括:

1、传输sql给数据库

2、服务器先检查查询缓存,如果命中,吉利返回结果;否则进入下一阶段

3、数据库验证&解析sql

4、计算执行计划

5、根据执行计划调用存储引擎的api执行查询

6、返回数据

在上面步骤中,第4步非常耗时。为了提高性能,数据库会缓存执行语句及其执行计划。但是,sql语句本身为key,执行计划为value,sql语句中只要有一个字节不一样就不能命中缓存。

因此,有了preparedStatement,能够提高在只有参数不一样的sql的缓存命中率。

preparedStatement创建的时候,会将参数化的语句发给数据库,进行语法检测和执行计划计算。sql语句被预编译并且存储在preparedStatement对象中,下次执行相同的sql语句时,数据库不会再进行预编译,而是直接使用数据库的缓冲区,提高数据库的访问效率。

  • 作用

1、代码可读性&可维护性

2、在批量执行时提高cache命中率,提高性能

3、提高安全性,防止sql注入

  • 适用场景

有大量的sql结构相同,只有数值不同的sql请求时。或者处于对写入的sql的安全考虑。

时间: 2024-12-05 06:18:30

记一次mysql的preparedStatement使用超限问题的相关文章

【夯实Mysql基础】记一次mysql语句的优化过程!

1. [事件起因] 今天在做项目的时候,发现提供给客户端的接口时间很慢,达到了2秒多,我第一时间,抓了接口,看了运行的sql,发现就是 2个sql慢,分别占了1秒多. 一个sql是 链接了5个表同时使用了 2个 order by和 1个limit的分页 sql. 一个sql是上一个sql的count(*),即链接了5个表,当然没有limit了(取总数). 2. [着手优化] 1)[优化思路] 第一条是 做client调用 service层的数据缓存 第二条就是 优化sql本身. 这里着重讲一下

记一次MySql入库后,文本出现乱码的问题

最近采用ADO.NET开发了一个工具,解析了一条如下的日志并入库(MySql) 2014-08-19 00:37:10 [INFO] roleName=♣丶伊诺,orderId=1408190037102121039,price=10.0,point=100 发现入库后的roleName中的♣显示为□乱码了,一开始以为是表的编码问题,确认后是utf-8编码木有问题,后来又排除了文件编码及代码中的字符串都没有乱码的问题,凭经验分析,可能是连接字符串的问题吧,后来把MySql的连接字符串中也加入了u

Type mismatch: cannot convert from java.sql.PreparedStatement to com.mysql.jdbc.PreparedStatement

Connection.prepareStatement()函数出错,提示: 这是因为引入的包不对头, import com.mysql.jdbc.PreparedStatement; import com.mysql.jdbc.Statement; 引入正确的包就没有这个错误了. import java.sql.PreparedStatement; import java.sql.Statement;

关于Mysql数据库longblob格式数据的插入com.mysql.jdbc.PreparedStatement.setBinaryStream(ILjava/io/InputStream;J)V问题分析

当数据库字段为blob类型时 ,我们如果使用PreparedStatement中的setBinaryStream(int,InputStream,int)方法需要注意 在向blob字段类型中插入数据时,要使用javaio的inputstream,读入文件. 而相反从blob字段中读出数据时,同样使用javaio的inputstream,再用javaio的outputstream写入文件. 同clob的示例中的问题 如果在设置字节流的地方不加类型转换的话,如下: stat.setBinaryStr

java.lang.AbstractMethodError: Method com/mysql/jdbc/PreparedStatement.isClosed()Z is abstract

在学习Mybatis时出现下面的错误 1 java.lang.AbstractMethodError: Method com/mysql/jdbc/PreparedStatement.isClosed()Z is abstract 查阅资料后发现是Mybatis和mysql版本兼容问题 现在使用 <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>or

记一次MySQL找回用户数据

事情经过 有天,我们公司外区的一个销售C说他8月3号以前的工作流记录找不到了.问清缘由,原来是更新了微信号(我们公司的工作流是基于企业微信开发的).经过分析,微信号和流程数据并没什么关系,所以初步得出结论:本来只需要更新微信号的,结果我们公司的流程系统管理员把用户先删除,再创建了新的用户. 解决过程 1.首先想到的是直接从定时备份数据里面找回原来的用户ID,结果发现系统只备份了十天的记录,而工作流系统上显示销售C只有8月3号以后的流程记录,距今已经40多天,从自动备份的数据里已经无法恢复. 2.

初码-Azure系列-记一次MySQL数据库向Azure的迁移

初码Azure系列文章目录 还在继续给客户迁移不同的系统到Azure,这一次是一个系统的MySQL数据库要迁移,将迁移过程记录一下 原系统环境 数据库版本:MySQL Community Edition 5.7 服务器环境:自建机房虚拟化环境,E7 4820 分配8核32G,主节点+非热备灾备节点 运维方式:每日夜间停止服务,ETL程序备份全库至灾备环境 数据规模:用户与业务表相关表,记录数据量在500万级别以内:日志表,记录数据量在5000万条以内,总数据存储量在50G Azure环境选择 考

记一次mysql数据恢复

确切的说更像是一次数据迁移. 背景介绍: 操作系统:Windows Server 2008 R2 数据库版本:MySQL 5.5 数据库的安装目录与数据文件目录不在同一个磁盘,数据文件所在的目录磁盘损坏.而后通过数据恢复工具恢复数据文件.前期研发的同事尝试启动恢复数据库,不成功,多轮尝试不成功后找到我. 1.得到同事给的数据文件 ibdata1,Mysql安装目录MySQL\MySQL Server 5.5.调整my.ini文件尝试启动数据库. 2.将mysql base dir 拷贝到英文目录

【夯实Mysql基础】记一次mysql语句的优化过程

1. [事件起因] 今天在做项目的时候,发现提供给客户端的接口时间很慢,达到了2秒多,我第一时间,抓了接口,看了运行的sql,发现就是 2个sql慢,分别占了1秒多. 一个sql是 链接了5个表同时使用了 2个 order by和 1个limit的分页 sql. 一个sql是上一个sql的count(*),即链接了5个表,当然没有limit了(取总数). 2. [着手优化] 1)[优化思路] 第一条是 做client调用 service层的数据缓存 第二条就是 优化sql本身. 这里着重讲一下