Java分页查询提速案例优化
传统分页问题分析
// 传统分页 - 性能低效
@Repository
public class UserRepository {
// 问题1: COUNT查询和主查询分开执行
public Page<User> findByPage(int page, int size) {
// 先count
Long total = jdbcTemplate.queryForObject(
"SELECT COUNT(*) FROM user", Long.class);
// 再分页查询
List<User> list = jdbcTemplate.query(
"SELECT * FROM user LIMIT ? OFFSET ?",
new Object[]{size, page * size},
new UserRowMapper()
);
return new Page<>(list, total, page, size);
}
}
优化方案一:延迟COUNT
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
// 优化1: 延迟计算总记录数
public Page<User> findByPageOptimized(int page, int size) {
// 仅第一页需要总记录数
if (page == 0) {
return userRepository.findByPageWithCount(page, size);
}
// 非第一页:先获取数据
List<User> list = userRepository.findByPageNoCount(page, size);
// 根据数据判断是否需要总记录数
if (list.size() < size) {
// 最后一页,计算总记录数
Long total = userRepository.countAll();
return new Page<>(list, total, page, size);
}
// 非最后一页,使用估算或上一次的总记录数
return new Page<>(list, null, page, size);
}
}
优化方案二:覆盖索引查询
@Repository
public class UserRepository {
// 优化2: 使用覆盖索引
public List<User> findByPageWithCoveringIndex(int lastId, int size) {
String sql = """
SELECT id, name, email, created_at
FROM user
WHERE id > ?
ORDER BY id
LIMIT ?
""";
return jdbcTemplate.query(
sql,
new Object[]{lastId, size},
new UserRowMapper()
);
}
// 配合使用
public Page<User> findByPageWithCursor(int lastId, int size) {
List<User> list = findByPageWithCoveringIndex(lastId, size);
// 获取最后一个ID作为下一页的游标
Integer nextCursor = list.isEmpty() ? null : list.get(list.size() - 1).getId();
return new Page<>(list, size, nextCursor);
}
}
优化方案三:SQL_CALC_FOUND_ROWS
@Repository
public class UserRepository {
// 优化3: 使用SQL_CALC_FOUND_ROWS
public Page<User> findByPageWithCalcRows(int page, int size) {
// 一次查询获取总记录数和数据
String sql = """
SELECT SQL_CALC_FOUND_ROWS id, name, email, created_at
FROM user
WHERE status = 1
ORDER BY created_at DESC
LIMIT ? OFFSET ?
""";
List<User> list = jdbcTemplate.query(
sql,
new Object[]{size, page * size},
new UserRowMapper()
);
// 获取总记录数
Long total = jdbcTemplate.queryForObject(
"SELECT FOUND_ROWS()", Long.class);
return new Page<>(list, total, page, size);
}
}
优化方案四:分页缓存
@Service
public class UserService {
@Autowired
private CacheManager cacheManager;
// 优化4: 缓存总记录数
@Cacheable(value = "userCount", unless = "#result == null")
public Long getUserCount() {
return userRepository.countAll();
}
public Page<User> findByPageWithCache(int page, int size) {
// 缓存总记录数,减少COUNT查询
Long total = getUserCount();
List<User> list = userRepository.findByPage(page, size);
return new Page<>(list, total, page, size);
}
// 定期更新缓存
@Scheduled(fixedDelay = 60000) // 1分钟更新一次
public void refreshUserCount() {
cacheManager.getCache("userCount").clear();
getUserCount();
}
}
优化方案五:性能对比测试
@Component
public class PaginationBenchmark {
@Autowired
private UserService userService;
public void benchmark() {
int page = 0;
int size = 20;
int iterations = 100;
// 测试传统分页
long start1 = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
userService.findByPageTraditional(page, size);
}
long end1 = System.currentTimeMillis();
System.out.println("传统分页耗时: " + (end1 - start1) + "ms");
// 测试优化后的分页
long start2 = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
userService.findByPageOptimized(page, size);
}
long end2 = System.currentTimeMillis();
System.out.println("优化分页耗时: " + (end2 - start2) + "ms");
// 输出性能提升比例
double improvement = ((double)(end1 - start1) - (end2 - start2)) / (end1 - start1) * 100;
System.out.println("性能提升: " + String.format("%.2f", improvement) + "%");
}
}
数据库索引优化
-- 创建复合索引
CREATE INDEX idx_user_status_created ON user(status, created_at, id);
-- 覆盖索引
CREATE INDEX idx_user_cover ON user(id, name, email, created_at);
-- 分区表(大表优化)
ALTER TABLE user PARTITION BY RANGE (YEAR(created_at)) (
PARTITION p2020 VALUES LESS THAN (2021),
PARTITION p2021 VALUES LESS THAN (2022),
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025)
);
完整优化方案选择
@Component
public class PaginationStrategy {
public Page<?> findByPage(int page, int size, PaginationStrategy strategy) {
switch (strategy) {
case CURSOR:
return findByCursor(page, size);
case COVERING_INDEX:
return findByCoveringIndex(page, size);
case SQL_CALC_ROWS:
return findBySqlCalcRows(page, size);
case CACHED:
return findByCached(page, size);
default:
return findByTraditional(page, size);
}
}
// 结合多种优化方案
public Page<User> findByComprehensiveOptimization(int page, int size) {
// 1. 小数据量使用传统分页
if (page * size < 10000) {
return findByTraditional(page, size);
}
// 2. 大数据量使用游标分页
if (size <= 50) {
return findByCursor(page, size);
}
// 3. 使用覆盖索引+延迟COUNT
return findByCoveringIndexWithLazyCount(page, size);
}
}
enum PaginationStrategy {
TRADITIONAL,
CURSOR,
COVERING_INDEX,
SQL_CALC_ROWS,
CACHED
}
| 优化方案 | 适用场景 | 性能提升 | 复杂度 |
|---|---|---|---|
| 延迟COUNT | 非首页查询 | 30-50% | 低 |
| 覆盖索引 | 大数据量 | 50-80% | 中 |
| SQL_CALC_ROWS | 中等数据量 | 20-40% | 低 |
| 游标分页 | 滚动加载 | 60-90% | 中 |
| 缓存策略 | 高频查询 | 70-90% | 中 |
根据实际业务场景选择合适的优化方案,建议组合使用以达到最佳效果。

版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播