
Wiki.js 性能调优实战4 个改动把知识库首屏从 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.js 是一个现代的 Node.js 知识库应用。我们生产实例的首屏平均要 2.8 秒编辑保存后要盯着转圈。这次复盘 4 个有真实数据的改动最终首屏压到 0.9 秒保存等待基本消失。先说结果首屏 2.8 秒到 0.9 秒靠这 4 个改动指标优化前优化后页面首屏平均2.8 秒0.9 秒首次访问静态资源传输量2.9 MB0.8 MB重复访问的资源请求数38 个0 个高峰300 并发接口 p951.6 秒0.5 秒实例常驻内存约 600 MB约 150 MB下文按用户最先遇到的三个症状拆解每个改动动了什么、量化收益是多少、当时又走了哪些弯路。定位用三条数据找到瓶颈动手之前先把基线立起来测量工具就四类浏览器开发者工具的 Network 与 Performance 面板录制一次完整加载Lighthouse 跑一次完整性能评估得分 41服务端开启 SQL 日志半天配合数据库 EXPLAIN 复核压测脚本打 300 并发、持续 60 秒记录吞吐和错误率。证据很具体三条资源体积大且每次重复下载首次加载 JS/CSS 合计 2.9 MB响应没带 Cache-Control第二次访问照样全量下载 2.9 MB——而文件名含构建时戳在同一版本内根本不变本来就能放心缓存匿名请求反复走渲染管线服务端日志显示大量匿名页面请求完整跑了一遍 Markdown 渲染管线加 cheerio 目录解析见server/jobs/render-page.js而不是命中缓存慢查询加连接排队SQL 日志里抓到多条超过 500 毫秒的语句300 并发下接口 p95 被拉到 1.6 秒。结论很直接机器没毛病是该缓存的没缓存、该压缩的没压缩、该挪走的活没挪走。打开页面慢给静态资源加 Gzip 和一年期长缓存现象打开页面转圈两秒多Network 面板里一堆 JS/CSS 每次都在重新下载Lighthouse 里资源传输这一项扣得最狠。改动三处。第一Nginx 上给文本资源开 Gzip、给/_assets/路径设一年缓存。Wiki.js 构建产物输出到assets/目录publicPath 是/_assets/dev/webpack/webpack.prod.js的output.filename在构建时拼接时间戳${now}同一版本内文件名稳定这就是敢上长缓存的前提gzip on; gzip_comp_level 5; gzip_types text/css application/javascript application/json image/svgxml; location /_assets/ { expires 1y; add_header Cache-Control public, immutable; }换句话说文件没变时浏览器直接用本地副本连请求都不发只有重新构建产生新文件名才会重新下载。第二打包配置把第三方库统一归进 vendor chunk主包只留应用自身代码splitChunks: { chunks: all, cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: vendor } } }说白了vendor 包几乎不变、长缓存命中率极高应用代码更新时只有小块 diff 需要重新下载。重打包后主包从 4.1 MB 降到 2.6 MB。编辑器本来就是动态 import 懒加载的这次特意检查了别把它改回同步 require。第三给匿名读者在 Nginx 加一层页面级缓存命中直接返回 HTML渲染管线完全不进proxy_cache_path /var/cache/nginx/wiki keys_zonewiki:10m max_size1g; location / { proxy_cache wiki; proxy_cache_valid 200 5m; proxy_cache_bypass $http_authorization; }换句话说只缓存没有鉴权信息的请求5 分钟够用不要把登录态页面也缓存——缓存键一旦不区分身份用户 A 会拉到用户 B 的私有页面直接权限串号。效果首次传输量 2.9 MB → 0.8 MB降约七成重复访问资源请求 38 → 0平均首屏 2.8 秒 → 0.9 秒这是首屏收益最大的一步。弯路一开始把expires 1y套到所有路径页面 HTML 也被长缓存内容更新后几小时不生效只好回滚成资源长缓存、HTML 短 TTL。操作要等待给内存缓存设过期时间渲染挪出主进程现象保存长文档后要盯着转圈三四秒实例常驻内存从 300 MB 一路爬到 600 MB越跑越大。改动两处。第一给server/core/cache.js里内置的 NodeCache 补上默认过期时间return new NodeCache({ stdTTL: 600, checkperiod: 120 })说白了缓存键 10 分钟自动失效NodeCache 每 2 分钟清一次卫生没人看的历史页面不再在内存里越堆越多。第二把页面渲染挪出主进程。仓库自带独立 job 入口worker.js渲染逻辑在server/jobs/render-page.js生产环境让渲染以独立进程跑避免 CPU 密集的 Markdown 管线和 Web 请求抢事件循环node server/core/worker.js --job render-page --data 12这行命令的意思是主进程只负责收请求吐响应渲染这种重活由独立子进程干还能并行多个。效果常驻内存 600 MB → 约 150 MB 且稳定编辑保存的等待从约 4 秒降到 0.5 秒以内因为渲染异步化了页面立即可读。弯路stdTTL 第一版写成 86400一天编辑后两小时还有同事看到旧内容被迫回滚到 600。高峰期服务器吃力调连接池开 SQL 日志揪慢查询现象工作日上午高峰接口 p95 被拉到 1.6 秒偶发请求超时CPU 没打满但服务端日志里排队记录越来越多。改动先调池子。config.sample.yml里pool的 min/max 两行默认是注释掉的Knex 用保守默认值。连接池像餐厅座位太少高峰全在门口排队太多白占数据库连接和内存pool: min: 2 max: 8一句话常备 2 条连接上限 8 条按单实例并发配改完重启生效。再让数据决定下一步加什么索引config.yml的flags下开sqllog: true跑半天配合 EXPLAIN 看完就关flags: sqllog: true这步是让服务端把每条 SQL 都打出来N1 查询和缺索引的慢语句会自己跳出来这个日志很吵定位完务必关掉否则正常日志全被埋。效果接口 p95 从 1.6 秒降到 0.5 秒高峰时段排队超时告警归零。弯路一开始把 max 直接拉到 50数据库侧开始报too many connections又改回来了。走过的弯路这几个优化落地后全被回滚按错误做法、后果、正确姿势记录Gzip 全量开启连图片一起压→ CPU 明显飙升传输量几乎没变 → 只压 CSS/JS/JSON/SVG 这类文本。页面缓存 TTL 设成一天→ 内容更新后用户长时间看到旧版 → 分钟级短 TTL加上保存时主动失效。chunk 拆得过细→ HTTP/1.1 下请求数爆炸实测更慢 → 要么合并要么先上 HTTP/2。缓存登录态页面→ 权限串号用户看到不该看的页面 → 只对匿名 GET 生效或缓存键带上鉴权信息。Docker 里直接扩 4 个实例→ 共享同一数据库各实例内存缓存各管各的内容不一致 → 确实要扩容就开启config.sample.yml的ha标志并统一失效策略。验证验证靠的是四组固定数据不靠体感DevTools Performance 录制一次完整加载看主线程与网络瀑布Lighthouse 完整跑一遍性能分 41 → 86服务端响应耗时日志连看三天盯 p95压测 300 并发持续 60 秒记录吞吐与错误率错误率 0%。最终对比优化项预期收益实施成本静态资源 Gzip 一年期长缓存体积降约七成重复访问零下载低改 Nginx 配置NodeCache 设 TTL 渲染挪到 worker内存 600 MB → 150 MB保存等待 0.5 秒内中改一行加启动方式连接池调参 SQL 日志定位慢查询p95 从 1.6 秒降到 0.5 秒低改配置加重启匿名页面级 5 分钟缓存渲染管线重复执行明显减少中反代配置最小行动清单只有十分钟的话就做这三件事反向代理层给/_assets/加 Gzip 和一年期长缓存——纯 Nginx 配置不动代码十分钟见效改server/core/cache.js一行new NodeCache()补上stdTTL: 600, checkperiod: 120重启服务——成本就是改一行加一次重启config.yml临时开flags: sqllog: true跑一天把慢查询和 N1 揪出来再关掉——纯配置改动记得用完切回。调优不是一锤子买卖先把配置层的收益拿到手再碰缓存和构建每次动手前先看数据。【免费下载链接】wiki-Wiki.js | A modern and powerful wiki app built on Node.js项目地址: https://gitcode.com/GitHub_Trending/wiki78/wiki-创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考