MyBatis-Plus 动态表名插件性能优化

MyBatis-Plus 官方提供「动态表名插件」

参考官方文档:https://baomidou.com/pages/2a45ff/#dynamictablenameinnerinterceptor

但压测下来,通过火焰图分析,此插件比较消耗CPU,对性能产生了一定的影响。

1
2
3
4
5
6
7
8
9
10
11
12
@Bean
@Override
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

Map<String, TableNameHandler> tableNameHandlers = Maps.newHashMap();
tableNameHandlers.put("foobars", (sql, tableName) -> tableName + DateFormatUtils.format(new Date(), "yyyyMMdd"));
DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor(tableNameHandlers);
interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);

return interceptor;
}

以上代码(只是样例)为默认动态表名插件的配置,即访问 foobars 表会,会自动替换成 foobars_当前日期 表名。

如果为写此博客的日期,表名则为 foobars_20230721

通过源码分析不难发现,每个SQL(无论是否包含动态表名的SQL)都需要解析SQL!!!

如果项目中大部分表不是动态表名的,那就非常得不偿失了。

带来的性能影响 远远大于 带来的收益。

优化后的「动态表名插件」

动态表名插件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class MyDynamicTableNameInnerInterceptor extends DynamicTableNameInnerInterceptor {

private final Set<String> mapperClassNames = Sets.newHashSet();

public MyDynamicTableNameInnerInterceptor() {
setTableNameHandlerMap(Maps.newHashMap());
}

public void addDynamicTable(String tableName, Class<? extends Mapper<?>> mapper, TableNameHandler tableNameHandler) {
mapperClassNames.add(mapper.getName());
getTableNameHandlerMap().put(tableName, tableNameHandler);
}

@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
for (String mapperClassName : mapperClassNames) {
// 通过mapperClassName来判断是否生效,非动态表名的Mapper不执行SQL解析
if (ms.getId().startsWith(mapperClassName)) {
super.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
}
}
}

@Override
public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
MappedStatement ms = mpSh.mappedStatement();
for (String mapperClassName : mapperClassNames) {
// 通过mapperClassName来判断是否生效,非动态表名的Mapper不执行SQL解析
if (ms.getId().startsWith(mapperClassName)) {
SqlCommandType sct = ms.getSqlCommandType();
if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
if (InterceptorIgnoreHelper.willIgnoreDynamicTableName(ms.getId())) return;
PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
mpBs.sql(this.changeTable(mpBs.sql()));
}
break;
}
}
}

}
Bean配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Bean
@Override
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = super.mybatisPlusInterceptor();

MyDynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new MyDynamicTableNameInnerInterceptor();
dynamicTableNameInnerInterceptor.addDynamicTable(
"foobars",
FoobarMapper.class,
(sql, tableName) -> tableName + DateFormatUtils.format(new Date(), "yyyyMMdd")
);
interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);

return interceptor;
}

可减少大量的SQL解析!!!