Mybatis-Plus的Sql性能分析日志打印

2021/12/11

前言

上次项目中引入Mybatis-Plus(名字时间太长了,后面简称MP),我习惯性在本地开启性能分析插件方便打印sql的信息,但是这一次居然找不到PerformanceInterceptor这个类,后面查看MP的更新日志发现,居然在3.2版本移除了PerformanceInterceptor,我们项目中引入的是3.4.0版本。

image-20211211161132263

MP的GIthub上release日志

其中提到建议使用p6spy替代,经过我一番研究我总结了三个打印sql日志的方法,特意来分享一下。

本项目代码已经上传至Github,点击这里

三种方法

另外!另外!另外!打印Sql日志对执行的性能影响还算挺大的,生产环境上可千万不要启用,我一般在本地Debug的时候开启,尤其是微服务的跨模块调用真的是太太太好用了,只需要调用方打断点,被调用方的话只需要把这个Sql打出来。如果数据不符合预期,则看一下控制台打印的Sql或者把Sql放到DataGrip里面跑一下看看。

在db里面创建一张方便测试的表,我这里是在test的数据库下创建一张t_user表附上DDL

create table t_user
(
    id          int unsigned primary key auto_increment comment 'pk',
    username    varchar(255) not null default '' comment '用户名',
    password    varchar(100) not null default '123456' comment '用户密码',
    gender      char         not null default '0' comment '性别',
    phone       varchar(30)  not null default '' comment '手机',
    create_time datetime     not null default CURRENT_TIMESTAMP comment '创建时间'
) comment '用户表';

提前导入🔗

<dependency>
  	<groupId>mysql</groupId>
  	<artifactId>mysql-connector-java</artifactId>
  	<version>8.0.19</version>
</dependency>
<dependency>
  	<groupId>com.baomidou</groupId>
  	<artifactId>mybatis-plus-boot-starter</artifactId>
  	<!-- 先随便导入一个版本 -->
  	<version>3.1.2</version>
</dependency>

然后创建一个BO

@Data
@TableName("t_user")
public class TUser {

    /**
     * pk
     */
    @TableId(type = IdType.AUTO)
    private Long id;

    /**
     * 模拟用户名字
     */
    private String username;

    /**
     * 模拟用户密码, 方便演示 明文存储
     */
    private String password;

    /**
     * 模拟用户性别
     */
    private Integer gender;

    /**
     * 模拟用户手机
     */
    private String phone;

    /**
     * 创建时间
     */
    private LocalDateTime createTime;

}

然后写一个Mapper

@Mapper
public interface TUserMapper extends BaseMapper<TUser> {
}

最后写一个Test执行类

public class MybatisSqlLogTest {

    /**
     * 偷懒, 用mapper来演示查询日志展示效果
     */
    @Resource
    private TUserMapper tUserMapper;

    @Test
    public void selectPrintLogTest() {
        LambdaQueryWrapper<TUser> query = new LambdaQueryWrapper<TUser>()
                .select(TUser::getId, TUser::getPhone, TUser::getGender)
                .eq(TUser::getGender, 0)
                .likeRight(TUser::getUsername, "小");
        TUser result = tUserMapper.selectOne(query);
        System.out.printf("查询到的id是%s, 电话是%s\n", result.getId(), result.getPhone());
    }
}

开启Mybatis的日志拼接功能

这个是mybatis就自带的功能,开启方法也非常简单,只需要在你的配置文件里面把下面这行加上即可,一般我们有多个配置文件,建议只在dev开启,那么这一行只需要加到dev的配置文件里面就好啦。

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

mybatis-plus开头的原因是因为MP也兼容mybatis的配置,顶层的key换成mybatis: 也可以生效。

最后测试类来执行一下(上面的准备工作有demo,翻上去看一下)

这个缺点就是,输出的日志并非执行日志,日志格式需要自己拼接。输出的结果如下

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@16132f21] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@1271323139 wrapping com.mysql.cj.jdbc.ConnectionImpl@4f59a516] will not be managed by Spring
==>  Preparing: SELECT id,phone,gender FROM t_user WHERE gender = ? AND username LIKE ? 
==> Parameters: 0(Integer), 小%(String)
<==    Columns: id, phone, gender
<==        Row: 1, 13333333333, 0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@16132f21]

你需要把含有==>的两行放到一些插件或者三方工具里面进行转换,例如我是用uTools里面的mybatisLog工具转换结果如下

image-20211211165040675

三方工具

这种虽然是原生,但是略显麻烦,不是很直观,如果是sql特别长,一次请求会话特别多,看了和没看一个样。

MP 3.2以下版本使用插件

使用MP在3.2以下版本提供的PerformanceInterceptor插件,这一种方式是我原来用的最多,也用的最顺手的,配置起来也非常方便,也可以很方便的做环境区分。

使用方法和MP其他插件例如分页插件一样,只需要创建一个bean,设置好输入然后放入ioc中即可,MP运行的时候就会使用你的bean完成相应的插件功能。

首先确认好你的MP版本是在3.2一下

<dependency>
  	<groupId>com.baomidou</groupId>
  	<artifactId>mybatis-plus-boot-starter</artifactId>
  	<!-- 确定版本号是3.2以下 -->
  	<version>3.1.2</version>
</dependency>

然后创建一个配置类, 然后在里面注入一个PerformanceInterceptor的bean

@Configuration
public class MybatisPlusSqlLogConfig {


    /**
     * 打印 sql,性能分析拦截器
     * 因为每次sql都需要做拦截, 会有性能损耗
     * 所以建议只在dev或者test环境下方便debug的时候查看
     * 如果多个环境 -> @Profile({"dev", "test"})
     * @return PerformanceInterceptor bean
     */
    @Bean
    @Profile("mp")
    public PerformanceInterceptor performanceInterceptor() {
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        // 设置格式化, 类似于datagrip的美化sql语句, 在控制台好看一些
        performanceInterceptor.setFormat(true);
        return performanceInterceptor;
    }
}

值得注意的是在@Bean后面可以再打一个注解@Profile("dev"),只要你的dev配置文件是application-dev.yml,那么这个bean只会在dev环境里面注入。在生产上你也不用担心这个会影响性能啦,因为我代码中是mp-表示的插件demo,所以我这里只在mp下生效。

最后测试类来执行一下(上面的准备工作有demo,翻上去看一下)

然后结果如下,这样的效果就非常的直观,并且还是红色字体

 Time:22 ms - ID:cn.someget.mybatis.sqllog.mapper.TUserMapper.selectOne
Execute SQL:
    SELECT
        id,
        phone,
        gender 
    FROM
        t_user 
    WHERE
        gender = 0 
        AND username LIKE '小%'

查询到的id是1, 电话是13333333333

image-20211211171904411

红色醒目字体

本来是非常好用的,可能是作者觉得这个对性能影响非常大,为了防止这个插件大家误用就在3.2版本中下架了….

MP 3.2及以上版本使用p6spy

那我已经明确知道这个是不适合上生产,但是我现在本地方便debug看,除了降低MP版本,那还有什么解决办法呢。作者也在更新日志中提到推荐使用p6spy

P6Spy是一个可以用来在应用程序中拦截和修改数据操作语句的开源框架,GitHub地址请点击这里。它的功能很强大,这里只介绍它满足sql性能分析和日志输出的需求。

首先先导入他的依赖,我这里随便导入一个版本,所有版本请点击这里

<dependency>
    <groupId>p6spy</groupId>
    <artifactId>p6spy</artifactId>
    <version>3.8.2</version>
</dependency>

然后再配置文件里面配置datasource连接的时候,更换驱动类名以及url

# 配置一下mysql的连接信息
spring:
  datasource:
#   driver-class-name: com.mysql.cj.jdbc.Driver 这是原来的
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
#   url: jdbc:mysql://${mysql.host} 这是原来的url头部
    url: jdbc:p6spy:mysql://${mysql.host}:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false&useSSL=false
    username: root
    password: 123456

最后在resources创建一个spy.properties的文件里面写入一些基本配置

#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger

# 格式化(不设置的话一大串阅读性不强, 这个是内置的格式器可以按照源码自己写一个然后配置)
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger

# 取消JDBC的url前缀
useprefix=true

p.s. 注意p6spy只需要导包和在配置文件中配置即可,不需要编写代码

最后测试类来执行一下(上面的准备工作有demo,翻上去看一下)

 Consume Time:10 ms 1639215721188
 Execute SQL:SELECT id,phone,gender FROM t_user WHERE gender = 0 AND username LIKE '小%'

查询到的id是1, 电话是13333333333

image-20211211174242855

看起来也很直观,但是我翻了一下源码,好像没有自带格式化的配置类,如果大家有格式化需求,可以继承MessageFormattingStrategy自己照着P6SpyLogger写一个。

后言

一般基础组件,创建项目的时候就会选择一个稳定版开始搭建。后期上生产稳定以后一般都是锁版本的,我也是最近新项目引入MP才发现居然性能分析的插件已经被移除了,特意记录一下,给也习惯在本地开启sql日志输出的人提个醒。

最后这个本地debug看一下是真的爽,不能在生产环境打开哈。

(转载本站文章请注明作者和出处 没有气的汽水



┌┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┐
├ 文章已经完啦, 想要第一时间收到文章更新可以关注↓ ┤
└┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┘

Post Directory






下面是评论区,欢迎大家留言探讨或者指出错误哈