首页 > 数据库 >Mybatis PageHelper编译SQL引发的一次性能问题.18286262

Mybatis PageHelper编译SQL引发的一次性能问题.18286262

时间:2024-07-05 18:08:33浏览次数:22  
标签:语句 count 18286262 PageHelper 编译 SQL CPU

起源

最近一直在跟大佬们做公司项目的性能优化,我这种小卡乐咪基本上负责的就是慢接口优化,但实际上只有以下几种情况需要进行接口代码级别的改造:

  1. 循环查库、RPC
  2. 数据库设计不合理
  3. 业务流程太长,代码耦合性太高等

随着对接口分析的深入,我们越来越发现系统中有很多拖后腿的问题是与业务代码无关的,今天要分享的这个是关于MyBatis大名鼎鼎的第三方分页插件PageHelper在生成count语句时,若语句参数数量过大导致的性能问题。

初步发现问题

在对慢接口进行日志分析的时候,我们发现一些count语句日志的打印较上一行隔了很久,而从代码上来看,到上一行日志打印后,这中间没有做什么耗时操作。

img

一番排查后,我们发现这种慢的语句具有如下特性:

  1. 都是来自于分页查询中自动生成的count
  2. 都具有大量的参数(示例中是7000+个)

简单排查

虽然我们很怀疑这7000多个参数,这2000ms会不会花在网络IO或数据库查询上了,毕竟这种大量in的几乎都是全表查询,但冷静下来想一想,根据MyBatis打印SQL的一贯套路,可以推断出这个耗时肯定不是count语句实际执行产生的IO耗时或网络等待,因为在打印这行语句时SQL应该还没有执行。

插一嘴:由于我们表的数据量并不大,几十万级别,并且有覆盖in查询条件的索引可以利用,这大大减少了要扫描的页面数量,实际count语句执行只需要几十ms

在执行到该位置时观察CPU,当时几乎是单核心跑满的,所以当时一定在做一些CPU操作。

至此,已经开始怀疑是语句参数数量过大导致的SQL编译大量耗费CPU,但又考虑到一个现代的CPU不可能被这种简单的任务难住,于是决定jstack抓一下当时CPU究竟在干什么:

img

从堆栈上看,CPU当时肯定是在编译SQL没跑了,本地运行代码Debug到PageHelper中,也发现最终在getSmartCountSql中调用jSqlParser.parse来生成分页SQL时耗费了1到2s:

img

count之后,Mybatis自己也要编译这个具有大量in参数的SQL,而它只花了6ms,至此已经可以完全断定是PageHelper的性能问题了。

img

这种纯CPU的性能问题是不可扩展的,若你有一个四核心的CPU,CPU处理的总能力肯定只能在2s钟内执行完4个编译任务,假设有80个并发,这个编译时间只会越来越长。

官方issue中有两个人反馈了这个问题,但官方貌似持摆烂态度2

替换策略

联系大佬移除底层脚手架中的PageHelper,使用MyBatis内部的分页插件,性能回归正常

img

猜想

由于PageHelper内部使用的JSqlParser实现是CCJSqlParser,其是C语言实现的,怀疑是不是在参数量大时需要在native堆栈和JVM堆栈中大量传递数据导致的呢?目前不了解JNI,这个不敢妄下结论了。

标签:语句,count,18286262,PageHelper,编译,SQL,CPU
From: https://www.cnblogs.com/lilpig/p/18286380

相关文章

  • 在windows中把mysql8.0.3设置为允许外部ip访问
    在windows中把mysql8.0.3设置为允许外部ip访问要在Windows系统中设置MySQL8.0.3允许外部IP访问,你需要按照以下步骤操作:登录到MySQL首先,你需要使用管理员账号登录到MySQL:mysql-uroot-p输入密码后,你将进入MySQL命令行界面。2.更新用户权限接下来,你需要更新......
  • Sqlalchemy 连接SQL Server 登录失败
    实验系统环境Windows平台Sqlalchemy2.0.23Python3.10SQLServer2012aioodbc0.5.0问题详情sqlalchemy.exc.InterfaceError:(pyodbc.InterfaceError)('28000','[28000][Microsoft][ODBCDriver17forSQLServer][SQLServer]登录失败。该登录名来自不受信任的域,不......
  • 【Unity几种数据存储之间的区别】PlayerPrefs、Json、XML、二进制、SQLite数据存储之
    ......
  • Mysql之基本操作(库,表,用户管理)
    库中有表,表中有数据一、查看帮助信息点击查看代码mysql>helpcreate//help后面跟上具体命令可以查看帮助二、查看支持的字符集点击查看代码showcharset;//查看支持的字符集默认拉丁文字latin1utf8|UTF-8Unicode//阉割版的utf8mb4|UTF-8Unicod......
  • 面试必会之Mysql篇
    1.Mysql查询语句的书写顺序Select[distinct]<字段名称>from表1[<join类型>join表2on<join条件>]where<where条件>groupby<字段>having<having条件>orderby<排序字段>limit<起始偏移量,行数>2.Mysql查询语句的执行顺序(8)Sele......
  • linux(CentOS)搭建MySQL数据库--详细版
    1、MySQL数据库去mysql官网下载需要安装的mysql版本,我这里用的是mysql-5.7.36-linux-glibc2.12-x86_64.tar.gz将该压缩包放到/usr/local/mysql_store目录下,mysql_store目录需要自己手动创建,cd/usr/localmkdirmysql_store然后解压该压缩包,输入如下指令:tar-zxvfmy......
  • [Mysql]事务/隔离级别
    1.3事务在理解事务的概念之前,接触数据库系统的其他高级特性还言之过早。事务就是一组原子性的SQL查询,或者说一个独立的工作单元。如果数据库引擎能够成功地对数据库应用该组查询的全部语句,那么就执行该组查询。如果其中有任何一条语句因为崩溃或其他原因无法执行,那么所有的语句......
  • [Mysql]MVCC
    多版本并发控制MVCCMySQL的大多数事务型存储引擎实现的都不是简单的行级锁。基于提升并发性能的考虑,它们一般都同时实现了多版本并发控制(MVCC)。不仅是MySQL,包括Oracle、PostgreSQL等其他数据库系统也都实现了MVCC,但各自的实现机制不尽相同,因为MVCC没有一个统一的实现标准。可以......
  • openEuler下安装mysql
    环境:Os:openEuler2203sp41.1介质准备mysql-5.7.29-linux-glibc2.12-x86_64.tar.gz该介质可以从官网上进行下载 1.1创建mysql用户和用户组root@ecs-d7c2-0417412~]#groupaddmysqlroot@ecs-d7c2-0417412~]#useraddmysql-s/sbin/nologin-M-gmysql1.1下载解压二......
  • MySQL数据库
    1.0数据模型1.1第一种        MySQL客户端链接MySQL自带的客户端命令行mysql[-h127.0.0.1][-P3306]-uroot-p1.2第二种使用命令:mysql-uroot-proot-u:用户名-p:密码1.3第三种:使用客户端软件navicat和sqlyog等关系型数据库建立在关系建模基础上,......