ShardingJDBC 在数据分表下的三种分页查询实现

271 阅读4分钟

ShardingJDBC 在数据分表下的三种分页查询实现:

  • 1、逻辑分页:由shardingjdbc框架默认实现。
  • 2、基于游标的分页:由代码实现,每次传入条件,顺序id每次选择大于上一个id的区间返回。
  • 3、基于全局索引的分页:在redis建立排序和id关系,由分页变为区间查找。

优缺点:

  • 逻辑分页:简单易用,适合小数据量场景。
  • 基于游标的分页:性能高,适合大数据量场景,但不支持跳页。
  • 基于全局索引的分页:性能高,支持跳页,但需要额外维护成本。

一、分页查询的问题

在分库分表场景下,分页查询的主要问题包括:

  1. 性能问题

    • 传统的 LIMIT offset, size 需要扫描大量数据,尤其是在 offset 较大时,性能会显著下降。
    • 在分库分表场景下,每个库或表都需要执行 LIMIT offset, size,然后将结果合并,进一步加剧性能问题。
  2. 结果不准确

    • 如果直接在每个库或表上执行 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 分页查询的注意事项

  1. 排序字段

    • 分页查询必须指定排序字段,否则结果可能不一致。
    • 排序字段应尽量选择唯一且有序的字段(如 id)。
  2. 性能优化

    • 避免使用 OFFSET,尤其是在大数据量场景下。
    • 使用基于游标的分页或全局索引分页,提高性能。
  3. 结果合并

    • ShardingJDBC 会自动合并多个库或表的查询结果,但需要确保排序字段的唯一性,避免结果不一致。