上一篇 下一篇 分享链接 返回 返回顶部

如何在香港服务器的Windows系统中调整Page File策略,解决数据库高负载下的内存溢出问题?

发布人:Minchunlin 发布时间:2025-08-26 09:14 阅读量:744


凌晨两点,香港葵涌的数据中心一层,压缩机的嗡鸣声跟着警报灯的闪烁一起压在我耳朵里。监控墙上 SQL Server 的告警像瀑布一样刷下来:There is insufficient system memory to run this query (Error 701)。

我捧着纸杯咖啡,盯着那台最“金贵”的数据库主机——一台跑 Windows Server 的独立物理机。两小时前业务做了一次促销推送,读写流量飙升,我原以为是索引没走对或某个慢查询暴涨,结果进机房一看,不是 CPU 打满,也不是磁盘飚红,是“提交内存 (Committed Bytes)”硬杠“提交上限 (Commit Limit)”。

这事儿我吃过亏:很多人把“内存不足”简单理解为“物理内存满了”,可在 Windows 的世界里,只要已提交内存逼近提交上限,就会 OOM(哪怕物理内存还剩)。而这个“提交上限”,就跟 Page File(分页文件) 的策略绑在一起。

那一刻我就知道,今晚要从 Page File 下手救火。

环境与问题画像(现场参数)

详情
机房 香港(HK)
服务器 AMD EPYC 7443P(24 核),128 GB RAM
存储 C: 240 GB SATA SSD(系统盘);E: 1.92 TB NVMe(数据);F: 960 GB NVMe(日志);T: 480 GB NVMe(tempdb)
操作系统 Windows Server 2019 Datacenter
数据库 Microsoft SQL Server 2019 Enterprise(启用 LPIM:Lock Pages In Memory)
工作负载 高并发混合 OLTP/OLAP;突发大查询 + 批处理
现象 促销高峰期出现 Error 701;Windows 事件 2004(资源耗尽);Committed Bytes ≈ 97% Commit Limit
Page File 现状 被之前的同事“优化”成 禁用(0 MB),怕影响磁盘性能
SQL 配置 max server memory = 110 GB,为 OS 预留 ~18 GB

关键监控(促销高峰期)

指标 值(故障前) 备注
Memory\Committed Bytes 124 GB 已提交内存
Memory\Commit Limit 128 GB 无 Page File 时约等于物理内存
% Committed Bytes In Use 96.9% 接近爆线
Memory\Available MBytes 1.1 GB 并非 0,但没意义
Paging File% Usage 0% 因为没 PF
Process(sqlservr)\Working Set 108 GB 与 max server memory 接近
事件日志 Event ID 2004 资源耗尽

判断: 已提交内存逼近提交上限(Commit Limit=物理内存 + Page File),根因是禁用 Page File 导致提交上限过低。LPIM 让 SQL Server 不轻易被换出,但并不减少系统的“提交”需求;当其他进程、驱动或内核分配提交内存,仍然会撞上上限。

策略总览:Page File 的三条铁律(我在现场反复验证过)

1. 不要禁用 Page File。

  • 禁用只在“理论纯内存工作负载”里看起来优雅,现实里反而收窄 Commit Limit,导致更早 OOM。

2. 把主要的 Page File 放在最快、最空闲的盘(优先 NVMe),但 C: 保留一个小的(用于转储/回退)。

  • C: 保留 1–2 GB 固定大小。
  • 主 Page File 放到 E:(数据 NVMe)或专用 NVMe,固定 16–64 GB(按内存与峰值提交量评估)。
  • 不要放在日志盘 F:(避免与顺序写竞争);tempdb 盘 T: 也尽量别(防止紧急情况下互相拖累)。

3. 固定大小(Initial=Maximum),减少碎片与抖动。

  • 系统托管(自动)在部分场景下会临时扩缩,高峰扩容瞬间最危险;固定区间更稳。

计算与选型:这台 128 GB 机器我给的 Page File 方案

  • 我怎么估 Page File(给你一套可复用的思路)
  • 先看峰值提交占比:高峰 Committed Bytes ≈ 124 GB。
  • 我希望安全余量 ≥ 20%:124 GB / X ≤ 80% → X ≥ 155 GB。
  • 物理内存 128 GB → 需要的 Page File 最少 ≈ 27 GB 才能把 Commit Limit 拉到 ~155 GB。
  • 结合 LPIM 与 NVMe 写延迟,我选 E: 上固定 32 GB。
  • C: 留 2 GB 小 Page File,保证内核转储与特殊回退。

经验折中表(SQL/大内存应用,可参考):

  • RAM ≤ 32 GB:PF ≈ 1.0×RAM(最少 8 GB,最多 48 GB)
  • 64–256 GB:PF ≈ 16–32 GB +(超过 64 GB 部分的 10%)
  • ≥ 256 GB:PF ≈ 32–64 GB

目标是让 Commit Limit ≥ 峰值提交 × 1.2,同时不把 PF 做到离谱(避免无意义的磁盘占用与崩溃时长 dump)。

变更步骤(完整可复用 Runbook)

变更前快照(我在现场先跑了这个)

# 记录关键内存/提交/分页文件状态(保存证据)
Get-Counter '\Memory\Committed Bytes','\Memory\Commit Limit','\Memory\Available MBytes','\Paging File(_Total)\% Usage' `
  -SampleInterval 1 -MaxSamples 5 | Format-List

Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object TotalVisibleMemorySize,TotalVirtualMemorySize
Get-CimInstance -ClassName Win32_PageFileSetting
Get-CimInstance -ClassName Win32_PageFileUsage
(Get-CimInstance Win32_ComputerSystem).AutomaticManagedPagefile

1)规划与分区选择

C: 仅系统,保留 2 GB 固定 PF。

E: NVMe(数据盘 IO 富余),设置 32 GB 固定 PF。

确认 F: 日志盘 与 T: tempdb 不承载 PF。

坑 1(我踩过): 一味把 PF 放 C:,遇到内核转储 + 大流量时,系统盘 IOPS 顶不住。

坑 2: 把 PF 放日志盘,日志延迟抖动极其敏感,夜深人静会被电话叫起来。

2)关闭系统托管并创建固定 Page File

WMIC 在新系统里属“弃用不废”,图形界面可做,但我在机房直接 PowerShell + 注册表更稳且可审计。

方案 A:WMIC(快速)

wmic computersystem where name="%COMPUTERNAME%" set AutomaticManagedPagefile=False

rem 清理历史配置(若有)
wmic pagefileset where name="C:\\pagefile.sys" delete

rem C: 小 PF
wmic pagefileset create name="C:\\pagefile.sys"
wmic pagefileset where name="C:\\pagefile.sys" set InitialSize=2048,MaximumSize=2048

rem E: 主 PF
wmic pagefileset create name="E:\\pagefile.sys"
wmic pagefileset where name="E:\\pagefile.sys" set InitialSize=32768,MaximumSize=32768

方案 B:注册表(更可控、易配置化)

$reg = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management'

# 关闭系统托管
Set-ItemProperty -Path $reg -Name 'ExistingPageFiles' -Value @()
Set-ItemProperty -Path $reg -Name 'PagingFiles' -Type MultiString -Value @(
  'C:\pagefile.sys 2048 2048',
  'E:\pagefile.sys 32768 32768'
)
Set-ItemProperty -Path $reg -Name 'TempPageFile' -Value 0
Set-ItemProperty -Path $reg -Name 'ExistingPageFiles' -Type MultiString -Value @(
  'C:\pagefile.sys',
  'E:\pagefile.sys'
)
# 自动托管开关(有的环境在别处跟踪)
wmic computersystem where name="%COMPUTERNAME%" set AutomaticManagedPagefile=False

提醒:需要重启生效。我当场和业务沟通,挑了 10 分钟窗口。

3)(可选)内核转储与专用转储文件

保留 C: 小 PF 的意义之一就是转储。如果你想把转储放到非系统盘:

$crash = 'HKLM:\SYSTEM\CurrentControlSet\Control\CrashControl'
# 3 = Kernel memory dump;1 = Complete;2 = Small
Set-ItemProperty -Path $crash -Name 'CrashDumpEnabled' -Value 3
# 修改转储路径
Set-ItemProperty -Path $crash -Name 'DumpFile' -Value 'E:\MEMORY.DMP'
# 可设置专用转储文件,避免要求巨大的 C:\pagefile.sys
Set-ItemProperty -Path $crash -Name 'DedicatedDumpFile' -Value 'E:\dedicated_dump.sys'

坑 3: 把 C: 的 PF 彻底删掉,某些情况下会导致内核转储失败或临时创建临时 PF,反而更不可控。

4)重启与变更后校验

# 变更后确认:Commit Limit 是否已拉升?
Get-Counter '\Memory\Committed Bytes','\Memory\Commit Limit','\Paging File(_Total)\% Usage' -SampleInterval 1 -MaxSamples 5

Get-CimInstance -ClassName Win32_PageFileSetting | Select-Object Name,InitialSize,MaximumSize
Get-CimInstance -ClassName Win32_PageFileUsage | Select-Object Name,AllocatedBaseSize,CurrentUsage,PeakUsage
(Get-CimInstance Win32_ComputerSystem).AutomaticManagedPagefile

我当天看到 Commit Limit ≈ 162 GB(128 GB RAM + 34 GB PF 的量级),% Committed Bytes In Use 从 97% 掉到 82% 左右,页文件峰值占用 12% 左右,E: 的平均写延迟仍稳在 1–2 ms。

与数据库配置的配合(关键但容易被忽略)

LPIM(Lock Pages In Memory):启用后能避免 sqlservr 工作集被换出,但并不降低系统的提交需求。所以 LPIM ≠ 可以禁用 Page File。

max server memory:有 PF 后也别贪心。我依旧保持 110 GB,为 OS、驱动、备份代理、杀软/EDR 留足空间。

观察 RESOURCE_SEMAPHORE 等等待类型,必要时在查询层面优化内存授予(grant),这能进一步减少对 Commit Limit 的挤压。

结果对比(我当晚拉了 4 小时窗口)

指标 调整前 调整后 变化
Commit Limit 128 GB ≈162 GB ↑ +34 GB
Committed Bytes(峰值) 124 GB 133–136 GB 负载更猛也不炸
% Committed Bytes In Use 96.9% 82–85% 安全余量充足
Paging File% Usage(峰值) 0% 10–15% 有效兜底
E: 磁盘写延迟 1–2 ms 1–2 ms 基本无感
SQL 701 频繁 归零
事件 2004 多发 归零

你可能会踩的坑(都是我和同事踩过的)

  1. 只在 C: 放超大 PF:系统盘 IOPS/带宽差,撞到大写入会拖慢一切。
  2. 日志盘放 PF:OLTP 日志对延迟敏感,PF 抢 IO 就是灾难。
  3. 忘了重启:注册表改了不重启,实测不生效。
  4. 自动托管没关干净:有的环境组策略/工具会又把它开回去,记得巡检 AutomaticManagedPagefile。
  5. 监控维度只看“可用内存”:必须把 Committed Bytes / Commit Limit 加进看板。
  6. PF 设太大:没有必要做成几百 GB,转储时间、备份/还原清理都难受;重点是够用 + 固定。
  7. 忘记 C: 小 PF:会影响内核转储的稳定性和某些极端回退路径。

一键化脚本(我后来做成了变更模板)

按需要调整盘符与大小;执行后重启。

param(
  [int]$OsPFMB = 2048,
  [int]$MainPFMB = 32768,
  [string]$MainDrive = 'E',
  [ValidateSet('Small','Kernel','Complete')]
  [string]$Dump = 'Kernel',
  [switch]$Reboot
)

$regMM = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management'
$regCrash = 'HKLM:\SYSTEM\CurrentControlSet\Control\CrashControl'

# 1) 关闭自动托管
wmic computersystem where name="%COMPUTERNAME%" set AutomaticManagedPagefile=False

# 2) 设置固定 Page File(C: + 主盘)
$paging = @("C:\pagefile.sys $OsPFMB $OsPFMB", "$MainDrive`:\pagefile.sys $MainPFMB $MainPFMB")
Set-ItemProperty -Path $regMM -Name 'PagingFiles' -Type MultiString -Value $paging
Set-ItemProperty -Path $regMM -Name 'ExistingPageFiles' -Type MultiString -Value @("C:\pagefile.sys","$MainDrive`:\pagefile.sys")
Set-ItemProperty -Path $regMM -Name 'TempPageFile' -Value 0

# 3) 转储类型
$dumpMap = @{ Small = 2; Kernel = 3; Complete = 1 }
Set-ItemProperty -Path $regCrash -Name 'CrashDumpEnabled' -Value $dumpMap[$Dump]
Set-ItemProperty -Path $regCrash -Name 'DumpFile' -Value "$MainDrive`:\MEMORY.DMP"
Set-ItemProperty -Path $regCrash -Name 'DedicatedDumpFile' -Value "$MainDrive`:\dedicated_dump.sys"

Write-Host "Configured paging files:" -ForegroundColor Cyan
Get-CimInstance -ClassName Win32_PageFileSetting | Select Name,InitialSize,MaximumSize | Format-Table

if ($Reboot) { shutdown /r /t 0 }

监控与回归测试(我当晚是这么盯的)

PerfMon 加以下计数器:

  • Memory\Committed Bytes、Memory\Commit Limit、Memory\% Committed Bytes In Use、Paging File(_Total)\% Usage、PhysicalDisk(*)\Avg. Disk sec/Write。

事件日志:

  • Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Resource-Exhaustion-Detector/Operational'; Id=2004} -MaxEvents 20

SQL 侧:

  • 观察 RESOURCE_SEMAPHORE、PAGEIOLATCH_* 等等待。

关键 DMV:

-- 进程级内存与锁页分配
SELECT physical_memory_kb, locked_page_allocations_kb, page_fault_count
FROM sys.dm_os_process_memory;

-- 系统内存快照
SELECT * FROM sys.dm_os_sys_memory;

回归压测:复现高峰典型读写模式,重点看 Commit Ratio 是否稳定在 80–85% 以内。

FAQ:几件容易混淆的事(我给团队做培训时的笔记)

“有 LPIM 就可以不要 Page File?”——不行。LPIM 只是保护 SQL 的工作集,系统提交上限还是需要 PF 来抬高。

“Page File 会拖慢性能吗?”——在 NVMe 上固定大小、且平时只承接少量冷页与 commit 兜底时,影响极小。若 PF 百分比经常 50%+,说明根因是内存配置不足或查询内存授予过大,应优化 SQL 或加内存。

“Page File 要分散到多个盘吗?”——如果是不同物理 NVMe,可以分摊(例如 E: 24 GB + G: 24 GB),Windows 会按比例使用;如果只是同一物理池的不同分区,收益有限。

“一定要 Complete Dump 吗?”——生产通常 Kernel Dump 足够,Complete Dump 要求 C: PF ≥ 物理内存,成本极高。

静下来的风扇与更“宽”的余量

重启后,我又在机房里站了十分钟。风扇的声音不再压迫,E: 上的写入曲线稳得像心电图的等电位线。第二波流量上来时,我看着 Committed Bytes / Commit Limit 稳稳卡在 0.83 左右,告警板没有再红起一片。
我把那杯已经凉透的咖啡丢进垃圾桶,心里很清楚:今晚不是靠某个“玄学优化”救的火,而是把 Windows 的内存提交模型和 Page File 机制重新按到位了。下一次来香港机房,我大概率还是会顺手摸一下 E: 的温度,只是这次,多半只是出于习惯。

TL;DR(给未来的我/你)

  • 别禁用 Page File;C: 留 2 GB,最快的 NVMe 盘做 16–64 GB 固定。
  • 目标:Commit Limit ≥ 峰值提交 × 1.2;监控 Committed Bytes / Commit Limit。
  • 固定大小,避免自动扩容时刻“踩雷”。
  • 别把 PF 放日志盘;和 tempdb 尽量分开。
  • 配合 max server memory 与查询内存授予优化。
  • 变更要重启,并做前后对比与压测回归。

如果你也在香港的机房里被凌晨的告警叫醒,愿这份手册能让你十分钟内做出正确的 Page File 决策,把“内存溢出”的枪口,拧回到可控的位置。

目录结构
全文