filesystemwatcher监控ntfs压缩文件夹会失效,因压缩导致irp请求分批、事件延迟或重复;writethrough绕过缓存使压缩无效;需用deviceiocontrol设置压缩且句柄须generic_write;小文件压缩反降性能。

Windows 上用 FileSystemWatcher 监控压缩文件夹会失效
NTFS 压缩属性(compact /c)是透明的,但不是“零感知”的。当你对一个文件夹启用 NTFS 压缩后,FileSystemWatcher 的某些事件(尤其是 Created 和 Changed)可能延迟、丢失或触发两次——因为底层写入流程多了压缩/解压缓冲阶段,文件系统通知时机被扰动。
- 这不是 .NET Bug,是 NTFS 驱动层行为:压缩操作可能拆分成多个 IRP 请求,导致事件分批发出
- 若业务依赖实时捕获新建文件(如日志归档、热加载配置),必须在监控路径外做一层校验:比如轮询
LastWriteTime+ 文件大小是否稳定 - 避免对高吞吐目录(如每秒写入数百小文件)启压缩+监控组合,改用非压缩路径 + 应用层 LZ4 压缩写入更可控
FileStream 开启 FileOptions.WriteThrough 时压缩无效
NTFS 压缩只作用于“落盘前的最终写入流”,而 WriteThrough 绕过系统缓存直写磁盘,此时压缩引擎根本没机会介入——文件以原始大小写入,后续也不会自动压缩。
- 验证方式:用
compact /q filename查看结果,显示“0 个文件被压缩”即说明未生效 - 若你既需要强制刷盘又想压缩,只能二选一:
WriteThrough关闭(依赖系统缓存策略),或改用MemoryMappedFile+ 手动压缩后再Flush() - 特别注意日志场景:很多
StreamWriter默认带AutoFlush=true,但底层仍是缓存写入;真正触发WriteThrough需显式传参或调用Flush(true)
C# 中调用 DeviceIoControl 启用压缩需绕过 .NET 封装
.NET 没提供直接设置文件压缩属性的 API,File.SetAttributes 加 FileAttributes.Compressed 在大多数情况下只是修改元数据标记,不触发实际压缩——尤其对已存在内容的文件。
- 真实压缩必须调用 Win32
DeviceIoControl+FSCTL_SET_COMPRESSION控制码,且句柄需以GENERIC_WRITE打开并禁用共享写入(FileShare.None) - 常见错误:用
File.OpenRead获取句柄后尝试压缩 → 报错ERROR_ACCESS_DENIED (5) - 压缩是异步后台操作,调用返回成功 ≠ 文件立即变小;可用
GetCompressedFileSize检查实际压缩后大小,比Length更可靠
小文件反复读写时 NTFS 压缩反而拖慢性能
单个文件小于 4KB 时,NTFS 压缩不仅不省空间(最小分配单元仍是 4KB),还会因每次读写都触发 CPU 解压/压缩而显著增加延迟——实测随机读取 1KB 文件,压缩后 I/O 耗时平均增加 3–5 倍。
- 适用边界很明确:仅对 >64KB 的文本/日志/序列化数据文件开启压缩有收益
- 数据库文件(.mdf/.ldf)、SQLite 文件、内存映射文件一律禁用 NTFS 压缩——它们自身已有压缩逻辑或依赖精确字节偏移
- 若程序大量生成临时小文件(如缓存分片),宁可聚合写入大文件再压缩,也不要逐个启用 NTFS 压缩
压缩不是开关,是权衡:它把磁盘空间换给了 CPU 时间和内核路径复杂度。最常被忽略的是——你永远无法预测某个特定文件在特定硬件上是否真的被压缩了,得靠 compact /q 和 GetCompressedFileSize 实锤,而不是相信属性标记。









