ITSWE博客

MediaWiki 性能工程实录:搜索从 22 秒到 0.55 秒

一万三千条 SQL 的缓存配置、膨胀四十倍的索引表、中文分词的先天硬伤、一次搜索引擎迁移——一座知识库的四阶段性能工程。

主站是一座 MediaWiki 知识库,五百多个词条、九千多道题。它的站内搜索曾经慢到影响收录:搜索引擎来抓搜索页,要等十几秒,甚至超时。这篇记录整条性能工程的路——四个阶段,每一阶段都踩在上一阶段的尸骸上。

第一阶段:缓存配置的低级错误

最初的单次匿名搜索,会产生一万三千多条缓存查询 SQL——系统消息、链接缓存、繁简中文变体,逐条回源数据库。根因是配置里缓存后端被设成了数据库表。修复是给 PHP 装内存缓存并把三层缓存全部指过去,匿名页面渲染时间直接减半,严格的对比实测给出了一点二倍的提升。

这个阶段留下的教训最大:容器重建会把临时安装的模块静默抹掉,性能回退了一整天无人察觉。对策是给模块加每日存活探针,丢了就在日志里写一行大写警报。

第二阶段:一张膨胀四十倍的索引表

缓存修好后,搜索依然偶发数秒级卡顿。诊断三板斧上场:PHP 分段打点、数据库进程列表抓现场、看索引文件的实际大小——最后这一项直接暴露真相:一张一千五百行的搜索索引表,物理文件膨胀到一百多兆,正常的应该是二十兆上下。原因是当天上百次批量编辑,每次编辑都往增量段追加,全文索引碎片化劣化。重建这张表,耗时回落到两秒。由此立下运维约定:批量导入之后,必须重建搜索索引。

第三阶段:中文搜索的先天硬伤

到了这一步,长查询依然是五到二十秒,而且藏着一个更狠的雷:查询里带书名号,直接五百。追到原始 SQL 才看清两层机制——框架对中文查询逐字插空格,再把超过十个词的串包成短语查询,而数据库对短语查询的词数有硬上限,超了就报错。中间还试过一次换解析器,方向错了,改表期间内存又超限崩了一次,连夜回退。

当时的修复是给框架核心打补丁:超词的短语查询降级为逐词与运算。能用,但长查询还是秒级。根治要等下一阶段。

中文搜索硬伤的技术细节

第三阶段那个“带书名号就五百”的雷,值得单独解剖,因为它是中文搜索的代表性难题。机制是这样的:搜索层面对中文查询会把字符逐字切开建立检索词;对超过十个词的长串,它有个“中文补丁”会把整串包成短语查询以求精度;而底层数据库对单个短语的词数有硬性上限——三层机制叠加,长中文查询必然触雷。更麻烦的是,限制这个上限的配置项在新版数据库里已被移除,调参这条路是死的。

第一版补丁的思路是降级:超过阈值的查询不再包短语,改成逐词的与运算——牺牲一点精度换可用性,实测从直接报错变为秒级返回。但要根治精度问题,数据库全文检索对中文的先天不足(分词、索引结构)绕不过去,这就是第四阶段换专门搜索引擎的动因。这段经历给中文站长的提示是:选内容框架时,把“中文全文搜索的实现方案”当作一级评估项,它决定了后期是打补丁还是换引擎。

第四阶段:换引擎

最终方案是上专门的搜索扩展加独立搜索引擎。版本匹配是个深坑:框架新版本只兼容某个特定大版本的开源搜索引擎,而索引模板用到了一个只有官方增强插件才提供的分析过滤器——这个插件偏偏没有对应版本的开源构建。死路里找出活路:给框架核心的配置构造器打补丁,把五个位置的过滤器替换成内置等价物。

迁移后的实测:最刁钻的长中文查询从八到二十二秒降到零点五五秒;直连引擎压测,单机每秒四百多次查询,尾部延迟一百一十毫秒;整页搜索的吞吐天花板约每秒十次,正好是四个核心被动态渲染打满的理论值——真实流量峰值一到两次每秒,余量十倍。

诊断三板斧的具体操作

慢日志不可靠的原因值得展开:按耗时阈值记录慢请求的日志机制,在高负载下经常整段空缺,你等它抓现场,它沉默。可靠的是三板斧。第一,分段打点:在可疑代码路径上插入带时间戳的日志,一次上线收一次数据,把“慢”分解到每一段;第二,数据库进程列表抓现场:慢发生时立刻查看进程状态列,那个状态词会直接告诉你它在等什么——等待全文索引初始化,就是我们这样抓到的;第三,看索引文件的实际大小:表行数不多而物理文件巨大,就是索引碎片化膨胀的实锤。

A/B 实测也要讲方法:同一台机上让新旧环境互相切换,同一组查询跑多轮取中位数,排除缓存的干扰——只测一轮的“提升十倍”多半是缓存温差。我们给每一步优化都留了前后数字:页面渲染零点四三秒到零点一九秒,全文搜索零点五五秒,整页吞吐每秒十点七次正好是四核被动态渲染打满的理论值,与排队论吻合——数字之间能互相印证,才敢写进文档。

资源账

性能工程的另一面是资源账,迁移搜索引擎后实盘过一次:引擎本身实占约一个 G 内存,配置了内存上限与低自杀优先级——宿主内存吃紧时它先被牺牲,保业务库;数据库因搜索卸载自然回落了三百兆;宿主可用内存加交换文件兜底。上线后连续观察一周确认稳态,才算真正收官。性能优化不是只看快,还要看你为这个快付出了多少常驻资源、它在资源紧张时的让位顺序——这些都在设计之初写清楚,而不是出事了再补。

方法论沉淀

整条路走完,留下三条可复用的判断。其一,慢日志常常不可靠,分段打点自己抓现场才是硬功夫。其二,性能问题的三位证人:数据库进程状态、索引文件的实际大小、分段耗时打点——它们不会说谎。其三,每一分优化收益必须有对比数字背书,我们给每一步都留了前后实测,没有数字的优化是玄学,有数字的优化才是工程。

运维性能MediaWiki
返回全部文章