Spring Boot优化百万级数据查询:解决Ajax超时及分页异常
问题概述
- Ajax获取数据耗时极长,半小时后触发会话超时,需实现1秒内获取10万条数据的能力
- 数据每日递增,1年后将达40万条,现有全量查询方式无法支撑
- 已实现Ajax分页,但优化查询后仅能返回单条数据
一、优化查询耗时,支撑大数据量场景
1. 数据库层面优化
- 添加联合索引:针对查询条件
num_isvalid和排序字段alumni_user_id创建索引,避免全表扫描CREATE INDEX idx_valid_id ON sis.nvs_alumni_registration(num_isvalid, alumni_user_id DESC); - 替换N+1查询:原代码中
jnvNmae字段通过循环调用globalFunction.getInstNameByInstId获取,改成SQL关联查询直接返回名称,消除循环调用开销
修改DAO层SQL(假设机构表为sis.institution,关联字段为jnv_name对应inst_id):SELECT a.alumni_user_id, a.alumni_name, a.alumni_login_id, a.year_of_passing, inst.inst_name AS jnv_name, a.fields_of_work_id, a.country_name, a.alumni_email, a.contact_no, a.class_passng_jnv, a.isnri, a.status_of_working, a.present_qualification, a.present_organisation, a.present_designation FROM sis.nvs_alumni_registration a LEFT JOIN sis.institution inst ON a.jnv_name = inst.inst_id WHERE a.num_isvalid='1' ORDER BY a.alumni_user_id DESC LIMIT ? OFFSET ?; - 强制分页查询:彻底放弃全量拉取数据,通过
LIMIT+OFFSET控制单次返回数据量,适配前端分页需求
2. 代码层面优化
- Controller接收分页参数:新增
page和size参数,校验会话合法性后传递到业务层@RequestMapping(value = "/showReportforDirectory", method = RequestMethod.GET) public @ResponseBody List<AlumniBeanForDirectory> showReport(HttpServletRequest request, @RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "20") Integer size) { HttpSession session = request.getSession(false); if (session == null || session.getAttribute("isuseractive") == null || session.getAttribute("isuseractive").equals("")) { return Collections.emptyList(); } try { return alumniService.getAlumniRegDetailsByPage(page, size); } catch (Exception e) { e.printStackTrace(); return Collections.emptyList(); } } - Service层处理分页逻辑:计算偏移量,调用DAO的分页查询方法,过滤转换失败的null对象
@Override public List<AlumniBeanForDirectory> getAlumniRegDetailsByPage(Integer page, Integer size) { int offset = (page - 1) * size; return emptyIfNull(alumniDaoImpl.getAlumniRegistrationDetailsByPage(size, offset)) .stream() .map(this::setAlumniDirectoryToBean) .filter(Objects::nonNull) .collect(Collectors.toList()); } - DAO层执行分页SQL:传递分页参数,确保SQL正确接收
size和offsetpublic List<Object[]> getAlumniRegistrationDetailsByPage(Integer size, Integer offset) { try { String query = "SELECT ... -- 上面修改后的关联SQL" + " LIMIT ? OFFSET ?"; return dataAccess.runNative(query, size, offset); // 确保runNative支持参数绑定 } catch (Exception e) { e.printStackTrace(); return Collections.emptyList(); } } - 修复Bean转换错误:将
(obj[10]).toString()=="0"改为"0".equals(obj[10].toString()),避免字符串比较错误;同时确保转换异常时返回的null被过滤,不影响列表数据
二、解决会话超时问题
- 控制单次请求数据量:通过分页每次返回20-50条数据,大幅缩短请求耗时,避免会话超时
- 调整会话超时配置:在
application.properties中适当延长会话超时(仅作为辅助方案,优先通过分页解决)server.servlet.session.timeout=30m - 异步请求处理:若需批量获取数据,改用异步请求避免阻塞HTTP会话,Controller添加
@Async注解,返回CompletableFuture<List<AlumniBeanForDirectory>>,前端配合异步接收
三、修复分页仅返回单条数据问题
- 校验分页参数传递:确认前端传递的
page和size参数正确到达后端,避免参数缺失导致默认LIMIT 1 - 检查DAO参数绑定:确认
runNative方法正确处理参数顺序,避免将offset当成size传入 - 直接验证SQL:在数据库客户端执行分页SQL,检查返回结果是否符合预期,排除SQL语法错误
内容的提问来源于stack exchange,提问作者Megha Tyagi
相关产品推荐
相关产品推荐

