news 2026/7/18 14:38:25

42-RDB查询拖慢首屏-用索引分页和快照缓存拆开

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
42-RDB查询拖慢首屏-用索引分页和快照缓存拆开

第42篇|RDB 查询拖慢首屏:用索引分页和快照缓存拆开

摘要:本地 RDB 数据多了以后,首屏卡顿经常不是 UI 写得慢,而是页面一出现就做了全表排序、统计和复杂过滤。更稳的做法是把首屏快照、分页查询和统计重算拆开:首屏先读轻量快照,列表分页加载,统计在写入后维护或后台刷新。

一个记录类应用首屏从 300ms 变成 2 秒,表面看是列表项太多,实际是首页 aboutToAppear 里同时查全部记录、按时间排序、计算月统计、再渲染卡片。数据少时没问题,半年后本地记录多了,首屏就被 RDB 查询拖住。修复时不能只优化组件,得把查询路径拆开。

这篇文章解决四个实际问题:

  1. 区分首屏快照、列表分页、统计重算三类读请求。
  2. 给常用排序和筛选字段补索引,不在页面里全量排序。
  3. 写入后更新轻量摘要,首屏先用摘要展示。
  4. 用耗时日志证明慢在查询还是渲染。


先把首页查询拆成三类

首屏慢时不要只看列表组件。先列出首页到底读了哪些数据,每一类数据是否都必须阻塞首帧。

位置现象风险处理方向
今日摘要只需要总数和最近一条全表扫描太重读 SummaryStore
记录列表需要分页一次查全部卡住limit/offset 或游标
月统计计算量大不该阻塞首屏后台刷新
筛选排序按时间和类型查无索引导致慢迁移补索引

这一步的价值是先把问题放回运行链路。只要能确认问题停在哪个位置,后面就不用靠猜测改页面。

页面不能自己拼复杂查询

页面应该向 Service 要 HomeSnapshot 和 PageResult,而不是直接拿 repository 全量数据后自己排序过滤。

模块应该负责不应该负责
HomePage快照、分页结果、加载态SQL 条件
HomeService组合首屏数据RDB 连接细节
RecordRepository分页和筛选查询卡片展示
SummaryStore轻量摘要缓存业务按钮
Migration索引和版本升级页面生命周期

边界定清后,代码就不会在页面、服务和回调之间来回复制同一段判断。后续新增场景也能先判断应该落在哪一层。

首屏快照只放轻量字段

exportinterfaceHomeSnapshot{todayCount:numberlatestTitle:stringlatestTime:numbermonthTotal:numbergeneratedAt:number}exportinterfacePageQuery{cursorTime:numberpageSize:numbertypeFilter:string}exportinterfacePageResult<T>{items:T[]nextCursorTime:numberhasMore:boolean}

首屏快照是为快速展示设计的,不放完整列表。分页查询用 cursor 表达下一页位置,避免每次都从头读到尾。

这类模型最好放在modelscommon中,页面、Service 和仓储都使用同一份类型,避免各层用字符串互相猜。

Repository 只提供分页读

exportclassRecordRepository{asyncqueryPage(query:PageQuery):Promise<PageResult<RecordItem>>{constrows=awaitthis.queryRows({beforeTime:query.cursorTime,limit:query.pageSize+1,typeFilter:query.typeFilter})constitems=rows.slice(0,query.pageSize)constlast=items[items.length-1]return{items,nextCursorTime:last?.createdAt??0,hasMore:rows.length>query.pageSize}}}

Repository 不返回全量列表。多查一条用来判断hasMore,页面只关心下一页 cursor,不需要知道底层查询细节。

Service 的目标不是把所有逻辑都塞进去,而是把跨页面、跨生命周期或需要持久化的判断集中起来。页面只表达用户动作。

首页先显示快照,再加载列表

@Componentstruct RecordHomePage{@Statesnapshot:HomeSnapshot=createEmptyHomeSnapshot()@Staterecords:RecordItem[]=[]@StateloadingMore:boolean=falseprivateservice:HomeRecordService=createHomeRecordService()asyncaboutToAppear():Promise<void>{this.snapshot=awaitthis.service.loadHomeSnapshot()awaitthis.loadFirstPage()}privateasyncloadFirstPage():Promise<void>{constpage=awaitthis.service.loadRecords({cursorTime:Date.now(),pageSize:20,typeFilter:''})this.records=page.items}}

首屏不会等月统计重新计算完成才显示。快照先给用户一个稳定首页,列表再分页补齐。

页面层要控制展示和交互节奏,但不应该拥有底层事实。这样页面重建、横竖屏变化或返回前台时,都能重新从 Service 拿到可信结果。

写入后同步更新摘要

exportclassHomeRecordService{constructor(privatereadonlyrepository:RecordRepository,privatereadonlysummaryStore:SummaryStore){}asyncsaveRecord(input:SaveRecordInput):Promise<void>{constrecord=normalizeRecord(input)awaitthis.repository.save(record)awaitthis.summaryStore.updateAfterWrite(record)}asyncloadHomeSnapshot():Promise<HomeSnapshot>{returnthis.summaryStore.loadSnapshot()}}

摘要不是页面临时算出来的,而是在写入路径维护。这样下一次打开首页时能直接读快照,不用全表重算。

实际项目里,很多问题不是第一次进入页面暴露,而是在冷启动、返回前台、页面重建或回调晚到时暴露。把这段生命周期补上,修复才算闭环。

慢查询要留下耗时证据

情况页面表现工程处理
首屏快照慢记录 SummaryStore 耗时检查是否退回全表统计
分页慢记录 cursor 和 pageSize检查索引和过滤字段
写入后摘要错对比原始记录重算修 updateAfterWrite
列表渲染慢查询快但首帧慢再看组件和图片

兜底路径不是可有可无。用户遇到异常时,页面至少要给出当前状态、可执行动作和可排查线索。

排查时找全量查询和排序

rg-n"queryAll|findAll|ORDER BY|sort\(|filter\("entry features common rg-n"limit|offset|cursor|pageSize|SummaryStore|HomeSnapshot"entry features common rg-n"aboutToAppear|onShown|loadHome|loadRecords"entry features common

第一组命令找旧路径,第二组命令找新边界,第三组命令看生命周期或回调位置。命中后不要只看字段名,要判断它是否仍在直接改页面状态。

索引和摘要要跟版本迁移一起走

很多首屏慢的问题不是某一次查询写错,而是版本演进时没有同步维护本地结构。新增筛选条件后,页面开始按typecreatedAtupdatedAt查,但迁移里没有补索引;新增首页统计后,写入路径没有更新摘要,只能在首页临时全量重算。

变更需要同步处理不处理的后果
新增时间排序给时间字段补索引数据多后排序拖慢首屏
新增类型筛选给类型和时间组合查询设计路径筛选页每次全表过滤
新增首页统计写入后更新摘要首页打开时全量重算
新增导入功能导入结束后重建摘要导入后统计和列表不一致

所以 RDB 优化不是最后才做的性能补丁。每次本地数据结构变化,都应该顺手问一句:首屏读什么、列表怎么分页、摘要在哪里更新、旧版本升级后这些结构是否存在。

验证清单

验证项预期结果
千条记录冷启动先出现快照,再分页出列表
新增记录首页摘要立即更新
加载更多每次只追加一页
筛选切换查询耗时可记录
无记录空状态不触发全量统计循环

这些验证最好在真机或模拟器里按顺序走一遍。只看源码很难发现冷启动、重启和回调晚到这类问题。

常见问题和处理方式

现象常见原因处理方式
首屏 2 秒全量查加统计阻塞快照和分页拆开
越用越慢无索引且全表排序迁移补索引
统计不准页面手动改计数写入后统一更新摘要
列表卡顿一次渲染全部记录分页加载

如果现象没有出现在表里,仍然建议按“输入来源 -> 中间状态 -> 持久化或页面展示 -> 失败兜底”的顺序排查。

小结:首屏不要背负全量数据

RDB 首屏慢要先拆读路径。首页读快照,列表分页,统计在写入或后台维护,慢查询有日志。这样数据增长后,首屏仍然是一个可控链路,而不是所有本地数据的总入口。
|
| 列表卡顿 | 一次渲染全部记录 | 分页加载 |

如果现象没有出现在表里,仍然建议按“输入来源 -> 中间状态 -> 持久化或页面展示 -> 失败兜底”的顺序排查。

小结:首屏不要背负全量数据

RDB 首屏慢要先拆读路径。首页读快照,列表分页,统计在写入或后台维护,慢查询有日志。这样数据增长后,首屏仍然是一个可控链路,而不是所有本地数据的总入口。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/18 14:36:06

思考:要做到Bug Free需培养怎样的思维?

一般来说&#xff0c;刷题、写代码想要快速入门并达到Bug Free的水平是几乎不可能的&#xff0c;只有勤练习&#xff0c;才可以逐渐全面自己思考问题的角度&#xff0c;减少编写代码时候的漏洞。而在炼成 Bug Free 的漫漫长路上&#xff0c;又有两重坎&#xff1a;写无 Bug 的代…

作者头像 李华
网站建设 2026/7/18 14:35:09

任天堂Switch注入神器TegraRcmGUI:5分钟从零到精通

任天堂Switch注入神器TegraRcmGUI&#xff1a;5分钟从零到精通 【免费下载链接】TegraRcmGUI C GUI for TegraRcmSmash (Fuse Gele exploit for Nintendo Switch) 项目地址: https://gitcode.com/gh_mirrors/te/TegraRcmGUI 核心关键词&#xff1a;Switch RCM注入、Tegr…

作者头像 李华
网站建设 2026/7/18 14:32:55

Translumo:免费开源实时屏幕翻译的终极解决方案

Translumo&#xff1a;免费开源实时屏幕翻译的终极解决方案 【免费下载链接】Translumo Advanced real-time screen translator for games, hardcoded subtitles in videos, static text and etc. 项目地址: https://gitcode.com/gh_mirrors/tr/Translumo 你是否曾经因为…

作者头像 李华
网站建设 2026/7/18 14:32:20

如何快速提升老旧窗口清晰度:Magpie超分辨率工具的终极指南

如何快速提升老旧窗口清晰度&#xff1a;Magpie超分辨率工具的终极指南 【免费下载链接】Magpie A general-purpose window upscaler for Windows 10/11. 项目地址: https://gitcode.com/gh_mirrors/mag/Magpie 你是否曾为那些不支持高分辨率的老旧游戏或应用程序在现代…

作者头像 李华
网站建设 2026/7/18 14:31:51

VESC Tool完全指南:5分钟掌握开源无刷电机控制器配置

VESC Tool完全指南&#xff1a;5分钟掌握开源无刷电机控制器配置 【免费下载链接】vesc_tool The source code for VESC Tool. See vesc-project.com 项目地址: https://gitcode.com/gh_mirrors/ve/vesc_tool VESC Tool是一款功能强大的开源无刷电机控制器配置工具&…

作者头像 李华