一、现象描述
测试过程中,发现拔掉电脑供电线,C#程序里逻辑判断已经写入到硬盘里的数据变为0,前面多次写入(程序判断写入成功的)也为0。如图所示:
二、测试代码:
一直以为调用Flush()就将数据直接写入了硬盘。
三、问题
调用Flush(),文件是否真的写入了磁盘?
四、解惑
(1)调用了Flush()后,数据不一定成功写入到磁盘。
查看机械硬盘参数,如下图所示:机械硬盘有自己的缓存
(2)查看电脑里硬盘的设置:启用缓存
(3)分析
如果打开硬盘的缓存:启用设备上的写入缓存(W),写入操作将先写入磁盘的缓存,然后在到达大小或时间门限时,才写入物理磁盘。如果内容还没有完全写入磁盘就重启计算机,就会造成数据丢失。于是把这个功能取消,结果发现问题依旧。说明不是硬盘缓存造成的。
这是因为File data caching process进程写磁盘时,文件会根据一定的策略缓存到系统的文件缓存中,达到一定门限后才会写入物理磁盘缓存。由于这个系统文件缓存对应用程序是透明的,我们在应用程序中调用 文件的 Close, Flush 只能保证文件已经被写入了操作系统的文件缓存,但无法保证文件实际被写入了磁盘缓存。这个机制虽然提供了较好的写入性能,但却增加了丢失数据的风险。从应用角度,我们从逻辑上认为写入已经成功,但实际上并没有写入到实际的磁盘,也就是说写入是否真的成功了,软件无从知道,这样带来很多逻辑上的混乱。
(4)缓存机制的优点
微软提供这个机制当然是有原因的,他的最大优点是大大提高了读取的性能。我们可以做如下的实验:当我们打开一个大文件,并顺序读取这个文件,我们发现系统开机后,第一次读取的速度是非常慢的,这个速度主要取决于磁盘的读取速度,因为第一次读取是没有缓存的。但当我们关闭进程,再重新运行进程读取这个大文件时,无论是顺序读取还是随机读取,都比原来快上百倍,这就是因为这个操作系统缓存在里面起了作用,数据是从内存读取的。由于这个缓存是全局的,进程退出后,文件的缓存并没有被清空。
五、解决办法
(1)我们有没有办法关闭这个文件缓存呢
答案是否定的。
(2)办法
幸运的是 windows 为应用提供了一个标志叫 FILE_FLAG_WRITE_THROUGH, 这个标志可以让应用在写入缓存的同时直接写入磁盘缓存(磁盘缓存掉电时有自己的数据保护机制)。代码如下所示:
测试发现,电脑断电后数据没有丢失。测试结果如下所示:
六、深入问题
(1)采用 WriteThrough 后没有关闭磁盘缓存,会不会造成数据丢失?
如果第二个选中,则有可能会出现数据丢失,如果没有选中则不会。从实际测试来看,磁盘缓存也确实不影响数据丢失的问题。
(2)采用 WriteThrough 后是否会降低写入磁盘的性能。
写入单个文件测试结果:
写入多个文件结果:
测试结果发现,采用 WriteThrough确实影响了程序的性能,相对于电脑断电丢失数据的危害性,性能的降低勉强可以接受。
如果追求性能,必须应用软件做缓存和数据比对处理。