当前位置: 首页 > news >正文

蓝凌EKP产品:列表查询性能优化全角度

列表查询是应用最广泛的也是最容易出现性能问题的,列表查询需要多表关联查询,多表关联,表与表之间产生的笛卡尔积就大,在大数据量下,主要从 “减少数据处理量”“加速数据定位”“优化分页效率” 三个核心目标出发,结合 SQL 优化、索引设计、分页机制等多维度实施。以下是具体优化方向:

一、优化关联逻辑:减少笛卡尔积规模

多表关联的核心问题是中间结果集(笛卡尔积)过大,因此需从源头减少参与关联的数据量:

  1. 优先过滤,再关联
    对每个表先通过 WHERE 子句过滤掉无关数据,再进行关联(避免先关联全表再过滤)。
    反例(低效):

    sql

    SELECT * FROM a 
    JOIN b ON a.id = b.a_id 
    WHERE a.status = 1 AND b.create_time > '2023-01-01'; -- 先关联全表,再过滤
    

    正例(高效):

    sql

    SELECT * FROM (SELECT * FROM a WHERE a.status = 1) a  -- 先过滤a表
    JOIN (SELECT * FROM b WHERE b.create_time > '2023-01-01') b  -- 先过滤b表
    ON a.id = b.a_id;  -- 再关联小结果集
    
  2. 控制关联表数量
    只关联必要的表,避免 “为了方便” 关联冗余表。例如:列表只需展示 “订单名称 + 用户姓名”,则关联 orders 和 users 即可,无需关联无关的 logs 表。

  3. 优化关联顺序
    数据库优化器可能无法总是选择最优关联顺序,此时可手动调整:让小表(过滤后结果集小的表)作为驱动表,减少外层循环次数。
    例如:a表(100行) JOIN b表(100万行) 比 b表 JOIN a表 更高效(驱动表行数少,内层循环次数少)。

二、索引设计:加速关联与过滤

索引是减少扫描范围的关键,针对多表关联需重点优化:

  1. 关联字段必须建索引
    多表 JOIN 的关联字段(如外键)必须创建索引,避免全表扫描。
    例:a.id 与 b.a_id 关联,则 b.a_id 需建索引:

    sql

    CREATE INDEX idx_b_aid ON b(a_id);
    
  2. 过滤条件字段建索引
    WHERE 子句中用于过滤的字段(如状态、时间、关键词)需建索引,减少单表扫描行数。
    例:频繁按 create_time 过滤,则:

    sql

    CREATE INDEX idx_a_create_time ON a(create_time);
    
  3. 复合索引适配查询场景
    若过滤条件 + 关联字段频繁组合出现,可创建复合索引,进一步减少回表查询。
    例:查询 WHERE a.status=1 AND a.create_time > '2023-01-01' 且关联 a.id = b.a_id,则:

    sql

    CREATE INDEX idx_a_status_time_id ON a(status, create_time, id); -- 按过滤字段+关联字段顺序
    

三、分页机制优化

避免 “全量扫描 + 偏移” 的低效

传统分页(LIMIT offset, size)在 offset 很大时(如 LIMIT 100000, 20)性能极差,因为数据库需扫描前 100020 条数据再丢弃前 100000 条。需优化分页方式:

  1. 基于 “游标” 的分页(推荐)
    利用有序字段(如自增主键、时间戳)作为 “游标”,通过条件过滤代替 offset,避免全量扫描。
    例:以上一页最后一条数据的 id 作为游标,下一页查询:

    sql

    -- 第1页:取前20条,按id排序
    SELECT * FROM (SELECT a.id, a.name, b.info FROM a JOIN b ON a.id = b.a_id WHERE a.status = 1 ORDER BY a.id ASC
    ) t LIMIT 20;-- 第2页:以上一页最后一个id(如20)为游标,直接过滤
    SELECT * FROM (SELECT a.id, a.name, b.info FROM a JOIN b ON a.id = b.a_id WHERE a.status = 1 AND a.id > 20  -- 用id > 游标代替offsetORDER BY a.id ASC
    ) t LIMIT 20;
    

    优势:通过索引快速定位 a.id > 20 的位置,无需扫描前 20 条,效率不受页码影响。

  2. 限制最大页码
    前端禁止访问过大的页码(如超过 1000 页),引导用户通过筛选条件缩小范围(如按时间、分类过滤),避免极端分页场景。

  3. 延迟关联(针对大表分页)
    先分页获取主表 ID,再关联其他表获取详细信息,减少关联的数据量。
    例:

    sql

    -- 步骤1:先分页获取主表a的id(小结果集)
    SELECT id FROM a WHERE status = 1 ORDER BY id LIMIT 20 OFFSET 0;-- 步骤2:用获取的id关联其他表(只关联20条数据)
    SELECT a.name, b.info 
    FROM a JOIN b ON a.id = b.a_id 
    WHERE a.id IN (1,2,...20); -- 步骤1的结果
    

        

 EKP 框架的分页分布进行

         结合EKP现有的框架分页,对分页机制进行优化,主要是异步请求分页总数,         

         1. 按需字段查询:findPageForListview()

            传统列表查询中,返回完整 Model 对象会引发懒加载问题。使用:

List<String> displayColumns = SysListShowUtil.getListDisplayColumns(modelName, pagePath);
Page page = service.findPageForListview(hqlInfo, displayColumns);

        主要优化查询方法,使用findPageForListview即只查询页面真正展示的字段。

  • 支持关联属性(如 docCreator.fdName)
  • 查询结果为 JSONObject,可灵活使用

        2. 列表后置处理接口

        可在查询完毕后自定义处理数据:

@Override
public Page postProcessForListview(HQLInfo hqlInfo, List<String> displayColumns, Page page) throws Exception {// 补全颜色、标签等字段return page;
}

       3 . 列表总数异步统计

        在 jsp页面使用 <list:listview>、<list:colTable> 标签中加入:

        

		<list:listview id="listview_send" asyncCount="${showConfig.asyncCount}"><ui:source type="AjaxJson">{url:'/km/imissive/km_imissive_send_main/kmImissiveSendMain.do?method=listChildren&categoryId=${JsParam.categoryId}&q.docStatus=30'}</ui:source><list:colTable url="${LUI_ContextPath }/sys/profile/listShow/sys_listShow/sysListShow.do?method=getSort&modelName=com.landray.kmss.km.imissive.model.KmImissiveSendMain" isDefault="false" layout="sys.ui.listview.columntable" rowHref="/km/imissive/km_imissive_send_main/kmImissiveSendMain.do?method=view&fdId=!{fdId}"  name="columntable"><list:col-checkbox></list:col-checkbox><list:col-serial></list:col-serial><list:col-auto props=""></list:col-auto></list:colTable></list:listview> 

后端通过:

HQLInfo hqlInfo = new HQLInfo();
hqlInfo.setOrderBy(orderby);
hqlInfo.setPageNo(pageno);
hqlInfo.setRowSize(rowsize);
changeFindPageHQLInfo(request, hqlInfo);
String s_pagingType = request.getParameter("pagingtype");
KmImissiveShowConfig showConfig = new KmImissiveShowConfig();
String pagingSetting = showConfig.getDataMap().get("pagingSetting");
String pagePath = request.getParameter("q.j_path");
if ("simple".equals(s_pagingType)|| ShowConfig.PAGING_SETTING_SIMPLE.equals(pagingSetting)) {hqlInfo.setPagingType(HQLInfo.PAGING_TYPE_SIMPLE);
}

主要是如下代码:

if ("simple".equals(pagingType)) {
hqlInfo.setPagingType(HQLInfo.PAGING_TYPE_SIMPLE);
}

实现总数查询异步化,降低渲染耗时。

四、SQL 与执行计划优化

  1. 避免 SELECT *,只查必要字段
    减少数据传输量和内存消耗,尤其避免查询大字段(如 TEXTCLOB)。
    例:只查列表展示需要的 id, name, create_time,而非全字段。

  2. 用 EXPLAIN 分析执行计划
    通过 EXPLAIN 查看 SQL 是否存在全表扫描(type: ALL)、临时表(Using temporary)、文件排序(Using filesort)等问题,针对性优化。
    例:

    sql

    EXPLAIN 
    SELECT a.id, b.name FROM a JOIN b ON a.b_id = b.id WHERE a.status = 1 LIMIT 20;
    

    若出现 type: ALL,说明关联字段或过滤字段缺少索引,需补充。

  3. 避免子查询嵌套过深
    复杂子查询可能导致优化器无法生成最优计划,尽量用 JOIN 改写。

五、数据库与架构层面优化

  1. 表分区
    对超大表(如千万级以上)按时间、区域等维度分区(如 MySQL 的分区表、Oracle 的分区功能),使查询只扫描目标分区。
    例:按 create_time 分区,查询 2023 年数据时,只扫描 2023 年分区。

  2. 读写分离
    列表查询通常是读操作,可将查询路由到从库,减轻主库压力。

  3. 缓存热点数据
    对高频访问的列表(如首页推荐列表),用 Redis 缓存查询结果(设置合理过期时间),避免重复查询数据库。

  4. 分表分库
    若单表数据量过亿,可水平分表(按 ID 哈希或范围拆分),使每个分表数据量控制在百万级,关联查询时只需操作对应分表。

六、应用层配合

  1. 前端分页参数校验
    限制 size 最大值(如最多 100 条 / 页),避免一次性查询过大数据量。

  2. 异步加载与懒加载
    列表滚动到底部时,再加载下一页(懒加载),减少初始加载压力。

  3. 预加载相邻页
    提前缓存下一页数据(如用户浏览第 1 页时,异步加载第 2 页),提升用户体验。

总结

多表关联的列表查询优化核心是 “最小化处理数据量”:通过过滤减少关联数据、索引加速定位、游标分页避免全量扫描,再结合数据库架构和应用层策略,可在大数据量下保持高效查询。实际优化中需结合 EXPLAIN 执行计划和业务场景,优先解决最影响性能的瓶颈(如缺失索引、全表扫描)。

http://www.lryc.cn/news/612602.html

相关文章:

  • Git 文件删除操作指南:管理与恢复已删除文件
  • 合约收款方式,转账与问题安全
  • 「耘•学社」耘少年第五期学能突破导师制领袖特训营,圆满落幕
  • 计算机视觉前言-----OpenCV库介绍与计算机视觉入门准备
  • 解决Git提交人信息默认全局化问题:让提交人自动关联当前用户
  • Element Plus实现分页查询
  • 【PHP 中的 `use` 关键字完全指南】
  • 数码论坛|基于SprinBoot+vue的数码论坛系统(源码+数据库+文档)
  • Redis为什么要引入多线程?
  • Beelzebub靶机
  • 防火墙环境下的全网服务器数据自动化备份平台搭建:基于 rsync 的完整实施指南
  • Java基础学习1(Java语言概述)
  • spring cache(二)核心接口
  • 浏览器渲染与GPU进程通信图解
  • ubuntu 2024 安装拼音输入法
  • 纪念《信号与系统》拉普拉斯变换、Z变换之前内容学完
  • 迭代器与生成器:Python 中的高效数据遍历机制
  • 现代制冷系统核心技术解析:从四大件到智能控制的关键突破
  • QDataStream入门
  • LeetCode每日一题,2025-8-7
  • JSON、JSONObject、JSONArray详细介绍及其应用方式
  • Self-RAG:基于自我反思的检索增强生成框架技术解析
  • 【感知机】感知机(perceptron)学习策略
  • 阿里云polardb-x 2.0迁移至华为云taurusdb
  • 【感知机】感知机(perceptron)模型与几何解释
  • MySQL数据库索引及底层数据结构
  • 2025国赛数学建模C题详细思路模型代码获取,备战国赛算法解析——决策树
  • 软件架构:系统结构的顶层设计与战略约束
  • Maven入门到精通
  • Cervantes:面向渗透测试人员和红队的开源协作平台