1. 故障初现SQL Server 服务启动后自动停止早上刚到工位手机里弹了十几条监控告警说某台业务数据库连不上了。远程过去一看Windows 服务列表里 SQL Server (MSSQLSERVER) 服务处于“正在启动”状态过两秒又自己停了再点“启动”按钮弹窗提示服务无法启动完全不带商量的。这台机器跑的是 Microsoft SQL Server 2019前几天刚被运维老哥调整过内存配置原因是同一台服务器上还部署了别的应用怕 SQL Server 把内存吃满于是把“最大服务器内存”从默认的 2TB 上限改成了一个很小的值。改完当时没重启等到晚上数据库例行维护窗口一重启服务直接起不来了。这个故障在 2019 上尤其典型。很多人听说过 SQL Server 重启后会按照 max server memory 配置来约束内存使用却忽略了内存管理器在启动阶段也要按这个配置做初始化。如果上限被压得太低进程会在初始化阶段判定“预算不足”直接拒绝启动。这时候服务管理器里看到的不是内存不足而是服务启动失败。这篇文章就围绕这个故障展开讲清楚根因、修复步骤、注册表兜底方案以及后续防坑监控。适合刚接手 SQL Server 运维的同学也适合那些正准备给数据库限内存、心里没底的人。不用全文细读先把现象对照上第二章和第三章是直接能救命的操作第四章是治本的配置思路。2. 症状与根因最大内存设多少才会“饿死”服务2.1 故障现象Windows 事件日志里有什么遇到这种服务起不来的情况第一步不是瞎折腾而是先看 Windows 事件查看器。在“应用程序”日志里能找到来源为 MSSQLSERVER 的错误事件错误内容通常包含类似“Could not create buffer pool”或“Buffer pool initialization failed”的描述也可能出现“内存管理器初始化失败”之类的信息。SQL Server 自己的错误日志也值得翻一下。默认位于 C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\Log\ERRORLOG用记事本打开最新一个文件就能看到启动过程中卡住的点。常见表现是启动信息打了十几行到 memory manager 初始化相关行之后就没有后续了这说明进程不是死在文件访问或权限上而是死在内存款子系统的初始化上。另外一个容易混淆的现象是有些时候错误日志会附带报告 master.mdf 或 tempdb 相关文件访问失败乍一看像文件权限问题其实是因为内存初始化失败导致整个启动流程被中断后续的数据库文件加载根本没执行到。这个坑我踩过一次手动去查了一大圈权限和磁盘浪费了不少时间。2.2 根因拆解SQL Server 内存管理器的“最低生存预算”SQL Server 的内存架构比很多人想象得复杂。进程启动后内存管理器要做的第一件事就是根据 max server memory 的配置值来规划缓冲池Buffer Pool、编译缓存、锁内存、查询执行内存等一系列内存分区。缓冲池不是拿多少用多少它一上来就要先建立内部的数据结构和哈希表。如果 max server memory 被设成比如 64MB内存管理器测算后发现连基础的缓冲池结构都搭不起来就会直接判定“这个环境没法干活”于是进程主动退出。用生活里的例子类比你叫了一个搬家师傅结果只给他租了一辆后备箱大小的货车让他去搬一整套房子。他看一眼车就知道这活干不了当场拒绝。SQL Server 也一样启动就是它“看一眼货车”的时刻预算不够直接罢工。所以核心逻辑是max server memory 不只是“内存占用上限”它同时是 SQL Server 启动时自我评估的“预算总额”。预算低于最低运行门槛服务就完全启动不了。很多人把“限制内存”理解成“随便设个小数”这是导致这类故障的普遍原因。2.3 动手前的诊断确认就是内存配置导致的在进入修复之前必须做两个确认避免误判。第一打开“服务”管理器看 SQL Server (MSSQLSERVER) 服务的“可执行文件路径”确认“启动参数”里没有别人手动乱加的内容。第二用注册表编辑器打开 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server 下的对应实例键定位到 MSSQLServer\Memory查看 MemoryFraction 的值。MemoryFraction 这个键对应的就是最大内存上限默认情况下它是一个很大的数值比如 2147483647单位是 MB。如果你看到的数值是个位数或百位数那基本就锁定了问题。配合事件日志里的内存初始化错误就可以确定走后面的修复流程了。3. 最小侵入式修复用 sqlservr.exe -f 把配置改回来3.1 为什么要用最小配置模式启动SQL Server 提供了一个启动参数 -f表示以“最小配置模式”运行。这个模式的特点是在启动时忽略无效或过低的配置值让数据库引擎用一套安全默认值跑起来方便管理员登录进去把问题配置改掉。这个参数对当前场景特别合适。因为现在服务起不来的原因就是最大内存配置太低而 -f 模式会跳过对内存预算的严格检查相当于先给搬家师傅换了一辆正常大小的货车让他进门再说。改完配置再正常重启问题就解了。网上有些人建议直接改注册表这个方案也可以但 -f 模式更稳妥因为走的完全是 SQL Server 自己支持的配置通道改完以后内存配置和注册表能保持一致不会出现两边不同步的隐患。3.2 完整操作流程从命令行启动最小配置模式整个修复过程需要至少两个命令行窗口。第一个窗口用来启动最小配置模式下的 SQL Server 进程第二个窗口用来连接它执行修改语句。先用管理员权限打开命令行窗口找到 sqlservr.exe 所在目录。默认实例通常在这个路径C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\Binn在命令行里切换到该目录并执行cd C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\Binn sqlservr.exe -f -s MSSQLSERVER注意 -s 参数后面跟的是实例名默认实例通常是 MSSQLSERVER命名实例换成实际名字。如果目录下有多个实例的 SQL Server 二进制确认自己要对哪个实例操作别选错。执行后窗口会停留在前台开始输出 SQL Server 启动日志。看到进程没有报错退出就说明最小配置模式生效了。此时千万不要关这个窗口关了就相当于把刚启动的数据库又干掉了。另开一个命令行窗口用 sqlcmd 连接本机默认实例sqlcmd -S . -E连接成功后在 sqlcmd 的提示符下执行三个语句EXEC sys.sp_configure Nshow advanced options, 1; RECONFIGURE; EXEC sys.sp_configure Nmax server memory (MB), 4096; RECONFIGURE;第一条语句是打开高级配置选项的开关不打开的话直接配置 max server memory 会报错。第二条和第三条真正把最大内存改成了 4096MB也就是 4GB。数值不一定要 4096可以根据机器物理内存灵活调整。后续章节会讲怎么选先给这台 16GB 内存的服务器定了 8GB 也可能更合适。重要的是把值从“两位数”改回一个能正常工作的量级。修改完成后执行 SHUTDOWN 命令关闭数据库进程SHUTDOWN;然后回到第一个命令行窗口等 SQL Server 正常退出确认窗口回到命令行提示符。这一步不能省直接在窗口里按 CtrlC 强杀进程可能导致配置没写盘。最后在服务管理器里正常启动 SQL Server 服务过几秒刷新状态应该已经是“正在运行”。再用 sqlcmd 查一下验证配置SELECT name, value_in_use FROM sys.configurations WHERE name Nmax server memory (MB);查到 value 和 value_in_use 都是 4096说明配置已经生效。3.3 这个方案的关键风险与注意事项整个流程里最容易被忽视的是权限问题。sqlservr.exe 在管理员命令行下运行时SQL Server 服务本身用的虚拟账户可能没有权限访问某些数据文件目录。这种场景下启动日志会报文件访问相关错误看上去像配置路径错了其实是上下文不一致。如果遇到这种情况不要死磕 -f 模式切到注册表方案改 MemoryFraction 后直接重启服务反而更快。另外一个风险是训练有素的命令窗口纪律问题千万不要在多个窗口里同时执行 RECONFIGURE连接开了多个会话时一定要注意确认自己在哪个会话里执行了修改别搞混了。4. 备用方案注册表直接改 MemoryFraction4.1 注册表路径与键值识别如果 -f 模式因为权限问题走不通或者用户不希望用命令行方式可以改注册表。SQL Server 的 max server memory 配置最终是持久化在注册表里的用注册表改和用 sp_configure 改本质上是同一件事只是绕过了启动检查。打开注册表编辑器定位到HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQLServer\Memory如果你的实例名不是 MSSQLSERVER路径里的随实例目录要替换。在 Memory 目录下找到名为 MemoryFraction 的值。在 2019 中这个值通常是 DWORD 类型单位是 MB默认值是 2147483647换算过来就是约 2TB 上限。4.2 修改方法与字节序注意右键 MemoryFraction选择修改基选“十进制”填入目标值。比如物理内存是 16GB想要给 SQL Server 分配 8GB就填 8192。填完确定关掉注册表编辑器然后在服务管理器里重启 SQL Server 服务。这里要注意一个坑某些安装版本中 MemoryFraction 可能是 REG_BINARY 类型显示的是一串十六进制字节。这种情况需要先在“十六进制”模式下读到原始值再换算成对应的字节序。大部分场景下直接当成十进制 DWORD 处理即可但遇到二进制显示值时不要慌把前四个字节按十六进制转十进制就是 MB 数。修改完以后务必确认注册表里其它相关项没有被顺手改掉。特别是 min server memory 对应的项如果也被设成了很低的值服务虽然能起来但运行起来性能会非常差。修复时最好把最大和最小两个值一起核算。4.3 两种方案的取舍建议我的个人做法是能进系统、能开命令行、权限没问题优先用 sqlservr.exe -f机器远程桌面图形界面方便、操作人不熟悉命令行直接改注册表更合适。前者事后配置一致性有保障后者操作门槛低但要注意类型和字节序。如果严格按步骤改注册表改完启动服务再用上述 SELECT 语句验证效果和 -f 方案完全一样。两条路都走过之后我是越来越倾向于推荐运维基础偏弱的人用注册表方案简单直接一条命令都不用敲只要路径别找错就行。5. 治本之道内存上下限怎么配才安全5.1 最大内存的推荐计算公式问题修好后接下来的问题是以后到底怎么设置才不会再犯。很多人的第一反应是“物理内存多少就给 SQL Server 留多少”这是错的。操作系统本身需要内存Windows 内核、驱动程序、杀毒软件、远程连接会话都要吃内存全给了 SQL Server 会让整个系统失去响应。一个常用的经验值是专用数据库服务器上max server memory 设置为系统物理内存的 75% 到 80%。这台机器只有 SQL Server 一个主要负载16GB 内存就设 12GB 到 13GB预留出操作系统的生存空间。如果还要跑其它应用先估算那些应用的真实内存占用再倒推给 SQL Server 的部分。具体操作建议先让 SQL Server 跑一段时间观察内存基线占用。比如一台 32GB 的机器SQL Server 稳定运行时进程占用约 20GB那 max server memory 可以设在 22GB 左右留 10GB 给操作系统和应用。切忌一上来就拍脑袋给个整数一定要基于观察数据。5.2 最小内存同样不能忽略排查这类故障时经常发现当事人不仅把最大内存调小了最小内存也跟着调成了更离谱的数值。min server memory 和 max server memory 是两回事最小内存决定 SQL Server 在空闲时保底占用的内存量它不会影响启动但会影响启动后的内存回收策略。建议把 min server memory 设置成 max server memory 的 50% 到 60%。比如 max 是 8192MBmin 就设 4096MB 或者 5120MB。设得太低SQL Server 在业务低谷时可能把内存大量还给系统高峰时又得重新申请内存增长的代价比很多人想的高。另外特别提醒只设置 min 而不动 max或者 min 和 max 同时设很小都无法解决本书描述的故障。导致启动失败的是 maxmin 只影响运行表现别把它们混为一谈。5.3 用动态管理视图持续观察内存压力修复和调整之后建议再用一两个动态管理视图持续观察内存情况。查询进程级内存状态用 sys.dm_os_process_memory看系统内存压力用 sys.dm_os_sys_memory看内部缓存分配分布可以用 sys.dm_os_memory_clerks。日常运维不需要天天看但业务反馈数据库变慢时先查一下 OS 可用内存和 SQL Server 已用内存的差距往往能快速定位是不是内存分配逼得太紧。另外一个值得盯的指标是 Buffer Manager 的 Page Life Expectancy它代表数据页在缓冲池里平均存活的秒数。这个值如果长期低于 300 秒说明内存压力大、频繁淘汰数据页就要考虑调高 max server memory 或者优化查询了。6. 那些让 SQL Server 起不来的其它“内存类”原因6.1 系统物理内存本身不足处理多了会发现有些服务起不来并不是 max server memory 配置问题而是 Windows 系统自己内存不够。最小配置模式下 SQL Server 也需要分配一部分工作内存如果整台机器只剩几百 MB 可用内存就算配置值合理启动依然会报内存不足。排查方法很简单打开任务管理器看“性能”选项卡确认物理内存未提交上限和已提交量。如果系统内存长期处于 90% 以上占用说明这台服务器的内存规模已经不够用了这时候该扩容的扩容该迁移的迁移光调 SQL Server 配置解决不了根本问题。6.2 32位与64位版本的差异SQL Server 2019 只有 64 位版本在大多数场景下都能充分利用物理内存。但如果用户是从旧版本升级脚本或脚本文件复制过来的配置某些老式的最大内存设置可能太低旧版本的默认值和 2019 的默认机制存在差异这种情况也会导致新版本启动异常。处理这种问题通常直接参照注册表方案把 MemoryFraction 改成符合 2019 建议的数值即可。千万别沿用旧服务器上“为了兼容”而设的极端小值2019 的内存管理模型比老版本更激进对下限的要求也更高。7. 实操心得我处理这种问题时的检查顺序这类故障处理过几次之后我养成了一套固定的检查顺序每次都能很快定位。先开服务管理器看服务状态和启动参数确认没有二手配置污染然后翻 Windows 事件日志看有没有内存初始化相关错误紧接着看 SQL Server 错误日志的 ERRORLOG 文件确定卡在哪个阶段。这三步走完基本能区分是配置文件问题、权限问题还是系统内存不足。接下来的修复动作简单直接先用注册表方案确认 MemoryFraction 当前值如果值过小直接用 -f 模式或者注册表改回去。改完以后一定记得重启服务和验证配置哪怕业务再急也要把验证做完再交出去。最后再分享一个小技巧修复完成后把原来的错误 max server memory 值和修复后的值都记录下来连同这台机器的物理内存、其它负载情况一并写进备注。下次再有人想调内存直接把这个记录甩过去能少很多沟通成本和踩坑概率。这个故障本身不算难踩过一次之后再看到“服务起来又停”的告警第一反应就该想起这个老朋友。