Skip to content

Commit

Permalink
refactor: 增加SQL解析缓存,以提升性能
Browse files Browse the repository at this point in the history
之前除SqlServerDialect外,其他Dialect之前没有sql解析缓存.

优化前项目中每次此plugin的SQL解析基本在7/8到几十毫秒之间,有时比sql本身数据库执行还慢时!

经分析这个慢原因是其依赖的jsqlparser本身解析就慢,且在4.5时有反向优化(采用线程池却没每次重新线程!)
```
jad net.sf.jsqlparser.parser.CCJSqlParserUtil parseStatement
```

测试发现jsqlparser 4.7和pagehelper不兼容, 4.6 和4.5有同样的问题

增加SQL解析缓存后,重复访问的耗时基本0.5毫秒以下

*需要说明的这次重构目前只在mysql下测试,其他Dialect未经测试*
  • Loading branch information
qxo committed Apr 27, 2024
1 parent 30dd0f3 commit a200701
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 52 deletions.
158 changes: 128 additions & 30 deletions src/main/java/com/github/pagehelper/dialect/AbstractHelperDialect.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,15 @@

package com.github.pagehelper.dialect;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.pagehelper.Constant;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageRowBounds;
import com.github.pagehelper.cache.Cache;
import com.github.pagehelper.cache.CacheFactory;
import com.github.pagehelper.util.ExecutorUtil;
import com.github.pagehelper.util.MetaObjectUtil;
import com.github.pagehelper.util.StringUtil;
Expand All @@ -48,6 +53,16 @@
* @since 2016-12-04 14:32
*/
public abstract class AbstractHelperDialect extends AbstractDialect implements Constant {
/**
* Logger for this class.
*/
private static final Logger logger = LoggerFactory.getLogger(AbstractHelperDialect.class);

protected Cache<String, String> CACHE_COUNTSQL;
protected Cache<String, String> CACHE_PAGESQL;

public static boolean cacheOnFlag = true;// 临时性开关,为了方便切换,以验证缓存前后对比.
public static boolean tracingOn = false;// 临时性开关

/**
* 获取分页参数
Expand All @@ -61,7 +76,7 @@ public <T> Page<T> getLocalPage() {

@Override
public final boolean skip(MappedStatement ms, Object parameterObject, RowBounds rowBounds) {
//该方法不会被调用
// 该方法不会被调用
return true;
}

Expand All @@ -72,13 +87,48 @@ public boolean beforeCount(MappedStatement ms, Object parameterObject, RowBounds
}

@Override
public String getCountSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, RowBounds rowBounds, CacheKey countKey) {
public String getCountSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, RowBounds rowBounds,
CacheKey countKey) {
final long startTime = tracingOn || logger.isDebugEnabled() ? System.nanoTime() : 0;
if (startTime > 0) {
logger.info("getCountSql start ...");
}
Page<Object> page = getLocalPage();
String countColumn = page.getCountColumn();
final String sql = boundSql.getSql();
final String countSqlKey;
String cachedSql;
final boolean cacheOn = cacheOnFlag && CACHE_COUNTSQL != null;
if (StringUtil.isNotEmpty(countColumn)) {
return countSqlParser.getSmartCountSql(boundSql.getSql(), countColumn);
countSqlKey = sql + countColumn;
cachedSql = cacheOn ? CACHE_COUNTSQL.get(countSqlKey) : null;
if (cachedSql != null) {
logCountSqlEnd(startTime);
return cachedSql;
}
cachedSql = countSqlParser.getSmartCountSql(sql, countColumn);
} else {
countSqlKey = sql;
cachedSql = cacheOn ? CACHE_COUNTSQL.get(countSqlKey) : null;
if (cachedSql != null) {
logCountSqlEnd(startTime);
return cachedSql;
}
cachedSql = countSqlParser.getSmartCountSql(sql);
}
if (cacheOn) {
CACHE_COUNTSQL.put(countSqlKey, cachedSql);
}
logCountSqlEnd(startTime);
return cachedSql;
}

private void logCountSqlEnd(final long startTime) {
if (startTime > 0) {
final long time = System.nanoTime() - startTime;
logger.info("getCountSql(cacheOn={}) end: {}", cacheOnFlag,
Double.toString(time == 0 ? 0 : time / 1000000d));
}
return countSqlParser.getSmartCountSql(boundSql.getSql());
}

@Override
Expand All @@ -88,58 +138,61 @@ public boolean afterCount(long count, Object parameterObject, RowBounds rowBound
if (rowBounds instanceof PageRowBounds) {
((PageRowBounds) rowBounds).setTotal(count);
}
//pageSize < 0 的时候,不执行分页查询
//pageSize = 0 的时候,还需要执行后续查询,但是不会分页
// pageSize < 0 的时候,不执行分页查询
// pageSize = 0 的时候,还需要执行后续查询,但是不会分页
if (page.getPageSizeZero() != null) {
//PageSizeZero=false&&pageSize<=0
// PageSizeZero=false&&pageSize<=0
if (!page.getPageSizeZero() && page.getPageSize() <= 0) {
return false;
}
//PageSizeZero=true&&pageSize<0 返回 false,只有>=0才需要执行后续的
// PageSizeZero=true&&pageSize<0 返回 false,只有>=0才需要执行后续的
else if (page.getPageSizeZero() && page.getPageSize() < 0) {
return false;
}
}
//页码>0 && 开始行数<总行数即可,不需要考虑 pageSize(上面的 if 已经处理不符合要求的值了)
// 页码>0 && 开始行数<总行数即可,不需要考虑 pageSize(上面的 if 已经处理不符合要求的值了)
return page.getPageNum() > 0 && count > page.getStartRow();
}

@Override
public Object processParameterObject(MappedStatement ms, Object parameterObject, BoundSql boundSql, CacheKey pageKey) {
//处理参数
public Object processParameterObject(MappedStatement ms, Object parameterObject, BoundSql boundSql,
CacheKey pageKey) {
// 处理参数
Page page = getLocalPage();
//如果只是 order by 就不必处理参数
// 如果只是 order by 就不必处理参数
if (page.isOrderByOnly()) {
return parameterObject;
}
Map<String, Object> paramMap = null;
if (parameterObject == null) {
paramMap = new HashMap<String, Object>();
} else if (parameterObject instanceof Map) {
//解决不可变Map的情况
// 解决不可变Map的情况
paramMap = new HashMap<String, Object>();
paramMap.putAll((Map) parameterObject);
} else {
paramMap = new HashMap<String, Object>();
// sqlSource为ProviderSqlSource时,处理只有1个参数的情况
if (ms.getSqlSource() instanceof ProviderSqlSource) {
String[] providerMethodArgumentNames = ExecutorUtil.getProviderMethodArgumentNames((ProviderSqlSource) ms.getSqlSource());
String[] providerMethodArgumentNames = ExecutorUtil
.getProviderMethodArgumentNames((ProviderSqlSource) ms.getSqlSource());
if (providerMethodArgumentNames != null && providerMethodArgumentNames.length == 1) {
paramMap.put(providerMethodArgumentNames[0], parameterObject);
paramMap.put("param1", parameterObject);
}
}
//动态sql时的判断条件不会出现在ParameterMapping中,但是必须有,所以这里需要收集所有的getter属性
//TypeHandlerRegistry可以直接处理的会作为一个直接使用的对象进行处理
boolean hasTypeHandler = ms.getConfiguration().getTypeHandlerRegistry().hasTypeHandler(parameterObject.getClass());
// 动态sql时的判断条件不会出现在ParameterMapping中,但是必须有,所以这里需要收集所有的getter属性
// TypeHandlerRegistry可以直接处理的会作为一个直接使用的对象进行处理
boolean hasTypeHandler = ms.getConfiguration().getTypeHandlerRegistry()
.hasTypeHandler(parameterObject.getClass());
MetaObject metaObject = MetaObjectUtil.forObject(parameterObject);
//需要针对注解形式的MyProviderSqlSource保存原值
// 需要针对注解形式的MyProviderSqlSource保存原值
if (!hasTypeHandler) {
for (String name : metaObject.getGetterNames()) {
paramMap.put(name, metaObject.getValue(name));
}
}
//下面这段方法,主要解决一个常见类型的参数时的问题
// 下面这段方法,主要解决一个常见类型的参数时的问题
if (boundSql.getParameterMappings() != null && boundSql.getParameterMappings().size() > 0) {
for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
String name = parameterMapping.getProperty();
Expand Down Expand Up @@ -168,7 +221,8 @@ public Object processParameterObject(MappedStatement ms, Object parameterObject,
* @param pageKey
* @return
*/
public abstract Object processPageParameter(MappedStatement ms, Map<String, Object> paramMap, Page page, BoundSql boundSql, CacheKey pageKey);
public abstract Object processPageParameter(MappedStatement ms, Map<String, Object> paramMap, Page page,
BoundSql boundSql, CacheKey pageKey);

@Override
public boolean beforePage(MappedStatement ms, Object parameterObject, RowBounds rowBounds) {
Expand All @@ -180,19 +234,51 @@ public boolean beforePage(MappedStatement ms, Object parameterObject, RowBounds
}

@Override
public String getPageSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, RowBounds rowBounds, CacheKey pageKey) {
public String getPageSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, RowBounds rowBounds,
CacheKey pageKey) {
String sql = boundSql.getSql();
Page page = getLocalPage();
//支持 order by
// 支持 order by
String orderBy = page.getOrderBy();
String cacheSqlKey = getPageCacheSqlKey(page, sql);
final boolean cacheOn = cacheOnFlag && CACHE_PAGESQL != null;
final boolean orderByOnly = page.isOrderByOnly();
if (StringUtil.isNotEmpty(orderBy)) {
if (cacheOn) {
cacheSqlKey += orderBy;
if (orderByOnly) {
cacheSqlKey += "-orderByOnly";
}
}
pageKey.update(orderBy);
sql = orderBySqlParser.converToOrderBySql(sql, orderBy);

String cachedSql = cacheOn ? CACHE_PAGESQL.get(cacheSqlKey) : null;
if (cachedSql == null) {
cachedSql = orderBySqlParser.converToOrderBySql(sql, orderBy);
if (cacheOn && orderByOnly) {
CACHE_PAGESQL.put(cacheSqlKey, cachedSql);
}
}
sql = cachedSql;
}
if (page.isOrderByOnly()) {
if (orderByOnly) {
return sql;
}
return getPageSql(sql, page, pageKey);
String pageSql = cacheOn ? CACHE_PAGESQL.get(cacheSqlKey) : null;
if (pageSql == null) {
pageSql = getPageSql(sql, page, pageKey);
if (cacheOn) {
CACHE_PAGESQL.put(cacheSqlKey, pageSql);
}
}
return pageSql;
}

protected String getPageCacheSqlKey(final Page page, final String sql) {
if (page.getStartRow() == 0) {
return sql;
}
return sql + "-1";
}

/**
Expand All @@ -212,7 +298,7 @@ public Object afterPage(List pageList, Object parameterObject, RowBounds rowBoun
return pageList;
}
page.addAll(pageList);
//调整判断顺序,如果查全部,total就是size,如果只排序,也是全部,其他情况下如果不查询count就是-1
// 调整判断顺序,如果查全部,total就是size,如果只排序,也是全部,其他情况下如果不查询count就是-1
if ((page.getPageSizeZero() != null && page.getPageSizeZero()) && page.getPageSize() == 0) {
page.setTotal(pageList.size());
} else if (page.isOrderByOnly()) {
Expand All @@ -231,12 +317,21 @@ public void afterAll() {
@Override
public void setProperties(Properties properties) {
super.setProperties(properties);
final String sqlCacheClass = properties.getProperty("sqlCacheClass");
if (StringUtil.isNotEmpty(sqlCacheClass) && !sqlCacheClass.equalsIgnoreCase("false")) {
CACHE_COUNTSQL = CacheFactory.createCache(sqlCacheClass, "count", properties);
CACHE_PAGESQL = CacheFactory.createCache(sqlCacheClass, "page", properties);
} else if (!"false".equalsIgnoreCase(sqlCacheClass)) {
CACHE_COUNTSQL = CacheFactory.createCache(null, "count", properties);
CACHE_PAGESQL = CacheFactory.createCache(null, "page", properties);
}
}

/**
* @param boundSql
* @param ms
* @deprecated use {@code handleParameter(BoundSql boundSql, MappedStatement ms, Class<?> firstClass, Class<?> secondClass)}
* @deprecated use
* {@code handleParameter(BoundSql boundSql, MappedStatement ms, Class<?> firstClass, Class<?> secondClass)}
*/
@Deprecated
protected void handleParameter(BoundSql boundSql, MappedStatement ms) {
Expand All @@ -247,9 +342,12 @@ protected void handleParameter(BoundSql boundSql, MappedStatement ms) {

protected void handleParameter(BoundSql boundSql, MappedStatement ms, Class<?> firstClass, Class<?> secondClass) {
if (boundSql.getParameterMappings() != null) {
List<ParameterMapping> newParameterMappings = new ArrayList<ParameterMapping>(boundSql.getParameterMappings());
newParameterMappings.add(new ParameterMapping.Builder(ms.getConfiguration(), PAGEPARAMETER_FIRST, firstClass).build());
newParameterMappings.add(new ParameterMapping.Builder(ms.getConfiguration(), PAGEPARAMETER_SECOND, secondClass).build());
List<ParameterMapping> newParameterMappings = new ArrayList<ParameterMapping>(
boundSql.getParameterMappings());
newParameterMappings
.add(new ParameterMapping.Builder(ms.getConfiguration(), PAGEPARAMETER_FIRST, firstClass).build());
newParameterMappings.add(
new ParameterMapping.Builder(ms.getConfiguration(), PAGEPARAMETER_SECOND, secondClass).build());
MetaObject metaObject = MetaObjectUtil.forObject(boundSql);
metaObject.setValue("parameterMappings", newParameterMappings);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,9 @@ public Object processPageParameter(MappedStatement ms, Map<String, Object> param
public String getPageSql(String sql, Page page, CacheKey pageKey) {
return sql + " OFFSET ? ROWS FETCH FIRST ? ROWS ONLY";
}

@Override
protected String getPageCacheSqlKey(final Page page, final String sql) {
return sql;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,8 @@ public String getPageSql(String sql, Page page, CacheKey pageKey) {
return sqlBuilder.toString();
}

@Override
protected String getPageCacheSqlKey(final Page page, final String sql) {
return sql;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,8 @@ public String getPageSql(String sql, Page page, CacheKey pageKey) {
return sqlBuilder.toString();
}

@Override
protected String getPageCacheSqlKey(final Page page, final String sql) {
return sql;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,8 @@ public String getPageSql(String sql, Page page, CacheKey pageKey) {
return sqlBuilder.toString();
}

@Override
protected String getPageCacheSqlKey(final Page page, final String sql) {
return sql;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,16 @@ public String getPageSql(String sql, Page page, CacheKey pageKey) {
}
return sqlBuilder.toString();
}

@Override
protected String getPageCacheSqlKey(final Page page, final String sql) {
String cacheKey = sql;
if (page.getPageSize() > 0) {
cacheKey += "-p";
}
if (page.getStartRow() > 0) {
cacheKey += "-s";
}
return cacheKey;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,8 @@ public String getPageSql(String sql, Page page, CacheKey pageKey) {
return sqlBuilder.toString();
}

@Override
protected String getPageCacheSqlKey(final Page page, final String sql) {
return sql;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,8 @@ public String getPageSql(String sql, Page page, CacheKey pageKey) {
return sqlBuilder.toString();
}

@Override
protected String getPageCacheSqlKey(final Page page, final String sql) {
return sql;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,8 @@ public String getPageSql(String sql, Page page, CacheKey pageKey) {
return sqlBuilder.toString();
}

@Override
protected String getPageCacheSqlKey(final Page page, final String sql) {
return sql;
}
}
Loading

0 comments on commit a200701

Please sign in to comment.