这篇写给正在把 Moodle 放到生产环境上的高校继续教育学院、职业培训机构、企业大学与考试中心。Moodle 是开源的学习管理系统,部署门槛极低,一台最普通的 LAMP 机器就能装起来跑通,但「能跑」和「300 人同场开考不卡」之间隔着好几道具体机制。本文不谈教学功能,只回答一个可被量化的问题:Moodle 的服务器容量到底是被什么吃掉的,以及这种情况下该怎么选机器、先加什么、别加什么。
谁需要看这篇:已经确定用 Moodle 或正在从其他平台迁移、并且面临统一时间开考这类集中负载的团队。在哪里跑:中国大陆站点、中国香港站点、以及中国澳门与中国台湾地区节点的使用方,机房选择会影响的是学生侧的首字节时延,不会改变下面这些资源消耗机制。为什么要算:因为 Moodle 的容量不能按 PV 或注册人数估,只能按「登录态动态请求」和「随机写入行数」估。
Moodle 在生产上是三层加一块盘:Web 服务器(多数用 Nginx)负责 TLS 与静态文件,PHP-FPM 负责脚本执行,关系型数据库(MySQL、MariaDB 或 PostgreSQL)负责状态,moodledata 这个文件系统层负责课程文件、作业、缓存与会话。这四者里,最先被压垮的从来不是静态图片。
关键区别在有没有会话。未登录的首页可以命中整页缓存或被 CDN 接管,一个请求几十毫秒就结束;带了 PHPSESSID 的课程页则要先跑 require_login,做上下文检查、选课状态判断、角色权限计算、语言串加载与主题渲染,每一个未缓存的登录态请求都要完整走一遍 PHP 并打若干次数据库。Nginx 的 proxy_cache 默认会跳过带 Set-Cookie 的响应,POST 请求通常也不进整页缓存,所以登录态页面基本拿不到反向代理的保护。
因此估算容量时应该用的口径是「登录态动态请求每秒」,不是日活或 PV。一个可以直接用的换算式:所需常驻 PHP-FPM 进程数约等于峰值动态请求 QPS 乘以平均响应时间(秒)。当平均响应时间是 0.4 秒、峰值是 25 QPS 时,就需要约 10 个 worker 同时在跑;如果响应时间因为某个慢插件涨到 1.5 秒,同样的 QPS 需要约 38 个 worker。人数没变,机器先崩。
用平均值做计划还不够,真正决定用户体验的是高百分位。假设 90% 的请求在 0.3 秒内返回,剩下 10% 慢到 3 秒,那么用户在考试中的感受是由那 10% 决定的,而这 10% 恰好占用的 worker 时间最长、最容易把队列堵住。所以测量响应时间时至少要看 P95 或 P99,并且把「打开试卷」和「保存答案」这两类请求分开统计,不要把页面浏览和写入动作混在一张图上。
日常浏览以读为主,考试场景完全不同。学生点开试卷的那一刻,Moodle 要创建一次尝试Attempt,随后最消耗写资源的四张表开始连续增长:mdl_quiz_attempts 记录一次尝试,mdl_question_attempts 为试卷里每一道题建一行,mdl_question_attempt_steps 记录每一次交互(浏览、保存、提交),mdl_question_attempt_step_data 把每个答案字段再拆成多行。这类写入是随机、短事务、高频率的。
可以粗算一次放大倍数。一份 50 题的试卷,300 人同时开考:一次性写入约 300 行 attempt,加上约 15000 行 question_attempts(预估),随后每次翻页和每道题的作答都会追加 steps 与 step_data。也就是说一次「打开试卷」动作在后端的写入量,可能是普通浏览课程页面的几十倍到上百倍。
这种负载的典型症状是 CPU 看起来已经跑满,但 Nginx 的请求速率并不高。这时候先怀疑数据库而不是 PHP:并发插入带来的自增锁竞争、二级索引维护、以及每个事务提交时的 redo 日志刷盘,都会把时间花在读请求的响应里。不同档次固态盘之间的差别在数据盘 IOPS 与 fsync 延迟上被放大得很明显。
还要提前考虑数据增长带来的连带成本。attempt 相关的表一旦达到几百万行,即使每天的业务量没有变化,索引维护、统计类查询和备份耗时会同步上升。一个很实际的例子是对某门课做成绩导出或对某个学生做历史查询,这类操作在大表上的耗时往往是上线初期的数倍。把这类运维动作安排在考试窗口之外,比临时调优索引更省事,也更容易向教学口交付。
Moodle 的测验带自动保存,默认间隔常见的设定是 60 秒左右,具体以站点实际配置的版本与设置为准。保存是页面上发起的 AJAX 请求,既占用一个 PHP-FPM worker,又在数据库端产生一批 steps 与 step_data 的写入。
如果整个年级在同一秒开考,所有自动保存的时刻会对齐,形成每 60 秒一次的锯齿尖峰,而不是均匀分布的平均负载。这边给两个成本很低的做法:让不同考场、不同班级错开 2 到 3 分钟发卷;把考试开始时间设成整点前后的非整数值,减少同时踩自动保存周期的巧合。监控上要看的是 InnoDB 每秒行写入量与 redo 日志产生的节奏,只看 CPU 曲线看不出锯齿。
还要提醒一点:定时任务不要在考试时段跑重型作业。站点统计重算、日志清理、课程自动备份都会同时争用同一个数据盘的 IOPS。把这些任务挪到凌晨或考试窗口之外,是零成本的容量提升。
这是最容易算错的一笔账。PHP-FPM 的每个子进程在处理过若干请求后会带上一份常驻内存,Moodle 单个 worker 的常驻量通常在 40MB 到 90MB 之间,预估值取决于装了多少插件、主题有多大、有没有在请求里构造大数组。整机都需要为这个数字预留。
计算式很简单:worker 常驻内存乘以 pm.max_children 就是 PHP 层的峰值常驻。以 60MB、40 个子进程估算就是 2.4GB;若实际涨到 80MB 而 max_children 设了 80,仅 PHP 就要 6.4GB,一台 8GB 的机器此时已经没有余量给数据库缓冲池、Nginx、Redis 和操作系统本身。反过来,Nginx worker 数量一般随 CPU 核数走,PostgreSQL 的 work_mem 会按连接数乘出去,这两项也要算进来。
内存不够时的后果不是变慢那么简单。Linux 的 OOM killer 倾向优先杀死占用最大的进程,多数情况下被杀的是数据库而不是 PHP,站点直接变成「数据库连接失败」的全站错误。所以务必给数据库预留明确的配额,并配置 pm.max_requests 让 worker 定期回收,避免长期驻留产生内存碎片累积。
这张表记录了全站几乎所有的事件:登录登出、查看课程、查看活动模块、答案保存、提交、成绩更新。它不服务于任何页面渲染,只服务于审计与报表,却通常是整个库里行数增长最快的表。几千万行在运营了两三年的站点上并不罕见(预估,看活跃度)。
Moodle 自带日志清理任务按 loglifetime 保留,但默认每天执行一批大 DELETE 也会带来问题:大事务会撑大 undo 表空间、阻塞复制、在磁盘上留下长时间不可释放的版本。更合理的做法是把保留期压缩到实际审计需要的最短天数,并考虑按月分区,清理时整分区 DROP 而不是逐行删除。要注意 InnoDB 的独立表空间文件不会因为 DELETE 变小,需要重建表才能真正回收磁盘,而重建会锁表,必须安排在维护窗口。
取舍很清楚:这张表既不能被放任增长,也不能简单地清空。先确认学校或监管方的审计留存要求,再按这个天数反推分区策略,最后才是清理的执行方式。
另一个可选方向是换写的位置,而不是控制写的量。日志记录层本身就是可插拔的,可以把标准日志导向独立的数据库或专门的日志后端,让业务库只承担当天的交易型读写。这样做的好处是把 I/O 压力从主库挪走,代价是多了一套需要维护的组件和一个需要同步的保留策略。
考试场景下增长最快的表是 attempt 相关那几张,但真正被反复读的「热数据」还包括用户信息、课程、上下文、角色指派、分组与选课相关表,以及每次请求都要判定的 permission 表。所谓容量瓶颈,本质是热数据集超出了缓冲池,导致每次请求都要落到磁盘上做随机读。
判断方法不需要猜。看 Innodb_buffer_pool_reads 与 Innodb_buffer_pool_read_requests 的比值,结合 SHOW ENGINE INNODB STATUS 里的 free buffers;同时用 information_schema.tables 统计库里各表 data_length 与 index_length 之和,把 attempt 相关的表单独摘出来看增速。如果热集大小明显超过当前缓冲池,加内存就是最直接的手段。
用 PostgreSQL 的站点要换一套口径。shared_buffers 通常只给内存的一小部分,剩余部分依赖操作系统页缓存,work_mem 则是按每个连接、每个排序节点乘出去的。考试时连接数成倍上升,把 work_mem 调大会直接导致内存爆炸。这种情况下先控制连接池上限,再谈参数调优。
还有一种容易被漏掉的开销来自磁盘临时表。排序缓冲装不下时 MySQL 会把中间结果写到磁盘,而在混合了读取和写入的考试场景下,这类操作会额外占用本来已经紧张的 IOPS。看 Created_tmp_disk_tables 与 Created_tmp_tables 的比值能大致判断是否需要介入。
MUC 是 Moodle 的通用缓存框架,它把内容切成若干缓存定义,其中几个对整个站点的性能影响极大:core/databasemeta 缓存表结构元数据,core/config 缓存配置,core/coursemodinfo 缓存课程结构,core/plugin_manager 缓存插件清单,还有字符串与事件失效相关的定义。
没有外部缓存时,这些定义默认落到本地文件或数据库。后果是每次请求都要重复做几十次元数据查询——尤其是 databasemeta,缺了它,Moodle 会反复去查 information_schema,把明明可以省掉的开销原样走一遍。装上 Redis 或 memcached 作为 application store 之后,这部分开销基本消失。
会话存储是另一件事。默认的文件型会话会在 moodledata/sessions 下为每个会话留一个文件,同时把这类小文件压力加到文件系统上;如果改成数据库型会话,每个请求都要在 mdl_sessions 里插入或更新一行,几百并发时会出现行锁等待与间隙锁竞争,表现为「登录之后点哪都转圈」。两种方案都不如直接在配置里启用 Redis 或 memcached 作为会话处理器,同一套 Redis 还能顺带承担锁工厂。
要注意的是,把会话、锁工厂和 MUC 应用缓存放进同一个 Redis 实例虽然常见,但三者对数据丢失的容忍度并不一样。缓存可以丢,锁丢失会导致任务重复执行,会话丢失则等于学生被强制登出。规模稍大的站点建议用不同实例或至少不同逻辑库分开,并且在运维动作里明确「清缓存」只允许作用于缓存那一块,不能整体执行 flushall。
Moodle 从较新版本开始把上传文件按内容哈希存放在 moodledata/filedir 下,文件名是哈希值、没有扩展名,再按两字符前缀分为两级目录。这个设计的好处是去重与易迁移,代价是很难用肉眼判断「这个目录属于哪门课」,也让数量变得比容量更重要。
一个 200MB 的教学视频和一个 5KB 的作业文档,消耗的 inode 完全一样。真正先撑爆的通常是作业提交、缩略图、缓存碎片、临时文件与旧的会话文件,而不是那几个大视频。检查命令应该是 df -i,而不是只看 df -h。
四个需要重点盯的目录:moodledata/temp 会在备份或报表任务失败时滞留大量临时文件;moodledata/trashdir 会保留被删除的文件直到超过设定周期;moodledata/cache 与 localcache 则随插件数量增长。给 moodledata 单独划分一个分区,选 XFS 这类对大量小文件与并发更友好的文件系统(具体以所用内核版本为准),并在格式化时确认 inode 数量够用,是并在格式化时确认 inode 数量够用,是成本极低的一步。
换算成运维习惯的话,就是每周看一次 inode 使用率的趋势,而不是等磁盘报警。inode 耗尽时的表现很反直觉:磁盘剩余空间还很充裕,但所有写入都失败,连临时文件和日志也无法生成。这种情况在考试进行中出现时,排查方向通常会被误导到数据库,反而耽误了处置时间。
课程自动备份会周期性生成 .mbz 文件,是否包含学生数据与日志取决于备份设置。单门课程一次几百 MB 很常见,一个开设几十门课的站点按周保留十周,累积量是几 GB 起步(预估)。这些文件同样以哈希形式落进 filedir,删除之后还要在 trashdir 里躺一段时间才真正回收。
最需要警惕的不是占了多少空间,而是把数据盘写满。MySQL 的数据盘一旦满了,写入会立刻报错,站点从「有点慢」直接跳到「完全不能提交答案」,这种故障在考试进行中出现几乎没有补救时间。所以备份目标目录应当指向独立挂载的另一个磁盘或远程存储,并设置明显的容量告警阈值。
另一条容易被忽略的是归档成本。整机快照对 inode 数量不敏感,但如果用 rsync 或打包的方式做离线归档,几百万个小文件的遍历时间会成为瓶颈。评估备份方案时可以同时看 GB 数和文件数这两个指标。
BigBlueButton 本身是另一套独立服务,通常需要单独部署,不应该和 Moodle 抢同一台机器的 CPU 与内存,Moodle 侧插件只负责 API 调用与状态回调。但即便如此,每个学生进入课堂仍然会产生 join 请求、出席同步与回调处理,PHP 侧的请求数随之上升。
H5P 的成本在另一处:内容预览与 xAPI 结果上报会写成专用的活动记录表,交互型课程越多,这张表的写入增长越接近考试场景。装了题库类插件的站点则要看答案判定的实现方式——某些题型会把判定交给外部程序执行,单次判定可能耗时几百毫秒到秒级(预估),期间 PHP worker 一直被占用。
结论是:插件决定了单个请求的时长,请求时长代入前面的公式会放大 worker 需求。选型时把插件带来的平均响应时间当作一个独立变量去测,别指望靠通用配置表覆盖。
PHP 的 opcache 缓存的是编译后的字节码,占用只取决于代码文件数与总字节数,与并发和用户数无关。Moodle 核心加上常见插件集的代码量不小,所以内存配额要给足,而不是照搬通用 Web 项目的默认值。
判断是否需要扩容的依据是 opcache_get_status 返回的状态:关注内存使用率、命中率、缓存键是否用满,以及是否出现过因为内存耗尽而触发的重启。一旦发生这种重启,所有请求退回重新编译,CPU 消耗会明显上抬。
在生产上常见的做法是关闭时间戳校验,彻底跳过每次请求的文件检查,由发布流程在部署完成后显式 reload PHP-FPM 或调用重置。这个配置能省下可观的文件系统调用,代价是忘记刷新会出现「上线后代码不生效」,必须写进发布清单。
同样的逻辑适用于另外一个常被忽略的参数:interned_strings_buffer。Moodle 里大量的字符串常量会在 intern 区域被共享,这块区域不足时不会像主缓存那样有明显的 miss 计数,排查起来更隐蔽。把它和主缓存一起按发布后的代码量调整,比事后追查更省事。
Nginx 应当负责 TLS 终端、HTTP/2、压缩、协议相关的超时设置与 theme 下的静态文件。需要注意 Moodle 的课程文件默认通过 pluginfile.php 这类脚本下发,如果全部由 PHP 传输,一个大文件的下载会长时间占住一个 worker。
解决办法是让 Nginx 承担实际传输:在 Nginx 里配置 internal 的内部路径,Moodle 侧开启 X-Sendfile 或 X-Accel-Redirect 并设置对应别名。启用后 PHP 只负责鉴权与响应头,字节流由 Nginx 发出去,同样的并发下 worker 占用时间大幅缩短。改动前务必验证权限控制没有被绕过。
还有一处必须对齐的参数链:Nginx 的 client_max_body_size、PHP 的 post_max_size 与 upload_max_filesize。作业上传或视频提交失败,绝大多数情况是这三个值不一致,而不是磁盘不够。
超时设置上还有一个常见错误:把 Nginx 的 fastcgi_read_timeout 设得比 PHP 的 max_execution_time 更短。课程备份、报表导出、一次性统计这类长任务会把连接卡在中间,表现为用户在浏览器里看到网关超时,而后台其实还在继续执行。正确做法是让三个值形成明确的层级关系,并把长任务交给命令行触发的计划任务,而不是通过页面点击启动。
还有一类资源经常被算漏:并发下载。考卷附件、课程讲义这类文件如果没有交给 Nginx 直接下发,而是全部走 PHP,那么每一个正在下载大文件的用户都会占住一个 worker,几十个并发下载就等价于几十个动态请求,直接挤压本来分配给考试动作的 PHP 容量。
考前十分钟是另一种尖峰:全员集中登录、集中打开课程、集中点开试卷。使用 LDAP 或 OAuth 等外部认证方式的站点,每一次登录都会打到外部目录服务,外部服务的超时会级联放大成 PHP 请求堆积。
冷启动同样危险。Redis 刚起来时全是 miss,coursemodinfo 这类缓存缺失会让每个学生都触发一次完整的课程结构重建;刚刚清过缓存或完成升级之后立刻开考,等于把全部压力推给数据库。提前用少量真实流量预热,或用命令行脚本提前重建重点课程的缓存,是比扩容便宜得多的手段。
把这件事固定成例行流程,效果比一次性调优稳定得多。建议的开考前三步:提前一到两个小时跑一次课程缓存重建,确认 Redis 里的主要缓存定义已经填充;确认当天没有安排版本升级、缓存清除或大批量报表导出;正式考试前用若干个测试账号走一遍完整流程,记录登录、开卷、保存、交卷四个动作的实际耗时。这三步加起来只需要几分钟,却能挡掉绝大多数由缓存冷启动引发的事故。
| 业务场景 | 同时在线人数 | 内存与 PHP-FPM 关注点 | 数据库与写放大关注点 | 存储与备份关注点 | 备注 |
|---|---|---|---|---|---|
| 单班课后练习、小班选修 | 50 人以内 | 8GB 内存起判,worker 按 60MB 估算后设 20 到 30 | 日志表按月清理,保留期按审计要求定 | SSD 数据盘,moodledata 与系统盘分开 | 无统一开考时间,负载较平缓 |
| 培训机构日常授课与作业 | 100 到 300 人 | 16GB 起,worker 30 到 50,必须外置 Redis | 作业提交集中,注意临时表与排序开销 | 定期清理 temp 与 trashdir,盯 inode 使用率 | 同时提交作业时会形成短促写峰 |
| 期末统考、资格认证集中开考 | 300 到 800 人 | 32GB 起,按峰值 QPS 乘响应时间反推 worker 数 | attempt 相关表写入洪峰,数据盘建议采用 NVMe,并持续监控 redo 节奏 | 自动备份单独挂载,避免写满数据盘 | 建议做一次 1 比 1 压测后再定机型 |
| 企业大学全国同步上线考试 | 1000 人以上 | Web 层横向扩展多台,单机不再靠加核数解决 | 数据库独立部署,缓冲池需要覆盖当日热数据 | 考虑文件与元数据外置,备份走对象存储 | 建议按区域拆分节点,靠近考生侧 |
| 直播课堂与 H5P 交互混合 | 200 到 600 人 | CPU 需求被插件抬高,先测单请求时长 | xAPI 与活动记录表写入增长接近考试场景 | 录制文件与导出内容另行规划容量 | 流媒体服务不建议与 Moodle 同机 |
这张表的读法是:人数只是入口,真正决定配置的是「同一个人在这段时间里产生了多少次动态请求」。同样的 300 人,分散浏览和集中在同一分钟开卷,对内存、磁盘 IOPS 与数据库连接数的要求是两回事。表里给出的是选型时要盯的字段,不是可以直接下单的固定配置。
再补一条选型时的常识:表中最后两行的场景,通常已经超过单机纵向扩容的合理区间。把 Web 层拆成多台、数据库单独一台、静态与备份外置,比继续给单台加两倍内存和两倍核数更可控,因为前一种方案的成本随人数近似线性增长,后一种会在某个点上突然失效。
判断标准不看人数,看三件事:能不能改 PHP-FPM 池参数、能不能装 Redis、定时任务的最小粒度是多少。前两项决定了前面提到的所有优化能否落地,第三项直接决定考试能不能正常收卷。
第三项常被忽略。Moodle 的定时任务依赖 cron 以较高频率执行,逾期处理、自动交卷、邮件队列、缓存失效都挂在上面。不少虚拟主机把 cron 的最小粒度限制在 15 到 30 分钟(取决于主机商策略),这种情况下考试超时的自动交卷会明显滞后,监考端看到的结果与学生实际作答不一致。
能被接受的边界大约是:只做课件浏览与资料下载、同时在线几十人以内、没有任何统一时间的考试安排(预估边界)。只要「统一时间开考」这个需求存在,就应该选择能自己控制进程数、内存配额与定时任务的独享环境。共享环境下同机邻居的磁盘争抢与 inode 限额同样是隐患。
还有一个经常被误解的点:把站点搬到性能更好的云主机上并不能自动解决所有问题。如果瓶颈是「响应时间被慢插件拉长」,那么搬到更强的机器只是把同样的串行等待挪到一个更贵的地方执行;正确的顺序永远是先量单请求时长,判断是否值得加机器,再决定加多少。这也是为什么我们建议在同一段时间里保留一份基线测量数据:同一份试卷、同样的人数、同样的动作序列,用来判断每一次改动到底是变好还是变坏。
先看是不是磁盘。iowait 长期偏高、redo 日志写入等待明显、备份期间全站变慢,都属于 IOPS 不足,更换更高性能的 SSD 比加任何 CPU 都有效。这类问题在考试场景下最容易被误判为 PHP 性能不足。
再看是不是数据库。慢查询日志、锁等待、hot 数据集超出缓冲池,都会表现为响应时间整体抬升。这时候加内存通常有用,因为内存最终会落到缓冲池上。
然后看 PHP-FPM 是不是打满。日志里出现「server reached pm.max_children setting」是明确信号,但它往往是结果而不是原因:如果根因是某个慢请求,单纯提高 max_children 只是让排队更长,还会同步抬高内存占用。真正该做的是先定位慢请求来自哪个插件或哪类页面。
最后才轮到 CPU。文档转换、视频转码、外部判题程序、统计重算这类确实消耗用户态算力的任务,才会从加核数里得到接近线性的收益。与此并列的一条经验是:当会话处理、表结构元数据缓存、MUC 应用缓存都还落在文件或数据库里时,先加 Redis 的性价比通常高于任何硬件扩容。
顺便明确一种「加了也没用」的情况:压测显示出口带宽使用率远低于采购上限,但响应时间依然很差,那么增加带宽不会带来任何改善。登录态试卷页是动态生成的文本,单个页面的传输体积通常只在几十千字节到几百千字节之间,真正耗时的是服务器端的渲染与数据库交互,而不是把字节发出去的那段时间。只有当确实存在大量视频播放、课件下载或实时课堂流量时,带宽才会成为关键变量。
一万网络深耕 19 年(成立于 2007 年),在这类「平时很闲、考试十分钟很忙」的系统上积累的经验是:容量必须从一个具体动作倒推,而不是从人数表找一个档位。给配置口径时我们会先问四个数——峰值动态请求 QPS、单请求平均响应时间、单次开考写入行数的估量、以及日志保留天数,再用这些数据去倒推 worker 数量、内存总量、数据盘 IOPS 与是否需要独立部署数据库。
落到机房与机型的取舍上:学生分布在中国大陆境内时,要考虑接入网络的访问质量与 CDN 配合;需要在中国香港、中国澳门或中国台湾地区节点部署时,重点核实是学生侧时延而不是改变资源消耗口径。起步阶段 Web 与数据库可以同机,但当峰值 QPS 常年超过单机承载或热数据集明显大于缓冲池时,应该拆成独立的两层,而不是继续给单台加配置。
价格方面,具体机型、内存规格与带宽形式的组合在内地与境外节点的公开渠道差异较大,以实时报价为准,涉及 GPU 或特殊带宽需求的部分均需询价。我们能承诺的是交付前提供一次按真实人数设计的压测方案,覆盖登录、开卷、自动保存、交卷四个动作;不做等保级别的承揽承诺,也不承诺任何形式的赔付条款。
交付之后的动作同样重要。我们建议把监控告警绑到与业务直接相关的指标上:一起来看看 worker 是否触顶、数据库缓冲池命中率是否下降、inode 使用率是否接近上限、自动保存周期是否形成明显锯齿。这四个信号比平均 CPU 更能提前预判故障,也更容易在开始扩容之前给出依据。
本文涉及的机制来自 Moodle 公开的架构说明与公开文档中的表结构与缓存定义,涉及的数值均为工程估算并已标注「预估」,没有在两地同时做对照压测,也不引用任何第三方测速或客户案例。实际数值应以自有站点的 1 比 1 压测为准。
核对时建议取以下字段:一是 PHP-FPM 状态页中的活动进程数、达到上限告警与慢日志;二是 database 端的 Innodb_buffer_pool_reads、Innodb_buffer_pool_read_requests、每秒行写入量与 redo 日志产生速率;三是 information_schema.tables 里各表 data_length 与 index_length 之和,重点看 mdl_logstore_standard_log 与 attempt 相关四张表的增速;四是 df -i 与 moodledata 下 temp、trashdir、cache、localcache、sessions 五个目录的文件数;五是 opcache_get_status 返回的内存使用率、命中率与键数量。
需要固化的结论有三条:写放大的主来源是可交互内容带来的行级写入,不是静态浏览量;内存需求必须由 worker 常驻量乘以 max_children 得出,不能按人数折算;外部缓存缺口带来的浪费,应当在自己机器上用上述字段重新跑一遍之后再用于采购决策。
口径统一提示:收集数据时务必在同一时间窗口内取所有字段,并写明当时的 Moodle 版本号、插件清单、PHP 版本与数据库版本,否则两次数据之间没有可比性。压测数据建议至少保留两轮,一轮作为改造前基线,一轮作为改造后对照,两者的差值才是可以写进采购申请的有效证据。