DBA 讨厌JAVA分页插件,随手写个分页代码
昨天我写了个分页服务类,今天发到群里,JAVA同事说,他开发个新分页插件,不会调用COUNT(*).
他说在JAVA拦截器做!
我们发现个非常特别的MYSQL函数
SELECT SQL_CALC_FOUND_ROWS * FROM TABLE_A WHERE A.ID=? ....
开源的pagehelp是分两条sql,而他就一条,实际发往数据库也是一条,
开发人员还是写一条SQL,而PageHelp插件实际发两条SQL到数据库.
而他下面通过另外个函数返回总数!
select FOUND_ROWS();
从他的部分JAVA拦截器代码来看,好像没有考虑下一页,也要统计一次总行数!
大家猜猜 上面那个SQL 在数据库的执行行为如何?
SELECT SQL_CALC_FOUND_ROWS *
FROM TABLE_A WHERE A.ID=? .... limit 10,10;
1 要返回总数, 2要返回前第20条数据.
在说出答案之间,我们还是看看开源插件的使用方式
@Override
public List selectUserReportInfolistPage(UserReportInfoVo condition, Pagination pagination)
{
if (null != pagination)
{
PageHelper.startPage(pagination.getPage(), pagination.getRows());
List resultList = userReportInfoMapper.selectByExample(this.makeExample(condition));
PageInfo pageInfo = new PageInfo(resultList);
pagination.setTotal((int) pageInfo.getTotal());
pagination.setTotalPage(pageInfo.getPages());
return resultList;
} else
{
List resultList = usertReportInfoMapper.selectByExample(this.makeExample(condition));
return resultList;
}
}
看起来行数比较少, 没有注释是比较难看得懂的,userReportInfoMapper
Mapper 文件接口类,是没有selectByExample 的方法的
this.makeExample(condition) 是本服务类的一个方法,做啥用的?
好像是拼接,又好像是!
总之搞个分页插件,套了一层,又一层!
快速过下眼看下分页插件几个类
public class PageInfo {
private int pages;
private int pageSize;
private int total;
private List list;
public int getPages() { return pages; }
public void setPages(int pages) { this.pages = pages; }
public int getPageSize() { return pageSize; }
public void setPageSize(int pageSize) { this.pageSize = pageSize; }
public int getTotal() { return total; }
public void setTotal(int total) { this.total = total; }
public List getList() { return list; }
public void setList(List list) { this.list = list; }
}
@ApiModel
public class Pagination implements Serializable, IPagination {
private static final long serialVersionUID = 1L;
public static final int DEFAULT_PAGE_SIZE=15;
public static final int DEFAULT_CURRENT_PAGE=1;
public static final int DEFAULT_SKIP_SIZE=10;
public static final int DEFAULT_CURRENT_SKIP=1;
public static final int MAX_PAGE_SIZE=200;
private int rows = DEFAULT_SKIP_SIZE;
private int total;
private int totalPage ;
private int page= DEFAULT_CURRENT_SKIP;
private int limit = DEFAULT_SKIP_SIZE;
private int offset;
public int getRows() {
return rows;
}
public void setRows(int rows) {
if(100 Limit: 10 row(s) (no early end due to SQL_CALC_FOUND_ROWS) (cost=216260.12 rows=10) (actual time=3.261..100830.348 rows=10 loops=1)
-> Table scan on A (cost=216260.12 rows=1911462) (actual time=3.254..100736.654 rows=399136 loops=1)
执行计划基本都一样, EXPALIN 是看不出来的, 只能使用高级执行计划
EXPALIN ANALYZE
最里面是
Table scan on A (cost=216260.12 rows=1911462) (actual time=3.254..100736.654 rows=399136 loops=1)
估算是191.万行,实际扫描了39..9万行 然后返回给MYSQL的SERVER
显然它要扫描全部行,然后再LIMIT
如果带有条件也如此, 比如符号条件有1万行, 返回前10行. 也是如此在引擎层必须扫描1万行,要不然怎么获得总数呢?
显然这种方法不好! 最后还涉及到LEFT JOIN 多表关联 COUNT是多余的关联.