深度分页问题优化方案

问题原因

Mysql使用select * from table limit offset, rows分页在深度分页的情况下, 性能急剧下降。

例如:select * 的情况下直接⽤limit 600000,10 扫描的是约60万条数据,并且是需要回表 60W次,也就是说⼤部分性能都耗在随机访问上,到头来只⽤到10条数据(总共取600010条数据只留10条记录)

优化方案

前端方案:业务层面限制跨度比较大的跳页

提供2种风格分页器供用户选择

  1. 标准分页器,展示最后一页和跳转指定页输入框
    image.png
  2. 简单分页器
    image.png

参考
百度方案: 不展示最后一页和直接跳转指定分页的输入框
image.png
Google方案:只展示查看下一页的按钮
image.png

界面设计器选表格/画廊的属性面板提供分页器风格的属性下拉选择

image.png

xml示例
<!-- 表格使用的标准分页器 --> <view type="TABLE" paginationStyle="SIMPLE"> <!-- fields --> </view> <!-- 画廊使用默认的标准分页器 --> <view type="GALLERY" paginationStyle="STANDARD"> <!-- fields --> </view>

后端方案

  1. 使用索引:确保数据库表中的相关字段上创建了适当的索引。索引可以加快查询速度,特别是在处理大数据量时。

  2. 分批查询:将大数据分成多个较小的批次进行查询,而不是一次性查询全部数据。可以通过限制每次查询的数据量和使用合适的偏移量来实现分批查询,例如使用LIMIT和OFFSET子句。

  3. 基于游标的分页:使用基于游标的分页技术,而不是传统的偏移分页。游标分页是通过记录上一次查询的游标位置,在下一次查询时从该位置开始获取新的数据,避免了大偏移量的影响。这可以通过数据库自身的功能(例如MySQL的CURSOR)或使用第三方库来实现。

  4. 缓存数据:如果数据变化较少,可以考虑将查询结果缓存到内存中,以避免频繁地查询数据库。这样可以提高页面相应速度,并减轻数据库负担。缓存的数据应该根据业务需要及时更新。

  5. 数据预处理:如果查询结果经常需要进行复杂的计算或处理,可以考虑提前对数据进行预处理并缓存结果,以减少每次查询的计算负担。

  6. 数据库优化:针对具体数据库系统,可以根据实际情况进行数据库调优。例如,合理设置数据库连接池大小、调整数据库参数等。

  7. 分布式存储和计算:对于非关系型数据库或分布式存储系统,可以考虑使用分布式存储和计算方案,将数据分散存储在多个节点上,并通过计算节点并行处理查询请求,以提高性能和可伸缩性。

参考链接

MySQL深分页场景下的性能优化

Oinone社区 作者:原创文章,如若转载,请注明出处:https://doc.oinone.top/other/75.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

Like (0)
's avatar
Previous 2023年6月20日 pm4:07
Next 2023年11月2日 pm1:58

相关推荐

  • Excel导出模块翻译值

    由于目前翻译资源导出只可以导出应用资源,无法导出模块资源,所以暂时提供以下方法导出模块资源。6.2.11、5.7.4.20 之前版本验证 方案一: 导出环境覆盖以下类 package pro.shushi.pamirs.translate.template.imports; import com.alibaba.fastjson.JSON; import com.google.common.collect.Lists; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import pro.shushi.pamirs.file.api.context.ExcelDefinitionContext; import pro.shushi.pamirs.file.api.entity.ExcelExportFetchDataContext; import pro.shushi.pamirs.file.api.extpoint.impl.DefaultExcelExportFetchDataExtPoint; import pro.shushi.pamirs.file.api.model.ExcelExportTask; import pro.shushi.pamirs.framework.connectors.data.sql.Pops; import pro.shushi.pamirs.framework.connectors.data.sql.query.LambdaQueryWrapper; import pro.shushi.pamirs.framework.connectors.data.sql.query.QueryWrapper; import pro.shushi.pamirs.meta.annotation.Ext; import pro.shushi.pamirs.meta.annotation.ExtPoint; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.api.Models; import pro.shushi.pamirs.meta.api.dto.wrapper.IWrapper; import pro.shushi.pamirs.meta.common.lambda.LambdaUtil; import pro.shushi.pamirs.meta.common.util.PStringUtils; import pro.shushi.pamirs.meta.constant.SqlConstants; import pro.shushi.pamirs.meta.domain.module.ModuleDefinition; import pro.shushi.pamirs.resource.api.enmu.TranslationApplicationScopeEnum; import pro.shushi.pamirs.resource.api.model.ResourceTranslation; import pro.shushi.pamirs.resource.api.model.ResourceTranslationItem; import pro.shushi.pamirs.translate.constant.TranslateConstants; import pro.shushi.pamirs.translate.proxy.TranslationItemExportProxy; import pro.shushi.pamirs.translate.service.TranslationDslNodeVisitor; import pro.shushi.pamirs.translate.template.TranslateTemplate; import pro.shushi.pamirs.translate.utils.UniversalParser; import java.util.*; import java.util.stream.Collectors; import static pro.shushi.pamirs.translate.constant.TranslateConstants.FIELD_TO_EXCLUDE; /** * @author Adamancy Zhang * @date 2020-11-04 18:09 */ @Slf4j @Component @Ext(ExcelExportTask.class) @SuppressWarnings({"unchecked"}) public class ResourceTranslationExportExtPoint extends DefaultExcelExportFetchDataExtPoint { private String resLangCodeColumn = PStringUtils.fieldName2Column(LambdaUtil.fetchFieldName(ResourceTranslationItem::getResLangCode)); private String langCodeColumn = PStringUtils.fieldName2Column(LambdaUtil.fetchFieldName(ResourceTranslationItem::getLangCode)); private String moduleColumn = PStringUtils.fieldName2Column(LambdaUtil.fetchFieldName(ResourceTranslationItem::getModule)); @Override @ExtPoint.Implement(expression = "context.name=="" + TranslateTemplate.TEMPLATE_NAME + "" && context.model=="" + ResourceTranslation.MODEL_MODEL + """) public List<Object> fetchExportData(ExcelExportTask exportTask, ExcelDefinitionContext context) { ArrayList<Object> objects = new ArrayList<>(); Map<String, Object> queryData = exportTask.getConditionWrapper().getQueryData(); TranslationItemExportProxy data = JSON.parseObject(JSON.toJSONString(queryData), TranslationItemExportProxy.class); LambdaQueryWrapper<TranslationItemExportProxy> queryWrapper = Pops.<TranslationItemExportProxy>lambdaQuery() .from(TranslationItemExportProxy.MODEL_MODEL) .eq(StringUtils.isNotBlank(data.getModule()), ResourceTranslationItem::getModule, data.getModule()) .eq(ResourceTranslationItem::getResLangCode, TranslateConstants.RES_LANG_CODE) .eq(StringUtils.isNotBlank(data.getLangCode()), ResourceTranslationItem::getLangCode, data.getLangCode()) .eq(data.getState() != null, ResourceTranslationItem::getState, data.getState()) .like(StringUtils.isNotBlank(data.getResLangInclude()), ResourceTranslationItem::getOrigin, data.getResLangInclude()) .like(StringUtils.isNotBlank(data.getTargetInclude()), ResourceTranslationItem::getTarget, data.getResLangInclude()); Map<String, String> moduleNameMap = Models.origin().queryListByWrapper(Pops.<ModuleDefinition>lambdaQuery() .from(ModuleDefinition.MODEL_MODEL) .eq(StringUtils.isNotBlank(data.getModule()), ModuleDefinition::getModule, data.getModule())) .stream() .collect(Collectors.toMap(ModuleDefinition::getModule, ModuleDefinition::getDisplayName, (_a, _b)…

    2025年8月21日
    64600
  • 表格页自定义按钮如何获取搜索区域的查询条件

    介绍 在使用 Oinone 平台开发过程中,开发者可能会遇到自定义动作需要获取搜索条件并传递给后端的情况。本文将介绍如何利用 Oinone平台 实现此功能。 技术基础知识 当我们在自定义一个动作的时候要先明确自定义的动作类型是什么样的,在Oinone平台中,分为了如下几个动作: 1: 视图动作2: 服务端动作3: 客户端动作3: URL动作 功能步骤或代码示例 案例1、服务端动作,动作点击时候要拿到搜索内容,然后传递给后端。 import { ActionType, ActionWidget, SPI, ServerActionWidget } from '@kunlun/dependencies'; @SPI.ClassFactory( ActionWidget.Token({ name: 'name', model: 'model', actionType: ActionType.Server }) ) export class MyServerActionWidget extends ServerActionWidget { protected async clickAction() { const rst = this.getSearchRsqlAndQueryParams(); } } 案例2、视图动作点击的时候把搜索内容带到另外一个视图或者弹窗 import { ActionType, ActionWidget, SPI, ServerActionWidget } from '@kunlun/dependencies'; @SPI.ClassFactory( ActionWidget.Token({ name: 'name', model: 'model' }) ) export class MyDialogViewActionWidget extends DialogViewActionWidget { // 继承当前动作原本的class protected async clickAction() { const { queryData } = this.getSearchRsqlAndQueryParams(); this.action.context = queryData super.clickAction() return true } } 在上述代码中,我们自定义了一个服务器动作,并在点击触发函数中调用了getSearchRsqlAndQueryParams方法,该方法会返回一个对象: { rsql: String, // 搜索内容对应的rsql queryData: Object, // 搜索的数据 condition: Condition, // 搜索内容对应的数据结构 queryDataToString: Function // 将搜索内容转成JSON字符串 } 这样我们就可以根据业务场景使用对应的值。 注意事项 1: 确保正确导入所需的依赖包。2: 理解并适当修改代码以满足特定业务需求。 总结 本文介绍了在 Oinone 平台中如何自定义一个服务端动作,并获取搜索条件传递给后端的方法。通过合理利用这些功能,开发者可以更灵活地定制应用程序,满足不同的业务需求。 实践案例 如何自定义点击导出动作绑定指定模板

    2024年3月6日
    1.7K00
  • 无代码启动常见问题

    1、执行yum-config-manager –add-repo https://download.docker.com/linux/centos/docker-ce.repo出现问题: 报错: Could not fetch/save url https://download.docker.com/linux/centos/docker-ce.repo to file /etc/yum.repos.d/docker-ce.repo: [Errno 14] curl#35 – "TCP connection reset by peer 解决方案: # 把docker-ce镜像源设置到阿里云 yum-config-manager –add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 2、如果重新运行startup.sh失败,可能是docker前面已经启过了还在运行。 解决方案: docker stop designer-allinone docker rm designer-allinone sh startup.sh 3、启动无代码失败 报错: 依赖包版本不匹配 pamirs-business-api已安装版本:[5.1.24],启动中包含的版本:[5.1.12] pamirs-data-audit-api已安装版本:[5.1.24],启动中包含的版本:[5.1.12] pamirs-sys-setting-core已安装版本:[5.1.24],启动中包含的版本:[5.1.12] ······ 解决方案:修改 startup.sh 中的版本修改docker pull 命令中的版本(注意:具体版本号查看https://doc.oinone.top/xin-ren-ru-men/18758.html中) 4、执行vi startup.sh出错,可能是没有修改这个文件的权限,执行chmod 777 startup.sh即可授权

    2024年11月8日
    2.1K00
  • 工作流动态表单使用说明

    需求背景 为了提高操作效率并简化流程设计过程,应对伙伴们反映的在流程设计器中,即使填写/审批流程相同,不同模型也需重新配置的问题,我们引入了“动态表单”功能。此功能旨在减少重复配置的需求,通过设置节点名称和绑定视图,便可实现审批流程相同而视图不同,从而使得相同的审批流程可以被高效重复利用。 核心功能是:通过自定义函数实现视图的动态渲染,如图所示《动态表单函数》。 自定义动态表单函数 动态表单函数定义规则如下: namespace:强制为 WorkflowFunctionConstant.FUNCTION_NAMESPACE。 fun:强制以 WorkflowFunctionConstant.WORKFLOW_CUSTOM_VIEW_FUNCTION_PREFIX 为前缀。 入参说明: 参数1:节点数据,例如,配合instanceof可以判断当前是填写节点(WriteNode)还是审批节点(ApprovalNode)。 参数2:触发节点的模型数据,如果您的触发节点不确定,可以通过Map接收参数。 参数3:该节点所配置数据来源的数据。 出参说明:视图,如果出参为null,流程终止运行,错误信息提示为“流程节点执行失败,动态表单函数获取视图为空”。 @Slf4j @Component @Fun(WorkflowFunctionConstant.FUNCTION_NAMESPACE) public class DynamicFormCustom { /** * 根据动态表单任务获取视图 * * @param node 节点数据 * @param dynamicFormTask 触发节点数据 * @param dataObj 源数据 */ @Function.fun(WorkflowFunctionConstant.WORKFLOW_CUSTOM_VIEW_FUNCTION_PREFIX + "fetchDynamicFormFunction") @Function.Advanced(displayName = "[内置]获取动态表单函数") @Function(name = "fetchDynamicFormFunction") public View fetchDynamicFormFunction(Node node, DynamicFormTask dynamicFormTask, Map<String, Object> dataObj) { DynamicFormTaskNode dynamicFormTaskNode = fetchDynamicFormTaskNode(node, dynamicFormTask); if (dynamicFormTaskNode == null) { return null; } dynamicFormTaskNode.fieldQuery(DynamicFormTaskNode::getView); return dynamicFormTaskNode.getView(); } private DynamicFormTaskNode fetchDynamicFormTaskNode(Node node, DynamicFormTask dynamicFormTask) { List<DynamicFormTaskNode> dynamicFormTaskNodeList = Models.origin().queryListByWrapper(Pops.<DynamicFormTaskNode>lambdaQuery() .from(DynamicFormTaskNode.MODEL_MODEL) .eq(DynamicFormTaskNode::getNodeName, node.getNodeName()) .eq(DynamicFormTaskNode::getTaskCode, dynamicFormTask.getCode()) ); if (CollectionUtils.isEmpty(dynamicFormTaskNodeList)) { return null; } if (dynamicFormTaskNodeList.size() > 1) { log.error("工作流动态获取表单函数视图匹配多个,{}", JsonUtils.toJSONString(dynamicFormTaskNodeList)); } return dynamicFormTaskNodeList.get(0); } } 相关链接 工作流动态表单最佳实践

    2025年2月7日
    1.3K00
  • 3.5.7.2 自定义母版

    母版是什么 在业务系统中,母版是一种全局通用的组件,用于包裹每个页面的元素。这个概念类似于 PPT 中的母版,它定义了页面的整体结构、样式和布局,使得系统具有一致的外观和风格。 特点: 全局通用:母版是全局性的,适用于系统中的每个页面,确保一致性的用户体验。 包裹元素:母版包裹每个页面的元素,定义了整体的结构和布局。 外观一致性:通过统一的母版设计,系统达到外观和风格的一致性。 与 PPT 母版的类比: 在母版的概念上,与 PPT 中的母版类似,都是用于定义整体结构和样式,确保每个页面都具有一致的外观。 默认母版范围: 图3-5-7-20 默认母版范围 作用场景 在系统中,我们提供了多个纬度的母版,包括全局、应用和页面纬度。这样的设计允许根据不同的业务场景选择合适的母版纬度,以满足不同层次的定制需求。 母版纬度: 全局母版: 覆盖系统中所有页面,确保全局的一致性和统一的用户体验。 定义全局性的结构、样式和布局。 应用母版: 适用于特定应用或模块,定制化程度介于全局和页面之间。 允许在不同应用间实现一定的差异化。 页面母版: 面向具体页面,提供最大的定制化空间。 允许在不同页面中定义不同的结构和样式。 选择纬度: 业务场景决定:根据具体的业务场景和需求选择合适的母版纬度。 定制化需求:如果需要全局一致性,选择全局母版;如果在应用层面有特定需求,选择应用母版;如果需要最大的灵活性和定制化,选择页面母版。 参数说明: 在系统中,使用 registerMask 注册母版时,需要提供两个参数。第一个参数是母版的 XML,第二个参数用于控制母版的注册行为,包括 module、moduleName、model 和 actionName。 母版 XML(第一个参数): 提供母版的 XML 描述,定义了母版的结构和样式。 控制参数(第二个参数): module:指定模块的名称。 moduleName:指定模块的显示名称。 model:指定母版所属的模型。 actionName:指定母版的操作名称,可以是字符串或字符串数组。 全局母版 可以使用 registerMask 注册母版。当第二个参数为 {} 时,即代表注册全局母版。 示例工程目录 以下是需关注的工程目录示例,并确保导入导出依赖正确: 图3-5-7-21 全局纬度注册母版工程目录示例 示例代码 以下是一个示例代码: import { registerMask } from '@kunlun/dependencies'; /** * mask: 在做系统时,我们通常会把外层的布局(菜单、顶部导航)等抽出公共组件,公共组件会抽离全局通用。类似ppt母版的概念即为Mask * registerMask 第二个入参为{}即注册了全局得母版 * * 和默认母版相比移除右上角消息、语言切换、用户 * `<block> * <widget widget="notification" /> * <widget widget="divider" /> * <widget widget="language" /> * <widget widget="divider" /> * <widget widget="user" /> * </block>` */ const registerGlobalMask = () => { registerMask(` <mask> <multi-tabs /> <header> <widget widget="app-switcher" /> </header> <container> <sidebar> <widget widget="nav-menu" height="100%" /> </sidebar> <content> <breadcrumb /> <block width="100%"> <widget width="100%" widget="main-view" /> </block> </content> </container> </mask>`, {}); } registerGlobalMask(); 图3-5-7-22 全局纬度注册母版代码示例 复制这段代码执行下registerGlobalMask()试试看吧 效果 右上角消息、语言切换、用户被移除。 图3-5-7-23 全局纬度注册母版效果示例 应用母版 在系统中,可以使用 registerMask 注册母版。当第二个参数为 {moduleName: ‘resource’} 时,即注册了 resource(资源)应用的母版。 示例工程目录 以下是需关注的工程目录示例,更新导出./moduleMask: 图3-5-7-24 应用纬度注册母版工程目录示例 示例代码 以下是一个示例代码: /** * registerMask 第二个入参为{moduleName: 'resource'}即注册了resource(资源)应用的母版 * * resource来源: 通常是浏览器url上的module * * 和默认模版相比拿掉了菜单 * `<sidebar> * <widget widget="nav-menu" height="100%"…

    2024年5月23日
    1.8K00

Leave a Reply

Please Login to Comment