惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
阮一峰的网络日志
阮一峰的网络日志
C
Check Point Blog
Stack Overflow Blog
Stack Overflow Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
酷 壳 – CoolShell
酷 壳 – CoolShell
M
MIT News - Artificial intelligence
L
LangChain Blog
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - Franky
WordPress大学
WordPress大学
博客园_首页
Y
Y Combinator Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
Visual Studio Blog
L
LINUX DO - 最新话题
S
Security @ Cisco Blogs
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Help Net Security
Help Net Security
大猫的无限游戏
大猫的无限游戏
Hugging Face - Blog
Hugging Face - Blog
The GitHub Blog
The GitHub Blog
Schneier on Security
Schneier on Security
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
U
Unit 42
Jina AI
Jina AI
雷峰网
雷峰网
罗磊的独立博客
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 【当耐特】
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
人人都是产品经理
人人都是产品经理
Microsoft Security Blog
Microsoft Security Blog
V
V2EX
N
News and Events Feed by Topic
V2EX - 技术
V2EX - 技术
宝玉的分享
宝玉的分享
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Hacker News - Newest:
Hacker News - Newest: "LLM"
P
Proofpoint News Feed
N
Netflix TechBlog - Medium
Martin Fowler
Martin Fowler
O
OpenAI News
P
Proofpoint News Feed
H
Help Net Security
S
Securelist
Vercel News
Vercel News
Hacker News: Ask HN
Hacker News: Ask HN
博客园 - 三生石上(FineUI控件)

博客园 - eaglet

HubbleDotNet 的注册码生成器 文件名通配符匹配的代码 .net 下如何将文档文件(Word, Pdf等) 中的文本提取出来 NTCPMSG 开源高性能TCP消息发送组件 文件在不同文件系统间拷贝文件时间改变的问题 如何自动延长 windows 2008 试用版的试用期 获取ALL USER 的特殊目录的类 i—比 i++ 快? 多线程环境下调用 HttpWebRequest 并发连接限制 HubbleDotNet+Mongodb 构建高性能搜索引擎--概述 C# 重启计算机的问题 HubbleDotNet 索引分词的测试方法和分词技巧 Windows 启动顺序详解 记录windows操作系统启动日志 C# 程序自动批量生成 google maps 的KML文件 通过 Windows 7 共享上网 正确理解 SqlConnection 的连接池机制 Windows 2008 server + IIS 7 设置身份模拟(ASP.NET impersonation) IIS 7.5 配置 FTP Passive 模式
文件是否真的写入了磁盘?
eaglet · 2012-09-13 · via 博客园 - eaglet

作者:eaglet

引用请注明出处

写文件后调用 FileStream.Close; FileStream.Flush; 或者 using (FileStream fs = new FileStream(…)) {} ,文件是否被实际写入了磁盘?可能大多数人都会说肯定会写入磁盘,但我要告诉你,不一定!

背景

我所在的公司有上千台的计算机在同时运行我们的系统,在实际运行过程中,我们发现有时候我们写入的文件会出现全0或者部分全0的情况,但程序中可以肯定的是我们已经关闭了文件句柄。这个问题困扰了我很久。它的发生概率大概在几千分之一,而且大部分是出现在机器重启时,也就是我们更新软件后要求机器自动重启,结果起来后发现更新的软件中有部分文件的大小是对的,但数据全是0。

问题分析

首先考虑的是不是程序的bug,但分析下来,程序没有任何问题,我们甚至用了 File.WriteAllBytes 这样的静态函数来写文件,依然会出现这个问题。

其次考虑的是不是磁盘缓存造成?因为windows操作系统每个磁盘上都可以设置 Enable write caching on the device. 如果打开这个开关,写入操作将先写入磁盘的缓存,然后在到达大小或时间门限时才写入物理磁盘。如果内容还没有完全写入磁盘就重启计算机,就会造成数据丢失。设置如下图所示:

image

于是把这个功能取消,结果发现问题依旧。

这到底是怎么回事?

放狗搜索后发现 windows 除了在磁盘的硬件级别上可以提供缓存外,在操作系统层面也有一个文件缓存

这个功能叫  File Caching, 是 windows 2000 以后提供的功能。如下图所示:

File data caching process

当进程写磁盘时,文件会根据一定的策略缓存到系统的文件缓存中,达到一定门限后才会写入物理磁盘。由于这个系统文件缓存对应用程序是透明的,我们在应用程序中调用 文件的 Close, Flush 只能保证文件已经被写入了操作系统的文件缓存,但无法保证文件实际被写入了磁盘。这个机制虽然提供了较好的写入性能,但却增加了丢失数据的风险。从应用角度,我们从逻辑上认为写入已经成功,但实际上并没有写入到实际的磁盘,也就是说写入是否真的成功了,软件无从知道,这样带来很多逻辑上的混乱。特别是一些服务进程利用文件锁来控制多个进程锁定的,比如 lucene.net, mongodb 等,就经常出现重启后文件锁锁定出问题的情况,估计也和这个机制的作用有关。

那么这个机制的优点到底在哪里呢?

微软提供这个机制当然是有原因的,他的最大优点是大大提高了读取的性能。我们可以做如下的实验:

当我们打开一个大文件,并顺序读取这个文件,我们发现系统开机后,第一次读取的速度是非常慢的,这个速度主要取决于磁盘的读取速度,因为第一次读取是没有缓存的。但当我们关闭进程,再重新运行进程读取这个大文件时,无论是顺序读取还是随机读取,都比原来快上百倍,这就是因为这个操作系统缓存在里面起了作用,数据是从内存读取的。由于这个缓存是全局的,进程退出后,文件的缓存并没有被清空。我所做的开源全文索引项目 HubbleDotNet  的较新版本就充分利用了这个机制,大大提高了机器重启后首次读取索引的速度。

关于windows操作系统的文件缓存机制以及如何优化不在本文的讨论范围内,我将在以后的文章中专门来讲述这个机制是如何工作的。

解决方案

那么回到这个问题

我们有没有办法关闭这个文件缓存呢?答案是否定的。但幸运的是 windows 为应用提供了一个标志叫 FILE_FLAG_WRITE_THROUGH, 这个标志可以让应用在写入缓存的同时直接写入磁盘。

用 C# 实现的代码如下:

using (System.IO.FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None,
                8192, FileOptions.WriteThrough))

在程序中做了如上更改后,2000多台机器运行了半年,没有发现一次文件数据丢失的问题(原来几乎每个月会出现几次),基本可以证明这个机制有效。

深入问题:

1. 采用 WriteThrough 后没有关闭磁盘缓存,会不会造成数据丢失?

我们再看一下本文最上面的那个图,磁盘缓存有两个 CheckBox, 第一个是是否打开磁盘缓存,第二个是是否关闭windows 的文件写缓存刷新磁盘。如果第二个选中,则有可能会出现数据丢失,如果没有选中则不会。

默认设置,这个地方是不选中的。从实际测试来看,磁盘缓存也确实不影响数据丢失的问题。

2. 采用 WriteThrough 后是否会降低写入磁盘的性能。

我认为如果是随机写,可能是会有影响的,但如果是顺序写,FileStream 这个类已经提供了缓存功能,并不会有太大的影响。除非你直接调用windows 的文件API去写入文件并且每次写入文件的内容都较小,这时确实会有影响。因为每次写入都会触发一次物理的磁盘写。