做运维这么多年,最怕的就是半夜被电话叫醒。尤其是那种“虚拟机还活着、系统能进、但一堆服务起不来”的告警——你说它宕机了吧,它能登录;你说它没事吧,SQL挂了、服务全崩、界面上还弹出一堆看不懂的报错。今天想和大家聊的,就是我在一套超融合虚拟化环境里排查Windows Server 2016数据库服务器故障的完整过程。故障现象从表面看五花八门:系统启动奇慢无比、SQL Server服务起不来、所有自动启动服务都提示磁盘空间不足、甚至关闭服务器管理器都弹权限和空间相关的报错。很多人遇到这种情况容易懵,觉得是系统坏了、权限乱了、或者中了毒。实际上,这一连串症状的根因只有一个:磁盘空间被彻底耗尽。这篇文章我就从头到尾把这套排查思路、处理手法和后续加固方案拆开讲清楚,希望遇到类似故障的朋友可以少走弯路。
先说清楚这个内容适合谁看——如果你的环境属于以下任意一种,这篇都值得仔细读:虚拟化或超融合平台上的Windows业务虚拟机,尤其是运行SQL Server的;使用Windows Server 2012 R2或2016版本;经历过或正在经历“莫名其妙的启动慢、服务起不来、报磁盘空间”三重折磨的运维同学。我下面讲的每一招都是实际踩坑后总结出来的,顺序和细节都按真实现场还原。
1. 故障现象拆解:为什么所有问题同时爆发
1.1 五个症状其实是一条链路
先把我当时看到的表象完整列一下,方便大家对照自己的环境:
- Windows Server 2016启动极慢,从开机到进入桌面花了接近30分钟;
- SQL Server数据库服务(MSSQLSERVER)无法启动,事件查看器里报错;
- 除了SQL,其他自动启动服务比如打印服务、更新服务、一些第三方Agent全部失败,提示“磁盘空间不足”;
- 打开服务器管理器,关闭窗口时弹红色错误:提示“您不具备用户权限和可用空间来保存用户关系”;
- 远程桌面可以连,但桌面点什么都像“假死”,资源管理器明显卡顿。
这些症状单独拎出来,每一个都能把人带偏。比如那个服务器管理器的提示,乍一看很像域策略、本机权限出了问题,实际上根本不是。服务器管理器在关闭时会尝试把当前用户的角色和功能视图缓存到本地配置目录,如果系统盘连几MB都写不进去,自然就报权限和空间的双重错误。而SQL Server无法启动,很多时候我们也习惯先去排查端口、服务账号、数据库文件损坏,很少第一时间想到“C盘满了”会导致SQLServer连临时文件都建不了,直接起不来。
1.2 磁盘空间耗尽的蝴蝶效应
为什么一个简单的“磁盘满了”会引发这么多连锁反应?关键在于Windows系统和SQL Server对临时文件、日志文件的写入要求极为敏感。SQL Server启动时,需要初始化tempdb、写错误日志、检查master库状态,这些操作都需要在系统盘或数据盘上有可用空间。哪怕只是需要几十MB的临时空间,只要写不进去,整个服务启动就会被判定为失败。更隐蔽的是,Windows服务控制管理器(SCM)在启动每个服务时,服务进程本身要创建日志、写注册表临时键值、初始化工作目录,底层磁盘空间不足时,这些操作全部失败,于是所有服务开始报“空间不足”,看起来像是一场集体故障,实际上只有一个根因。
我在处理时见过很多同行对着服务逐个排查,折腾了半天也没结果。这里想给大家提前建立一个认知:虚拟化环境里的Windows,如果同时出现服务批量失败、GUI操作异常、启动过程卡死,第一件事永远是查磁盘空间,尤其是C盘。
2. 根因定位:从系统内部到虚拟化底层
2.1 登录后的第一个判断动作
接到告警后,我用远程桌面连上虚拟机。当时鼠标虽然能动,但响应极慢,打开“此电脑”都要等很久。趁这个时间,我直接在命令行里敲了几个命令,这是整个排障过程中最关键的几个动作:
fsutil volume diskfree C: dir C:\Windows\Temp | measure -sum Length wmic logicaldisk get caption, freespace, sizefsutil volume diskfree C:会直接输出可用字节数,比看GUI准确得多——GUI有时候卡顿,显示的剩余空间是旧的缓存数据。当时结果显示C盘剩余空间是0字节,是的,一个字节都不剩。到了这一步,所有表象都有了合理解释。但光知道“满了”还不够,接下来要回答两个问题:什么东西把空间吃光了?还能不能快速腾出空间让服务启动?
2.2 揪出空间杀手的办法
很多人这时候喜欢用“磁盘清理”或第三方工具,但当时虚拟机已经卡到GUI几乎不可用,而且磁盘清理本身也要写临时文件,所以更靠谱的方式还是命令行。我建议大家在日常就提前做好一个PowerShell脚本,专门用来扫描C盘大文件,关键时候能救命:
Get-ChildItem C:\ -Recurse -Force -ErrorAction SilentlyContinue | Sort-Object Length -Descending | Select-Object -First 30 FullName, @{Name='SizeMB';Expression={[math]::Round($_.Length/1MB,2)}} | Format-Table -AutoSize注意几点:-Force参数必须加,否则看不到隐藏文件;-ErrorAction SilentlyContinue是为了跳过权限不足的目录,比如System Volume Information;如果机器实在太卡,建议分目录扫,先扫C:\Windows\Temp、C:\Users、C:\Windows\SoftwareDistribution,再扫全盘,避免长时间卡死。我当时扫出来的结果很有代表性——罪魁祸首是SQL Server的备份文件,之前某同事把数据库备份任务直接做到了C盘,日积月累攒了一堆几十GB的.bak文件。此外还有Windows更新缓存、系统临时文件和几个崩溃转储文件,全都在C盘挤成一团。
2.3 超融合虚拟化底层的隐藏因素
这里必须额外提醒一句:如果你确定虚拟机内部磁盘空间还很充足,但仍然出现类似的写入失败症状,那问题可能出在虚拟化底层。超融合平台通常使用精简置备(Thin Provisioning)模式,虚拟机看到的“空闲空间”和底层存储池的实际剩余空间不是一回事。精简置备的特点是:虚拟磁盘文件最初只占很小的物理空间,随着虚拟机内数据的增加,逐步向存储池申请真实空间。一旦存储池整体水位线用尽,虚拟机内部的写入操作就会失败,即使你在Windows里看到的剩余空间还有几百GB也没用。
当时我特意登录超融合管理平台看了一眼存储池状态,果然——存储池容量已经用到了98%,多个副本机制的占用量比预想的大得多。这个发现比虚拟机内部的问题更让人后怕:如果只埋头清C盘,治标不治本,很快又会复发。
3. 救火全实录:从腾空间到恢复服务
3.1 紧急释放空间:先让系统喘口气
空间已经见了底,首要目标是“尽快腾出能让Windows和SQL正常启动的空间”。我的操作顺序是这样安排的,每一步都有讲究:
第一步,清理临时文件目录。删除C:\Windows\Temp和C:\Users\*\AppData\Local\Temp下的文件,这些是纯垃圾,可以直接删:
Remove-Item C:\Windows\Temp\* -Recurse -Force -ErrorAction SilentlyContinue Remove-Item C:\Users\*\AppData\Local\Temp\* -Recurse -Force -ErrorAction SilentlyContinue第二步,清理Windows更新缓存。C:\Windows\SoftwareDistribution\Download里存的都是已经下载但可能已经安装或即将安装的更新安装包,可以安全删除:
Stop-Service wuauserv -Force Remove-Item C:\Windows\SoftwareDistribution\Download\* -Recurse -Force -ErrorAction SilentlyContinue Start-Service wuauserv第三步,删除SQL Server的数据库备份文件。这一步要想清楚,备份文件是最宝贵的,删之前确认这些备份已经没有保留价值,或者已经安全拷贝到其他存储。我在现场确认过那些备份确实是过期日志型备份后才删掉,瞬间释放出几十GB。
第四步,查看是否有磁盘转储文件C:\Windows\MEMORY.DMP,这是系统崩溃时的内存快照,对日常运维来说价值有限,通常可以删掉。还有一个容易忽略的目录是C:\ProgramData\Microsoft\Windows\WER\ReportArchive,里面是Windows错误报告的存档,也会积累不少无用文件。
这里要特别提醒:千万不要手动去WinSxS目录里删文件。很多教程说WinSxS是Windows组件存储,体积巨大,可以清理。但要清理它只能用官方工具,比如Dism.exe /Online /Cleanup-Image /StartComponentCleanup,手动删除文件会导致系统组件损坏。我见过有人手贱删了WinSxS,最后只能重装系统。
3.2 处理服务器管理器报错的正确姿势
当时C盘释放了一部分空间后,服务器管理器关闭时的报错提示就消失了。这个现象本身就是最好的验证——说明那个“不具备用户权限和可用空间来保存用户关系”的提示,确实只是空间问题导致的连带故障。但如果你清理完空间后,这个提示依旧存在,那大概率是用户配置文件损坏或ACL权限异常。处理方法很简单:找到本地用户配置下的服务器管理器缓存目录,一般是%LocalAppData%\Microsoft\ServerManager,把里面的内容重置掉:
# 先用管理员身份执行 Remove-Item "$env:LOCALAPPDATA\Microsoft\ServerManager" -Recurse -Force如果你是用普通管理员账号登录的,但目录的所有者不是你,可能需要先获取所有权。不过在实际操作中,95%以上的情况都只是空间问题,删除后系统会重新生成一份干净的配置,不用过度担心。
3.3 让SQL Server服务恢复启动
空间腾出来后,我没有立刻启动SQL服务,而是先看了一眼SQL Server错误日志。日志位置通常在:
C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Log\ERRORLOG如果你之前服务一直没能拉起来,这个日志文件里会记录启动失败的具体原因。我当时看到的错误记录显示tempdb无法初始化、错误日志写入失败,这就和磁盘空间满对上了。确认空间足够后,直接在服务管理器或PowerShell里启动服务:
Set-Service MSSQLSERVER -StartupType Automatic Start-Service MSSQLSERVER启动后建议立即验证几个关键数据库的状态:master是否在线、tempdb是否正常重建、用户数据库是否显示“正在恢复”。尤其注意tempdb,它每次重启都会重建,如果它的文件位置在C盘,未来C盘空间告急时它还是会成为第一个牺牲品。这个问题我在后文会专门讨论迁移方案。
3.4 虚拟化底层的存储扩容操作
虚拟机内部腾出空间只是解决了眼前的问题,底层的超融合存储池接近满水位才是真正的隐患。当时我在管理平台上做了几件事:清理过期的虚拟机快照——快照文件在超融合存储里占用的真实空间比你想像的更大,尤其是一个虚拟机挂了好几个快照且跨度时间很长的情况,底层映射表复杂,空间浪费和性能损耗都严重;检查是否有其他虚拟机的“僵尸磁盘”或备份任务残留文件;最后,如果条件允许,给存储池增加物理容量。
如果你的虚拟化平台支持在线扩容虚拟磁盘,可以在虚拟机开机状态下直接增大磁盘容量,然后在Windows里用“磁盘管理”扩展分区。这类操作每个平台界面虽然不同,但逻辑是一样的。我的建议是先删快照再扩容,避免底层映射错乱。扩展分区时也要注意:如果C盘和引导分区挨在一起,用系统自带的“扩展卷”功能通常能直接搞定;如果不挨着,就得借助第三方分区工具,风险会高一些,操作前必须确认备份。
4. 从临时救火到长期加固:我做了什么
4.1 把数据库文件搬出系统盘
这次故障的根源之一是SQL Server的备份文件写到了C盘,但更深层的问题是,业务系统的数据文件和日志文件默认都放在系统盘上。只要C盘空间紧张,数据库就是第一个受害者。我后来把SQL Server的几个数据库文件、日志文件、以及tempdb全部迁移到了独立的数据盘上,并且为独立数据盘配置了合理的剩余空间监控。
迁移tempdb的做法,如果大家有兴趣可以留意一下:在SQL Server配置管理器里,进入SQL Server服务的属性,找到“启动参数”,为tempdb数据文件和日志文件指定新的路径。也可以直接用T-SQL修改:
USE master; GO ALTER DATABASE tempdb MODIFY FILE (NAME = tempdev, FILENAME = 'E:\MSSQL\TempDB\tempdb.mdf'); ALTER DATABASE tempdb MODIFY FILE (NAME = templog, FILENAME = 'E:\MSSQL\TempDB\templog.ldf'); GO改完之后重启SQL服务生效。注意迁移前确认目标盘空间充足,而且不要把tempdb和操作系统页面文件放在同一块盘上,否则会产生严重的IO竞争。
4.2 建立监控和告警机制
说实话,这次故障完全可以靠监控提前发现。C盘空间从满到爆不是一个瞬间过程,备份文件是一天天累积的。如果我在之前就配置好了磁盘空间告警,早在一周前就能收到通知,根本不会拖到服务全部崩掉。我现在的做法是:对存储池和虚拟机内部磁盘建立水位监控,存储池剩余空间低于20%就黄色预警,低于10%红色告警;虚拟机内每个磁盘,尤其是C盘,剩余空间低于10GB或剩余比例低于15%就要触发告警。告警渠道可以是邮件或消息推送,关键是必须有人看、有人响应。
4.3 定期清理与归档策略
运维工作里,“会清理”比“会安装”更能体现功力。我后来做了三件很实际的事情:一是给每台服务器配置了定期临时文件清理计划任务,每天凌晨执行PowerShell脚本清理Temp目录和Windows更新缓存;二是数据库备份全部改为备份到独立备份服务器或对象存储,不再落本地C盘;三是把Windows系统事件日志大小做了上限限制,避免日志无限增长塞满系统盘。这些都是小事,但每一件都能在未来避免一次大故障。
5. 常见问题速查表:你可以直接照做的清单
为了让大家看得更清楚,我把这次故障中涉及到的关键检查点、典型误区、推荐操作整理成几个速查表,日常运维可以直接对照参考。
5.1 磁盘空间故障诊疗表
| 症状 | 可能根因 | 推荐操作 |
|---|---|---|
| 系统启动极慢 | 系统盘空间耗尽或IO阻塞 | 查磁盘剩余空间;查存储池水位;查延迟 |
| 所有服务报“磁盘空间不足” | 系统盘或虚拟磁盘底层无空间 | 清理临时/备份/缓存文件;扩容虚拟磁盘 |
| SQL Server无法启动 | tempdb初始化失败、错误日志写入失败 | 看ERRORLOG;腾空间;启动服务 |
| 关闭服务器管理器报“用户关系和空间” | 缓存目录写入失败、磁盘满 | 清理用户配置目录;释放磁盘空间 |
| 明明有剩余空间但写入失败 | 精简置备存储池满、快照过多 | 检查存储池容量;合并/删除快照 |
5.2 值得吃的教训清单
- 永远不要把SQL备份文件写到系统盘,这是这次故障最大的引爆点;
- 精简置备的存储池一定要持续关注真实水位线,虚拟机和存储两层都要看;
- 删除文件和释放空间不能靠桌面右键,刷新不及时会误导判断,要用命令行看真实数据;
- 任何清理操作前,先确认目标文件确实不再有业务价值,尤其是数据库备份和日志文件;
- 遇到“服务器管理器保存用户关系”的报错,先别急着修权限,查磁盘空间往往更快。
6. 最后再分享一个实用小技巧
如果你以后也遇到系统已经卡到桌面操作困难、但又必须立刻看到磁盘剩余空间的情况,记住一个小组合:远程桌面连上后不要点任何GUI,直接Ctrl+Shift+Esc打开任务管理器,在“新建任务”里输入powershell并勾选“以管理员身份运行”。然后在PowerShell窗口里执行磁盘检查命令,这种方式比在资源管理器里“此电脑”要靠谱得多,因为你不会因为GUI卡死而误判系统状态。
再补充一个个人习惯:每次处理完这类故障后,我都会把当时用到的核心命令整理成一个脚本文件放到C盘之外的目录里,命名叫“紧急自救工具集”,下次遇到类似问题直接调出来跑一遍。磁盘空间故障不会只发生一次,有备而来的运维才不会被半夜告警打乱节奏。我自己在这次故障里最大的体会就是——大部分“诡异”的系统问题,底层原因往往朴实无华,保持冷静,从最基础的资源检查开始,永远是最快的解法。