美文网首页
Mybatis 分页方案 逻辑分页 or 物理分页

Mybatis 分页方案 逻辑分页 or 物理分页

作者: layasntx | 来源:发表于2022-04-19 22:15 被阅读0次

一、什么是逻辑分页

逻辑分页:先查询所有数据到内存,再从内存截取需要数据 ,属于前台分页

二、什么是物理分页

物理分页:通过SQL语句实现分页,属于后台分页
数据库分页的SQL语句写法不同:
MySQL使用limit ,SQLServer 使用top ,Oracle使用rowNum

三、优势和劣势

1、数据库负担

物理分页每次都访问数据库,逻辑分页只访问一次数据库,
物理分页对数据库造成的负担大。

2、服务器负担

逻辑分页一次性将数据读取到内存,占用了较大的内容空间,
物理分页每次只读取一部分数据,占用内存空间较小。

3、实时性

逻辑分页一次性将数据读取到内存,数据发生改变,数据库的最新状态不能实时反映到操作中,实时性差。
物理分页每次需要数据时都访问数据库,能够获取数据库的最新状态,实时性强。

4、依赖性

逻辑分页不依赖于数据库,可移植性高。
物理分页依赖于数据库SQL,可移植性差。

5、使用场合

逻辑分页主要用于数据量不大、数据稳定的场合,
物理分页主要用于数据量较大、更新频繁的场合。

四、Mybatis分页方案

1、Mybatis自带的分页方案

Mybatis自带的分页方案 通过 RowBounds 实现 先看源码:
# org.apache.ibatis.executor.resultset.DefaultResultSetHandler
 private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
    ResultSet resultSet = rsw.getResultSet();
    //注意:这里传入rowBounds,跳过不需要的行 实现 offset
    skipRows(resultSet, rowBounds);
    while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
      Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
      storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
    }
  }

private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
    if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
      if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
        rs.absolute(rowBounds.getOffset());
      }
    } else {
      for (int i = 0; i < rowBounds.getOffset(); i++) {
        //循环执行 rs.next() 跳过行 实现 offset
        if (!rs.next()) {
          break;
        }
      }
    }
  }
很明显 MyBatis 是属于逻辑分页的

2、PageHelper分页插件

PageHelper 通过拼接 limit 实现,属于物理分页
# com.github.pagehelper.dialect.helper.MySqlDialect
    @Override
    public String getPageSql(String sql, Page page, CacheKey pageKey) {
        StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
        sqlBuilder.append(sql);
        if (page.getStartRow() == 0) {
            sqlBuilder.append("\n LIMIT ? ");
        } else {
            sqlBuilder.append("\n LIMIT ?, ? ");
        }
        return sqlBuilder.toString();
    }

3、Mabatis Plus的分页插件

MP的分页插件 PaginationInterceptor 通过拼接 limit 实现,属于物理分页
public class MySqlDialect implements IDialect {
    @Override
    public DialectModel buildPaginationSql(String originalSql, long offset, long limit) {
        String sql = originalSql + " LIMIT " + FIRST_MARK + StringPool.COMMA + SECOND_MARK;
        return new DialectModel(sql, offset, limit).setConsumerChain();
    }
}

五、分页方案选择

针对海量数据的场景,逻辑分页显然并不可取,如果你已经集成了MP,直接使用MP的分页插件即可,否则的话可以选择PageHelper。这两种插件有针对不同的数据库的实现,因此不用太担心可移植性差的问题。

事实上,单表数据过百万之后,offset越大速度越慢。此时可以通过子查询来优化,例如:
SELECT * FROM table order by id limit 1000000, 10;
可优化为:
SELECT * FROM table WHERE id >= (SELECT id FROM table order by id limit 1000000, 1) LIMIT 10;

相关文章

网友评论

      本文标题:Mybatis 分页方案 逻辑分页 or 物理分页

      本文链接:https://www.haomeiwen.com/subject/hoaeertx.html