分页查询的几种不同方式

mysql分页查询的几种方式

注:大写单词为mysql关键字,小写单词为参数

sql语句适用场景缺点
方式一SELECT * FROM tableName LIMIT #{offset},#{size}数据库中数据不多时的分页查询当使用LIMIT offset,size命令时,mysql会逐条遍历,抛弃前offset条数据,然后取size条数据,当offset非常大时性能下降明显
方式二SELECT * FROM table WHERE id > #{id} LIMIT #{size}需要连续取值时的分页查询当要跨页查询时(例如当前是第1页,直接跳到第5页的数据),尤其是现在普遍使用雪花算法生成id,无法得知id的具体值
方式三SELECT * FROM table WHERE id > #{id} ORDER BY id LIMIT #{offset},#{size}数据不多且不连续查询方式一和方式二结合,虽然解决了方式二的问题,当跨页查询的跨度太大时(例如从第5页跳转到第1000页),仍然会有方式一的问题
方式四SELECT * FROM table AS a INNER JOIN(SELECT id FROM table ORDER BY id LIMIT #{offset},#{size}) AS b USING(id)优化方式一当offset很大时同样有性能问题
方式五SELECT * FROM table WHERE id >(SELECT id FROM table ORDER BY id limit #{offset},1) LIMIT #{size}优化方式四相较于方式4不需要进行内联操作,但仍有同样的问题存在

基于缓存实现分页查询

问题关键点:基于以上的几种sql语句分析,分页查询的性能问题主要出现在当offset很大时,mysql需要逐条查询后丢弃数据,极端情况下几乎接近扫描全表,而不使用offset时(方式二)无法连续查询。

解决思路:使用方式二,找到一种快速定位id的方法即可解决扫描全表的问题

实现方案:(前提是使用自增且不重复的字段)

1、当向数据库新增或删除数据时,将数据的id维护到redis的zset集合中;

2、(可选)以数据的id为key,将整行数据存储到redis中

3、确定offset的值后,可以获取指定排名的数据id(zset使用跳表,查询的时间复杂度为O(logN)

4、确定id后使用方式二进行分页查询(或根据id在redis中查询数据)

问题:

1、保证数据库和缓存数据的一致性是另一个待研究的领域

2、很难实现复杂业务场景的分页查询,如指定筛选条件的分页查询

ORDER BY 和 LIMIT 关键字共用时所引发的数据问题
现象:
ORDER BY 和 LIMIT 关键字混用时,当ORDER BY的字段内容不唯一时,可能会导致分页查询的数据有重复。

根据官方文档的解释:
如果order by的字段有多个行都有相同的值,mysql是会随机的顺序返回查询结果的,具体依赖对应的执行计划。也就是说如果排序的列是无序的,那么排序的结果行的顺序也是不确定的。

解决方案:
在使用ORDER BY时,将主键作为排序值即可避免此问题

### MyBatis 分页实现方式 MyBatis 是一种流行的持久层框架,支持多种分页实现方式。以下是几种常见的分页方法及其特点: #### 1. **基于 `RowBounds` 的分页** 使用 `RowBounds` 对象可以在执行查询时指定偏移量和返回记录数。这种方式适用于简单的分页需求,但在大数据场景下性能较差,因为底层会加载所有数据后再截取所需部分[^3]。 ```java // 创建 RowBounds 实例 RowBounds rowBounds = new RowBounds(offset, limit); // 执行带分页参数的查询 List<User> users = sqlSession.selectList("selectUsers", null, rowBounds); ``` #### 2. **基于 SQL 参数的分页** 这种方式通过在 SQL 查询中手动添加分页条件来完成分页操作。例如,在 MySQL 数据库中可以通过 `LIMIT` 子句实现分页[^1]。 ```sql SELECT * FROM user LIMIT #{offset}, #{limit}; ``` 此方法的优点是可以充分利用数据库自身的优化能力,缺点是需要针对不同数据库编写不同的分页语法。 #### 3. **基于插件的分页** MyBatis 提供了一些第三方插件用于简化分页开发工作,其中最常用的插件之一是 PageHelper。它允许开发者仅需少量配置即可轻松实现分页功能[^2]。 ##### 配置 PageHelper 插件 在 Maven 项目中引入依赖项: ```xml <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.3.0</version> </dependency> ``` 使用示例: ```java // 设置分页参数 PageHelper.startPage(pageNum, pageSize); // 执行查询 List<User> userList = userMapper.selectAll(); // 获取分页信息 PageInfo<User> pageInfo = new PageInfo<>(userList); ``` #### 4. **基于封装代码的分页** 如果不想依赖外部插件,也可以通过自定义工具类或拦截器的方式实现分页逻辑。这种方法灵活性较高,适合有特殊需求的应用场景[^4]。 以下是一个简单的封装例子: ```java public class Pagination { private int offset; private int limit; public String getLimitSql(String sql) { StringBuilder sb = new StringBuilder(sql); sb.append(" LIMIT ").append(this.offset).append(", ").append(this.limit); return sb.toString(); } } ``` 调用时只需传入原始 SQL 和分页参数即可生成带有分页条件的新 SQL。 --- ### 总结 以上四种方式各有优劣,具体选择取决于实际业务需求和技术栈环境。对于小型应用来说,可以直接采用基于 SQL 参数的方法;而对于大型系统,则建议优先考虑插件方案以提升开发效率并降低维护成本。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值