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

香港服务器上的 Windows Server 2012:我如何把分页文件调到“听话”,并把数据库 I/O 抖动按回去

发布人:Minchunlin 发布时间:2025-08-22 08:45 阅读量:589


凌晨 2:40,香港葵涌机房的NOC的大屏上,数据库实例的延迟每隔几分钟就炸一次——从 8ms 突刺到 300ms。客户的交易系统一顿一顿的,电话没停过。那一刻,我盯着那台跑着 Windows Server 2012 的老伙计,心里只有一句话:“得先让内存和分页文件别内耗,再谈 I/O。”

现场环境与硬件拓扑

组件 型号/版本 关键参数 备注
机型 Dell PowerEdge R730 2×Xeon E5-2630 v3, 96GB RAM 双电冗余
操作系统 Windows Server 2012 Standard(非 R2) 已打至最新补丁 GUI 安装
数据库 Microsoft SQL Server 2014 Standard 单实例,OLTP 本文也给出 MySQL 补充要点
RAID 控制器 PERC H730P 2GB Cache,Write Back + Force WB w/o BBU 关闭 开启 Write Back(BBU 健康)
系统盘 2× 300GB 10K SAS RAID1 C:
数据盘 8× 600GB 10K SAS RAID10 D: DATADATA
日志盘 2× Intel S3710 400GB RAID1 E: LOGLOG(企业级 SATA SSD)
TempDB 同 E: 固定大小文件  
分页盘 2× Intel S3710 200GB RAID1 G: PAGEPAGE 专用,不放别的东西
网络 双口万兆 + LACP 前端接交易中间层

为什么不用 NVMe?

Windows Server 2012(非 R2)对 NVMe 的支持不如新系统靠谱;我们选择了企业级 SATA SSD,减少驱动“玄学”。稳定 > 一切花哨。

症状复盘:抖动并非“磁盘慢”,而是“内存-分页-存储”的连环踢

每隔 5–10 分钟,磁盘队列长度在数据盘 D: 突然拉高(> 50),平均磁盘读等待飙到 200–300ms。

SQL 等待类型短时以 PAGEIOLATCH_* 和 WRITELOG 为主。

Windows Perfmon 里:

  • Memory\Committed Bytes 接近 Memory\Commit Limit;
  • Paging File(_Total)\% Usage 突破 35%,Memory\Page Reads/sec 有刺刀式上扬;
  • Process(sqlservr)\Working Set 被动回落,紧接着发生磁盘抖动。

资源监视器显示零星硬错误(Hard Faults/sec)在抖动点飙高。

初步判断:系统托管(System Managed)的分页文件跨了 C: 和 D:,在数据库读写压力点触发增长/收缩与争抢;同时 SQL Server 的 max server memory 太激进,留给 OS、驱动和文件缓存的余量不足,触发内存修剪,连带换入换出,在 D: 上与数据文件抢 I/O。

诊断方法(我在现场怎么一步步定位)

1)看“承诺内存/提交限制”(Commit)是否顶天花板

# 快速看 Commit 与 Pagefile 使用
Get-Counter '\Memory\Committed Bytes','\Memory\Commit Limit','\Paging File(_Total)\% Usage','\Memory\Page Reads/sec' -SampleInterval 1 -MaxSamples 10

经验阈值:Committed Bytes / Commit Limit 长期 > 0.8 就要警觉;Page Reads/sec 成团攀升通常意味着真的在抖。

2)确认分页文件的真实布局与策略

# 是否系统托管分页
(Get-WmiObject -Class Win32_ComputerSystem).AutomaticManagedPagefile

# 现有分页设置
Get-WmiObject -Class Win32_PageFileSetting | Select-Object Name, InitialSize, MaximumSize

现场发现:系统托管且分布在 C: 和 D:,D: 正是数据盘。

3)验证卷格式与分配单元

# 看簇大小
fsutil fsinfo ntfsinfo D:
fsutil fsinfo ntfsinfo E:

D: 被人用默认 4KB 格式化过,E: 为 64KB。数据库卷建议 64KB。

4)SQL 侧等待与内存

-- 观察等待类型
SELECT TOP 10 * FROM sys.dm_os_wait_stats ORDER BY wait_time_ms DESC;

-- 内存压力
SELECT * FROM sys.dm_os_process_memory;

5)控制器缓存与电池

进入 PERC BIOS 检查:BBU 正常,Write Back 打开;若 BBU 异常导致 Write Through,会放大抖动。

解决思路:三板斧把“内存-分页-I/O”链条重新梳直

把分页文件固定到一块独立 SSD(G:),且固定大小,消灭动态增长与跨卷争抢。

给 OS 留足内存余量:限制 SQL Server max server memory,并启用“锁定内存”(Lock Pages in Memory),避免被 OS 随意修剪。

把存储基本功补齐:数据库卷 64K 簇、TempDB 独立 SSD、Instant File Initialization、服务器电源策略设为高性能。

实操步骤(可直接照抄执行)

A. 固定并迁移分页文件到专用 SSD(G:)

容量怎么定?

先看峰值 Committed Bytes (峰值)。我们的峰值约 78GB,物理内存 96GB。目标是Commit Limit ≥ 峰值 + 安全余量。
由于计划使用**内核内存转储(Kernel Dump)**而非完整转储,pagefile 不必 ≥ RAM。最终我给 24GB 固定值(足以覆盖峰值余量 + dump 需求),你可以按自己峰值+10~20%来定,常见区间 16–32GB。

步骤:

# 1) 关闭系统托管分页
Set-WmiInstance -Class Win32_ComputerSystem -Arguments @{ AutomaticManagedPagefile = $false }

# 2) 删除旧的分页设置(谨慎:如果在远程会话里做,建议维护窗口)
Get-WmiObject -Class Win32_PageFileSetting | Remove-WmiObject

# 3) 在 G: 创建固定分页文件,单位 MB(这里设 24576MB = 24GB)
Set-WmiInstance -Class Win32_PageFileSetting -Arguments @{
    Name='G:\pagefile.sys'; InitialSize=24576; MaximumSize=24576
}

# 4) 将系统故障转储设为“内核转储”,避免要求过大的 pagefile
# 0=禁用,1=完全,2=内核,3=小内存
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\CrashControl' -Name CrashDumpEnabled -Value 2

# 5) 重启(必须)
Restart-Computer

坑 1: 不要把分页文件放在启用数据重删或压缩的卷上(Server 2012 的重删卷不支持 pagefile)。

坑 2: 远程操作时请安排维护窗口,重启前确认你还有 out-of-band 管理(iDRAC/iLO)。

验证:

Get-CimInstance -ClassName Win32_PageFileSetting | Format-Table Name,InitialSize,MaximumSize
Get-Counter '\Paging File(_Total)\% Usage','\Memory\Commit Limit','\Memory\Committed Bytes'

B. 给 OS 留内存:SQL Server Max Memory & Lock Pages in Memory

估算(经验值):

  • OS、驱动、文件缓存、杀毒、备份代理等至少预留 6–10GB;
  • 96GB RAM 的机器,我通常把 SQL max server memory 设在 72–80GB 起步,根据观测微调。

操作:

-- 打开高级选项
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;

-- 设置上限(例如 80GB = 81920MB)
EXEC sp_configure 'max server memory (MB)', 81920; RECONFIGURE;

启用“锁定内存”(避免工作集被 OS 修剪):

  • 运行 secpol.msc → 本地策略 → 用户权限分配 → 锁定内存中的页面。
  • 添加运行 SQL Server 服务的账号(如 NT Service\MSSQLSERVER 或自定义域账号)。
  • 重启 SQL Server 服务。

坑 3: 启用 Lock Pages 后更要控制好 max server memory,否则你可能把 OS 吃到缺氧。

C. 存储与卷的基本功

卷按 64KB 簇格式化(数据库/日志/TempDB 卷)

PowerShell(Server 2012 可用):

Format-Volume -DriveLetter D -FileSystem NTFS -NewFileSystemLabel 'DATA' -AllocationUnitSize 65536 -Force
Format-Volume -DriveLetter E -FileSystem NTFS -NewFileSystemLabel 'LOG'  -AllocationUnitSize 65536 -Force

坑 4: 重格式化会清空数据,请提前迁移/备份。

TempDB 独立 SSD,固定文件 & 固定增长

-- 按 CPU 核心数设置 4~8 个数据文件,大小相等,增长固定块
USE master;
ALTER DATABASE tempdb MODIFY FILE (NAME = tempdev, SIZE = 1024MB, FILEGROWTH = 512MB);
-- 假设创建另外 3 个
ALTER DATABASE tempdb ADD FILE (NAME = tempdev2, FILENAME='E:\MSSQL\DATA\tempdb2.ndf', SIZE=1024MB, FILEGROWTH=512MB);
ALTER DATABASE tempdb ADD FILE (NAME = tempdev3, FILENAME='E:\MSSQL\DATA\tempdb3.ndf', SIZE=1024MB, FILEGROWTH=512MB);
ALTER DATABASE tempdb ADD FILE (NAME = tempdev4, FILENAME='E:\MSSQL\DATA\tempdb4.ndf', SIZE=1024MB, FILEGROWTH=512MB);
ALTER DATABASE tempdb MODIFY FILE (NAME = templog, SIZE = 512MB, FILEGROWTH = 256MB);

开启 Instant File Initialization(IFI)

本地安全策略 → 用户权限分配 → 执行卷维护任务 添加 SQL 服务账号。

这样数据文件增长不再清零,减少写放大(日志文件不受 IFI 影响)。

电源策略设为高性能

powercfg -L
powercfg -S 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c  rem 一般这是“高性能”GUID

控制器缓存

PERC H730P → Write Back + 缓存策略确认,BBU 健康。

不要因为“怕断电”就长期写直通(Write Through),那是抖动放大器。

D. 压力与验证工具

DiskSpd(替代 SQL 压测,模拟随机读写)

rem 数据盘随机读写(30% 写,64K,4 线程,队列深度 32,持续 60 秒)
diskspd -c20G -t4 -r -w30 -o32 -b64K -d60 D:\diskspd_test.dat > D:\before.txt

rem 调整后复测
diskspd -c20G -t4 -r -w30 -o32 -b64K -d60 D:\diskspd_test.dat > D:\after.txt

Perfmon 收敛指标(重点看时间窗 1 小时以上)

  • PhysicalDisk(*)\Avg. Disk sec/Read 常态 < 10ms
  • PhysicalDisk(*)\Avg. Disk sec/Write 常态 < 5–8ms(日志盘最好 < 3–5ms)
  • Paging File(_Total)\% Usage 常态 < 10%
  • Memory\Committed Bytes / Commit Limit 常态 < 0.7
  • SQLServer:Buffer Manager\Page life expectancy 明显回升(结合业务解读)

结果对比(来自现场导出的核心指标)

指标 调整前(峰值/常态) 调整后(峰值/常态) 备注
Committed Bytes / Commit Limit 0.92 / 0.85 0.72 / 0.60 固定分页 + 下调 SQL 内存
Paging File % Usage 38% / 25% 8% / 3% 迁到 G:,固定 24GB
D: Avg. Disk sec/Read 0.28s / 0.06s 0.012s / 0.006s 去掉分页争抢 + 64K 簇
E: Avg. Disk sec/Write 0.035s / 0.010s 0.006s / 0.003s 日志在 SSD RAID1
SQL PAGEIOLATCH_* 等待 间歇爆发 零星、业务峰时可控 TempDB 调优也有贡献
交易 P99 延迟 420ms 85ms 峰值时段对比
抖动周期 5–10 分钟 基本消失 偶发峰由批处理触发,可预期

额外细节与那些“坑”

分页文件最小值过小:系统托管喜欢“动态”,但数据库场景最怕动态增长——锁表外还有pagefile 扩容锁;固定大小是王道。

完整内存转储 vs 内核转储:完整转储要求 pagefile ≥ RAM,很多人因此把 pagefile 设成 96GB/128GB,反而拖累 I/O。除非极端问题定位,一般内核转储够用。

多分页文件:如果要多个 pagefile,只放在快盘,且不要与数据库热路径同盘。跨盘 pagefile 未必更快,反而可能触发跨盘争抢。

杀毒/备份代理:把数据库数据、日志、TempDB、pagefile 全部加到实时扫描排除。

卷重删/压缩:数据库与 pagefile 都不要上。

MySQL on Windows 补充:

  • innodb_flush_log_at_trx_commit=1(默认)保证持久性,日志盘必须快;
  • innodb_flush_method=O_DIRECT 减少双缓存;
  • Windows 上依然要 64K 簇、日志 SSD、Temp/ibtmp 独立快速卷;
  • 留内存给 OS,避免触发系统级分页压力。

我用过的“判定清单”(落地复盘用)

  •  AutomaticManagedPagefile=False,pagefile 仅在 G:,固定 24GB
  •  CrashDump Kernel
  •  SQL max server memory 已下调至 80GB;LPIM 开启
  •  D:/E: 64K 簇;TempDB 多文件、固定增长
  •  Instant File Initialization 已开启
  •  电源计划 高性能;PERC Write Back + BBU 正常
  •  杀毒/备份排除路径生效
  •  Perfmon 一周观测:Avg. Disk sec/*、Page Reads/sec 收敛

常用命令速记卡

# 查看簇大小/卷信息
fsutil fsinfo ntfsinfo D:

# 查看分页使用
Get-Counter '\Paging File(_Total)\% Usage','\Memory\Page Reads/sec'

# SQL 上限
sp_configure 'max server memory (MB)'

# 电源策略
powercfg -L; powercfg -S <GUID>

# 盘格式化(危险,慎用)
Format-Volume -DriveLetter D -FileSystem NTFS -AllocationUnitSize 65536 -Force

尾声:把“跌宕”变成“脉动”

第二天早上 7 点,NOC 的曲线终于变成了我想要的样子:不是一马平川的假平稳,而是贴着业务节奏的细微脉动。抖动消失,报警沉默。客户在电话那头说:“这次稳了。”
我合上笔记本,回头看那台 R730 ——它没有变快多少,但我们让它更懂得把力气用在该用的地方:分页文件不添乱,内存不内耗,I/O 不抢道。很多时候,优化并不是上更快的硬件,而是让系统中每一层各司其职。

TL;DR(给赶时间的人)

  • 固定分页文件到独立 SSD,固定大小(按峰值 Commit + 安全余量,常见 16–32GB);
  • 限制 SQL 内存并启用 Lock Pages in Memory,给 OS 留 6–10GB;
  • 卷 64K 簇、TempDB 独立 SSD、IFI 开启、高性能电源、控制器 Write Back;
  • 用 Perfmon/DiskSpd 验证,关注 Committed Bytes/Commit Limit、Page Reads/sec、Avg. Disk sec/*。

如果你也在香港机房里被 I/O 抖动“支配过”,按上面的顺序动手,先稳内存与分页,再治存储。多数时候,问题会像那晚的冷气一样,逐渐“消散”。

目录结构
全文