结案报告:MC 服务器玩家数据保存卡顿(PRY-65535)
- 排查周期:2026-09-26 20:00 ~ 2026-09-29 13:30
- 结论:D: 盘 TOPMORE Aries(序列号 DTMCSC11220174,固件 3.F.F.R2)固件缺陷——并发小写入突发下周期性冻结命令队列 200~1400ms。已通过更换物理硬盘解决。
- 主服业务数据已全部迁离故障盘;故障盘建议 RMA/退役。
一、问题现象
| # | 现象 | 数据 |
|---|---|---|
| 1 | 每 5 分钟 autosave 时真人数据保存极慢,小号正常 | 真人中位 397~400ms / 小号 9ms(5 分钟一批,最多一批 14.1s) |
| 2 | 耗时呈精确 200ms 整数倍量化 | 36~42% 真人样本落在 200ms±20ms(随机应 2.2%);实测 399-403 / 599-603 / 799-809 / 1196-1202 |
| 3 | 与数据大小无关(反例) | 1KB 文件出现 988ms;同批次 9ms 与 997ms 混杂 |
| 4 | 备份窗口放大 | 4 小时一次 48G gzip 备份时连续 Can't keep up 2~3.3s;备份结束后仍卡 |
| 5 | spark 两次拍到现场 | 耗时全部在 write0 / SetEndOfFile(文件系统 syscall),gzip deflate 仅 12~60ms |
| 6 | 真人 vs 小号的差异 | 真人每次保存写 3~4 个文件,小号 1 个 → 撞停顿窗口概率不同 |
二、排查时间线
阶段一:日志分析(9/26)
player_save_time.log(playerchecker-1.0.0 mod,反编译确认:mixinPlayerList.save方法头/RETURN 计时)- 110 个玩家统计:真人 400~700ms、小号 6~8ms;区块归因分析排除"位置/区块损坏";全物品区 1459 漏斗 90% 空闲("重但健康")
- 玩家 .dat 仅 0.9~21.8KB,与耗时相关系数 0.02 → "数据大"证伪
阶段二:ZGC 对照(9/27~29)
- 9/27 21:39 切换 ZGC(GraalVM-PRY,-Xmx32G)
- 全服集体卡顿消失、区块位置效应消失,但真人保存耗时不变、量化特征不变 → GC 与位置排除,锁定文件系统层
阶段三:spark 取证(9/29 00:17)
- pwfN8(ZGC 时代):杀假人 → 15KB 数据写入卡在
SetEndOfFile388ms - KfN9(G1 时代旧剖析):autoSave → saveAll 4364ms → write0 4060ms,deflate 仅 12ms
- 两个 GC 时代同签名 → 指向 Windows 文件系统层
阶段四:火绒排除(9/29 02:00~08:20)
- 发现
sysdiag.sys过滤驱动(8 实例挂全部卷,altitude 324600=AV 类)——此前"无杀软"结论作废 - A/B:关实时监控无效 → "退出"实际未退出(自保护拦截)→ 卸载后内核驱动残留仍卡 → 08:20 重启驱动物理消失后停顿依旧(8~10%)→ 驱动彻底排除
阶段五:隔离复现(9/29 05:00~06:20,/spec 工程,独立审查 PASS)
- 克隆服务端到
D:\Server\test(5 mod 精简版、端口 65530、RCON 自动化、备份禁用) - 8 名 carpet 假人:6 名真实高延迟玩家数据 + 2 名空数据对照(BFConvert 登录坐标与主服日志完全一致)
阶段六:硬件定位(9/29 上午)
- 三介质对照 + CDI/CDM 解读 + 固件升级 + 换盘(详见下表)
三、核心实验矩阵(全部数据)
3.1 主矩阵:盘 × 优化 对照(隔离测试台,同一服务端/同数据/8 假人/5~10 轮 save-all)
| # | 盘 | 配置 | 优化 | 保存条目 | >200ms 卡顿率 | 真人假人 avg | 探针(15KB 独立写) | 判定 |
|---|---|---|---|---|---|---|---|---|
| 1 | D: Aries | ZGC, sync=true | 无 | 88 | 65/66(98%) | 350~704ms,max 1202 | — | 100% 复现主服 |
| 2 | D: | G1, sync=false | 无 | 40 | 24/40(60%) | 251~652ms | 12/416 次(84~438ms) | GC/刷盘排除 |
| 3 | E: Gemini(players junction) | G1, sync=false | 无 | 40 | 0/40(0%) | 11.4~17.2ms | E: 0/472(max 1.1ms);D: 仍 3/458 | 迁盘=治愈 |
| 4 | D:(players 迁回) | G1, sync=false | 无 | 40 | 27/40(68%) | 264~654ms,量化复现 | — | A/B/A 因果闭环 |
| 5 | D: | G1, sync=false | playerasyncsave | 32 | 5/32(16%) | 多数 4~13ms,残余 255~393 | 8 次 243~351ms | mod 掩盖症状不治病 |
| 6 | E:(全套迁入) | G1, sync=false | 无 | 32 | 0/32 | 9.5~14ms | 0/1061(max 1.8ms) | E: 干净 |
| 7 | E: | G1, sync=false | playerasyncsave | 32 | 0/32 | 7.5~10ms | 0/1061 | 干净 |
| 8 | D:(重启后,驱动已消失) | G1, sync=false | 无 | 40+40 | 4/40 + 3/40(8~10%) | — | 5/1043 + 4/1048(214~350ms) | 驱动物理排除后仍卡 |
| 9 | F: HDD 7200转 | G1, sync=false | 无 | 40 | 2/40(5%,229/512 非量化) | 71~207ms(机械盘正常基线) | 0/1556(max 160ms) | HDD 慢但健康、无量化病灶 |
| 10 | D:(ColDataRefresh 冷数据刷新后) | G1, sync=false | 无 | 32 | 2/32(6%) | — | 独立 1/2373×2(↓10 倍);突发窗口 5/1042(不变) | 刷冷数据治背景不治突发 |
| 11 | D:(刷固件后立即,读数被污染) | G1, sync=false | 新固件 | 39 | 29/39(74%)* | — | 9/1008(254~442ms) | 刷后盘内部重建+MFT 损坏干扰 |
| 12 | D:(新盘) | G1, sync=false | 物理换盘 | 37+32+40=109 | 0/109(0%) | 7.0~12.8ms,max 23 | 0/3218(max 0.9ms) | 换盘=结案 |
* 11 号读数受刷固件伴生事故污染(意外移除 + MFT 损坏 + 刚拷 6GB),不代表新固件真实成绩。
3.2 微观基准(绕开 Minecraft 的独立验证)
| 测试 | D: | C: | E: | F: | 结论 |
|---|---|---|---|---|---|
| Python 15KB 截断写(空闲期) | 0.3ms | 0.3ms | 0.2ms | — | 盘/驱动/路径本身正常 |
| save-all 窗口内同盘并发写 | 11/420 次 243~389ms | — | — | — | 停顿波及同盘所有写者 |
| Java MiniSave(复刻 NbtIo 链路,双 JDK) | 9.4ms | — | — | — | MC 进程外不可复现 → 病灶依赖真实写入形态 |
| 8.6GB 顺序写(重分区+新固件后) | 6087 MB/s,0 冻结 | — | — | — | 顺序路径健康;病灶专挑小写入突发 |
3.3 硬件证据(CrystalDiskInfo / CrystalDiskMark / 事件日志)
| 指标 | Aries (D: 故障盘) | Gemini (E:) | SN770 (C:) | 解读 |
|---|---|---|---|---|
| SMART 健康 | 85%,0 介质错误,备用空间 100% | 100% | 84% | 数据安全,无磨损性劣化 |
| 错误日志项数 | 88 | 0 | 12 | 命令超时/中止的"案底",与停顿吻合 |
| 温度 | 51°C(传感器1 63°C) | 34°C | 43°C | 全机最热 |
| 通电/写入量 | 30355h / 190TB(3.5 年连续) | 5337h / 11.7TB | 35608h / 127TB | 老盘;磨损 15% 非寿命问题 |
| 写缓存 | VolatileWriteCache(有 DRAM) | 同 | 同 | "无DRAM"假说作废 |
| CDM 跑分 | 顺序 7132/6779 MB/s,4K Q1 写 11.3µs——全项满分 | — | — | 跑分测不到病灶(连续热数据负载) |
| 事件日志 | 两次"磁盘 3 意外移除"(ID 157)+ MFT 损坏 + 卷脏位 | 无 | 无 | 总线级不稳(第二次实为用户换盘动作) |
四、根因机制
主服 autosave(每 5 分钟)
└─ 突发:每玩家 3~4 个小文件写(.dat+advancements+stats+.dat_old)+ 并发区块写
└─ 撞上 Aries 固件内部维护窗口(FTL 回收/映射表刷写期间命令队列冻结 200~1400ms)
└─ 窗口内每次 write0/SetEndOfFile 等 200~400ms
└─ 主线程同步保存 = 200ms × 撞中次数(量化特征)
└─ 真人文件多→撞中概率高;小号 1 个文件→大多幸免("数据大小"假象)- 佐证:SMART 88 条命令超时;温度 51~63°C 放大维护频率(解释停顿率随负载波动 8%~68%);3.5 年老盘 vs 同品牌新盘(Gemini)零停顿;CrystalDiskMark 满分(合成负载测不到突发病灶);冷数据刷新只治背景停顿(↓10 倍)不治突发(固件内部状态不可达)。
- 排除清单(均有对照实验):玩家数据大小/损坏、gzip 压缩、GC 品牌、sync-chunk-writes、火绒驱动、区块位置、盘性能/磨损/容量、介质类型。
五、修复措施效果表
| 措施 | 实测效果 | 定位 |
|---|---|---|
| 更换硬盘(最终方案) | 98%/74% → 0/109 全绿(真人 7~13ms) | 根治 |
| playerdata junction 迁移(D:→E: 实测) | 65/66 → 0/40(30~40×) | 根治(针对玩家数据) |
| playerasyncsave mod | 68% → 16%,TPS 不再被拖 | 治标(停顿转入工作线程) |
| ColDataRefresh 冷数据刷新 | 背景停顿 ↓10 倍;突发停顿不变 | 辅助 |
| 关实时监控 / 退火绒 / 卸载火绒 | 无效(内核驱动残留) | — |
| ZGC / sync-chunk-writes=false / 降备份压缩 | 对此病灶无效(对其他负载仍有意义) | — |
六、遗留事项
- 旧 Aries 盘 RMA/退役(证据包:SMART 健康但 88 条命令超时、DiskMark 满分、三介质对照、量化停顿、传输速度归零实录、刷固件无效)。
- 新盘(同为 Aries 型号)建议头几天留意测试台成绩与事件日志 ID 157;用 CDI 记录其固件版本号对比 3.F.F.R2。
- 测试台留存:
D:\testmc、E:\testmc、F:\testmc(路径自适应,双击三个 bat 即可完整复验);工具链 rcon.py / lagtest.py / disksim.py / MiniSave.java / spark_parse.py。 - 经验教训:火绒"退出"≠卸载(内核驱动驻留至重启);SMART 健康 ≠ 延迟健康(88 条命令超时是关键线索);CrystalDiskMark 测不出突发型固件病;排查存储问题优先做"换盘 A/B"而非换软件配置。