ShardingJDBC 在数据分表下的三种分页查询实现:
- 1、逻辑分页:由shardingjdbc框架默认实现。
- 2、基于游标的分页:由代码实现,每次传入条件,顺序id每次选择大于上一个id的区间返回。
- 3、基于全局索引的分页:在redis建立排序和id关系,由分页变为区间查找。
优缺点:
- 逻辑分页:简单易用,适合小数据量场景。
- 基于游标的分页:性能高,适合大数据量场景,但不支持跳页。
- 基于全局索引的分页:性能高,支持跳页,但需要额外维护成本。
一、分页查询的问题
在分库分表场景下,分页查询的主要问题包括:
-
性能问题:
- 传统的
LIMIT offset, size需要扫描大量数据,尤其是在offset较大时,性能会显著下降。 - 在分库分表场景下,每个库或表都需要执行
LIMIT offset, size,然后将结果合并,进一步加剧性能问题。
- 传统的
-
结果不准确:
- 如果直接在每个库或表上执行
LIMIT offset, size,合并后的结果可能与全局排序不一致,导致分页结果不准确。
- 如果直接在每个库或表上执行
二、ShardingJDBC 分页查询的解决方案
ShardingJDBC 提供了对分页查询的支持,通过以下方式解决上述问题:
1. 逻辑分页
- 原理:ShardingJDBC 会将分页查询转换为对每个库或表的查询,然后将结果合并并进行逻辑分页。
- 优点:简单易用,无需额外配置。
- 缺点:在
offset较大时,性能较差。
示例
SELECT * FROM user ORDER BY id LIMIT 10 OFFSET 20;
ShardingJDBC 会将此查询分发到每个库或表,然后将结果合并并返回第 21-30 条数据。
2. 基于游标的分页
- 原理:通过记录上一页的最后一条数据的排序字段值(如
id),作为下一页查询的条件,避免使用OFFSET。 - 优点:性能高,适合大数据量的分页查询。
- 缺点:需要业务逻辑支持,且只能顺序分页(不能跳页)。
示例
-
第一页查询:
SELECT * FROM user ORDER BY id LIMIT 10;返回结果中的最后一条数据的
id为100。 -
第二页查询:
SELECT * FROM user WHERE id > 100 ORDER BY id LIMIT 10;
3. 基于全局索引的分页
- 原理:通过维护一个全局索引表(如 Elasticsearch 或 Redis),记录所有数据的排序信息,然后根据索引表查询分页数据。
- 优点:性能高,支持跳页。
- 缺点:需要额外的存储和维护成本。
示例
-
查询全局索引表,获取第 21-30 条数据的
id。 -
根据
id查询实际数据:SELECT * FROM user WHERE id IN (101, 102, ..., 110);
三、ShardingJDBC 分页查询的最佳实践
1. 使用逻辑分页
- 适用于数据量较小、分页深度较浅的场景。
- 直接使用
LIMIT offset, size,ShardingJDBC 会自动处理分页逻辑。
示例
String sql = "SELECT * FROM user ORDER BY id LIMIT 10 OFFSET 20";
List<User> users = shardingDataSource.executeQuery(sql);
2. 使用基于游标的分页
- 适用于大数据量的分页查询。
- 通过记录上一页的最后一条数据的排序字段值,作为下一页查询的条件。
示例
// 第一页查询
String sql = "SELECT * FROM user ORDER BY id LIMIT 10";
List<User> users = shardingDataSource.executeQuery(sql);
int lastId = users.get(users.size() - 1).getId();
// 第二页查询
sql = "SELECT * FROM user WHERE id > ? ORDER BY id LIMIT 10";
users = shardingDataSource.executeQuery(sql, lastId);
3. 使用基于全局索引的分页
- 适用于需要高性能和跳页功能的场景。
- 结合 Elasticsearch 或 Redis 维护全局索引。
示例
// 查询全局索引表,获取第 21-30 条数据的 id
List<Integer> ids = elasticsearchClient.search("user", 20, 10);
// 根据 id 查询实际数据
String sql = "SELECT * FROM user WHERE id IN (?)";
List<User> users = shardingDataSource.executeQuery(sql, ids);
四、ShardingJDBC 分页查询的注意事项
-
排序字段:
- 分页查询必须指定排序字段,否则结果可能不一致。
- 排序字段应尽量选择唯一且有序的字段(如
id)。
-
性能优化:
- 避免使用
OFFSET,尤其是在大数据量场景下。 - 使用基于游标的分页或全局索引分页,提高性能。
- 避免使用
-
结果合并:
- ShardingJDBC 会自动合并多个库或表的查询结果,但需要确保排序字段的唯一性,避免结果不一致。