ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Wiki.js 性能优化:首屏从 2.8 秒到 0.9 秒——一次完整的诊断、度量、修复、验收复盘

Wiki.js 性能优化:首屏从 2.8 秒到 0.9 秒——一次完整的诊断、度量、修复、验收复盘 Wiki.js 性能优化首屏从 2.8 秒到 0.9 秒——一次完整的诊断、度量、修复、验收复盘【免费下载链接】wiki-Wiki.js | A modern and powerful wiki app built on Node.js项目地址: https://gitcode.com/GitHub_Trending/wiki78/wiki-我们用 Wiki.jsNode.js 知识库系统跑一套内部知识库升级后匿名首屏要等 2 秒多编辑长文档后转圈 3~4 秒。本文用真实数据复盘这次 Wiki.js 性能优化全过程诊断、度量、修复、验收与固化。【诊断】定位 Wiki.js 页面加载慢的原因先查证据别急着修排查前我们只列症状不猜原因首屏 2 秒以上资源在重复下载。打开浏览器 Network 面板看 Size 和 From Disk Cache 两列JS/CSS 合计约 1.9MB缓存列基本是空的说明每次访问都在重传。匿名请求每次都在走完整渲染。服务端日志里几乎每个页面请求都打了一条Rendering page ID记录——这是 server/jobs/render-page.js 渲染 job 的日志说明 Markdown 渲染管线加 cheerio 目录解析对每个访问都完整跑了一遍。高峰期接口排队。300 并发下 p95 从 300ms 爬到 800ms 以上看 config.sample.yml 发现pool段整体被注释连接池落在保守默认值上。内存只涨不降。进程常驻 30 天从 300MB 涨到 1.4GB 且不再回落对应 server/core/cache.js 里的new NodeCache()没有任何参数缓存键没有过期机制。一句话结论机器没病卡点在于该缓存的没缓存、该算一次的在重复算、该放行的连接在排队。【度量】建立 Wiki.js 提速基线统一口径先拿病历全文指标口径固定避免自说自话首屏 LCPPerformance 面板录 5 次冷启动取中位数接口 p95wrk 打 300 并发、持续 60 秒取 95 分位首屏传输体积Network 面板 transfer size 求和压缩后口径。指标优化前测量方式首屏 LCP2.8sPerformance 面板 5 次冷启动中位数首屏传输体积约 1.9MBNetwork 面板 transfer size 求和接口 p95300 并发约 850mswrk 300 并发 60s 压测进程内存运行 30 天1.4GB 且持续上涨常驻监控取峰值【处方】降低 Wiki.js 首屏延迟的三处修改每张卡对应一个症状和看病一样病历拿齐才开始开方。以下三张处方卡每张只解决一个症状。症状一每次访问都重复下载整套 JS 和 CSS症状首屏传输约 1.9MB老用户再次访问仍产生大量 200 响应。根因dev/webpack/webpack.prod.js 里output.filename是js/[name].js?${now}——URL 自带构建时间戳查询串同一构建内内容不可变查询串等于版本号但反代层没给/_assets/设长缓存头浏览器每次访问都当新资源处理。改法在反代层加压缩与长缓存8 行以内gzip on; gzip_comp_level 5; gzip_types text/css application/javascript application/json; location /_assets/ { expires 1y; add_header Cache-Control public, immutable; }为什么这么改文件名变了才是新版本不变就是一年长缓存浏览器直接复用本地副本gzip 只列文本类型不碰图片二进制。效果复访传输从 1.9MB 降到约 0.2MB本地命中首访传输压缩后降约七成LCP 减少约 0.6 秒。症状二并发升高时接口排队内存只涨不降症状高峰期接口 p95 破 800ms进程常驻 30 天涨到 1.4GB。根因server/core/cache.js 的init()只有new NodeCache()无参构造缓存键无过期历史页面数据只进不出同时连接池未显式配置。为确认数据库侧开销我们开启了flags.sqllogserver/app/data.yml 中默认 false由 server/core/config.js 的applyFlags()映射到 Knex debug 打印全部 SQL一天内揪出 3 处 N1 查询和 1 条缺索引语句定位完立即关闭。改法缓存加过期与定期清理return new NodeCache({ stdTTL: 600, checkperiod: 120 })连接池按样例自带建议值放开pool: min: 2 max: 10为什么这么改stdTTL让热点页面 10 分钟到期自动失效checkperiod定期清掉过期键内存不再无限累积pool 是 config.sample.yml 里本就注释好的官方建议值只解除注释并重启。效果运行 30 天后内存稳定在 400MB 以内同 300 并发下 p95 预期从 850ms 降到约 320ms。症状三主包体积大更新后用户重传一大块症状主 JS 未压缩超 1.2MB任何小改动都让用户重下大块 vendor。根因dev/webpack/webpack.prod.js 中optimization.splitChunks仅有{ name: vendor, minChunks: 2 }没开chunks: all第三方库和应用代码混在同一个主包MomentTimezoneDataPlugin还打进了 2017 年起加未来 5 年的全量时区表。改法显式把第三方库固定进独立、稳定的 vendor chunksplitChunks: { chunks: all, cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: vendor, priority: 10 } } }为什么这么改客户端已对编辑器、管理后台等重路由做了动态 import 懒加载见 client/client-app.js主包只差 vendor 这一刀同时把MomentTimezoneDataPlugin的startYear/endYear收窄到用户实际所在区间预期再省约 200KB 时区表。另外构建侧cache-loader缓存目录.webpack-cache/已配置好部署脚本别再清它增量构建预计快 2~3 倍。效果预期主包缩小约四成版本更新后用户只需下载应用代码的小块 diff。【避坑】回滚过的 Wiki.js 性能优化反模式用 Gzip 连图片一起压二进制图片压缩率几乎为零CPU 白烧高峰期更卡。正确做法gzip_types只列文本类 MIME保持上面 8 行配置不变。把缓存 TTL 一味拉长页面更新后用户长时间看到旧版投诉比慢更伤。正确做法短 TTL分钟级加发布时主动失效用新鲜度换稳定。在反代层缓存登录态页面⚠️ 这是安全红线。同一 URL 对不同权限组内容不同缓存命中即越权串数据。正确做法只缓存匿名 GET缓存键中携带鉴权信息登录态响应保持 no-store。多实例共享数据库却不处理缓存一致性config.sample.yml 里ha标志存在就是为此——多个实例同库时各实例内存缓存各管各的A 实例改了内容 B 实例看不到。正确做法开ha并配合统一失效流程再扩容。把 chunk 拆得过于细碎HTTP/1.1 下请求数爆炸反而更慢。正确做法先上 HTTP/2 再细拆或维持一个稳定 vendor 懒加载路由的粗粒度。【验收与固化】对比 Wiki.js 提速前后数据固化监控机制指标口径与度量节一一对应指标优化前优化后测量方式同前首屏 LCP2.8s0.9sPerformance 面板 5 次冷启动中位数首屏传输体积约 1.9MB首访约 0.6MB / 复访约 0.2MBNetwork 面板 transfer size接口 p95300 并发约 850ms约 320ms预期wrk 300 并发 60s进程内存30 天1.4GB 持续上涨稳定 400MB 内常驻监控峰值长效机制四条防止回退每周跑一次 300 并发 60 秒压测p95 超过 500ms 触发告警并复查慢查询日志每次前端发版后重测 LCP超过基线 20% 就回滚构建参数改动每月检查一次进程常驻与数据库慢查询日志flags.sqllog只在排障时开一天新增实例前确认ha与统一缓存失效流程已就绪再谈扩容。复盘到此收束一次只改一处每处都挂上数字让数据划线。现在 10 分钟内能做完的三件事给反代/_assets/加 gzip 与一年长缓存给 server/core/cache.js 补上stdTTL与checkperiod重启服务开启flags.sqllog跑一天揪完慢查询再关掉。【免费下载链接】wiki-Wiki.js | A modern and powerful wiki app built on Node.js项目地址: https://gitcode.com/GitHub_Trending/wiki78/wiki-创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表