我正在编写一些软件来处理非常关键的数据,并且需要知道我需要做些什么来实现持久性.
我看的每个地方都是矛盾的信息,所以我很欣赏任何见解.
我写入磁盘有三种方法.
使用O_DIRECT | O_DSYNC,pread'ing然后pwrite'ing 512字节 - 16 MB块.
使用O_DIRECT,pread'ing然后pwrite'ing 512字节块,并根据需要定期调用fdatasync.
使用内存映射文件,我根据需要定期调用msync(...,MS_SYNC | MS_INVALIDATE).
这是所有在ext4上的默认标志.
对于所有这些,数据是否可能丢失(写入或同步返回后)或由于电源故障,恐慌,崩溃或其他任何原因而损坏?
如果我的服务器在pwrite中间,或者在pwrite的开头和fdatasync的结尾之间,或者在被更改的映射内存和msync之间,我可能会混合旧数据和新数据,或者它会是一个还是其他?我希望我的个人pwrite调用是原子的并且是有序的.是这样的吗?如果它们跨多个文件就是这种情况吗?所以,如果我用O_DIRECT |写 O_DSYNC到A,然后是O_DIRECT | O_DSYNC到B,我保证,无论发生什么,如果数据在B中,它也在A中?
fsync甚至可以保证数据的写入吗?这不是说,但我不知道从那时起事情是否发生了变化.
ext4的日志记录是否完全解决了这个SO答案所存在的腐败块的问题?
我目前通过调用posix_fallocate然后ftruncate来增长文件.这些都是必要的,它们是否足够?我认为ftruncate实际上会初始化已分配的块以避免这些问题.
为了给混音添加混乱,我在EC2上运行它,我不知道这是否会影响任何东西.虽然它很难测试,因为我无法控制它被关闭的积极程度.
我有一个测试程序.在Linux内核3.1.*上大约需要37秒,但在内核3.0.18上只需要大约1秒钟(我只需要在同一台机器上替换内核).请给我一个如何在内核3.1上改进它的线索.谢谢!
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int my_fsync(int fd)
{
// return fdatasync(fd);
return fsync(fd);
}
int main(int argc, char **argv)
{
int rc = 0;
int count;
int i;
char oldpath[1024];
char newpath[1024];
char *writebuffer = calloc(1024, 1);
snprintf(oldpath, sizeof(oldpath), "./%s", "foo");
snprintf(newpath, sizeof(newpath), "./%s", "foo.new");
for (count = 0; count < 1000; ++count) {
int fd = open(newpath, O_CREAT | O_TRUNC | O_WRONLY, S_IRWXU);
if (fd == -1) {
fprintf(stderr, "open …Run Code Online (Sandbox Code Playgroud) 从确保数据在磁盘上的信息(http://winntfs.com/2012/11/29/windows-write-caching-part-2-an-overview-for-application-developers/),即使是在例如停电,似乎在Windows平台上,您需要依靠其"fsync"版本FlushFileBuffers来最好地保证缓冲区实际上是从磁盘设备缓存刷新到存储介质本身.如果此信息正确,则FILE_FLAG_NO_BUFFERINGwith 的组合FILE_FLAG_WRITE_THROUGH不会确保刷新设备缓存,而只会影响文件系统缓存.
鉴于我将使用相当大的文件,需要"事务性地"更新,这意味着在事务提交结束时执行"fsync".所以我创建了一个小应用来测试这样做的性能.它基本上使用8次写入执行一批8个内存页大小的随机字节的顺序写入,然后刷新.批处理循环重复,在每隔这么多的书面页面之后记录性能.此外,它有两个可配置的选项:在开始页面写入之前,对刷新进行fsync以及是否将字节写入文件的最后位置.
// Code updated to reflect new results as discussed in answer below.
// 26/Aug/2013: Code updated again to reflect results as discussed in follow up question.
// 28/Aug/2012: Increased file stream buffer to ensure 8 page flushes.
class Program
{
static void Main(string[] args)
{
BenchSequentialWrites(reuseExistingFile:false);
}
public static void BenchSequentialWrites(bool reuseExistingFile = false)
{
Tuple<string, bool, bool, bool, bool>[] scenarios = new Tuple<string, bool, bool, bool, bool>[]
{ …Run Code Online (Sandbox Code Playgroud) 这个问题是我发布的早期问题的后续跟进:Windows fsync(FlushFileBuffers)与大文件的性能.在哪里我找到了可能的解决方案,但也有新问题.
在对不同的情节进行基准测试时,我发现了许多令人惊讶的结果.我希望有人可以帮助解释或指出我解释这些结果的信息方向.
这个基准测试的作用是按顺序将8个页面(32 K)的随机块(大4096字节)写入文件,然后刷新写入.它总共写了200000页,总计800 MB和25000次刷新.在开始写入之前,文件的大小设置为其最终长度.
它支持总共4个选项,其中运行所有组合:
FlushFileBuffers在写入批处理或正常刷新(NS)之后执行"fsync"/ 操作(FS).下表总结了我对我的系统(具有慢速主轴磁盘的64位Win 7笔记本电脑)的所有这些选项组合的发现.

我发现的是,"fsynced"缓冲写入的性能与文件的一个令人难以置信的低吞吐量的大小,使大型文件这样做不可行组合呈指数下降.如果文件的最后一个字节写入(选项LB),吞吐量甚至更低,所以我担心在随机而不是顺序写入情况下,性能将更加显着.
然而令人惊讶的是,对于无缓冲/写入I/O,吞吐量保持不变,与文件大小无关.最初(前100-200 MB)它的吞吐量低于缓冲写入,但之后平均吞吐量迅速赶上并且完成写入800 MB的速度更快.更令人惊讶的是,如果文件的最后一个字节写入,吞吐量会增加2倍.
通过内存映射文件写入文件时,可以看到相同的指数性能下降,同样在使用无缓冲/写入标志打开文件的情况下.在这里,如果文件有一个字节写入其最后位置,性能会更差.
UPDATE 基于霍华德的解释这里和这里,我重新测试,而无需启动之前写创建一个新的文件(即打开现有的,充分的书面文件,并覆盖它).我已更新原始问题中的代码,以反映此测试所做的更改.结果部分符合他对Linux的解释和发现.但有一些值得注意的例外.下表给出了结果,红色亮点显著的变化,蓝色的亮点,其中的变化并没有出现,这是令人惊讶的(即不符合预期,如果在霍华德的解释中提到的效应在作怪唯一的).

对于具有"fsync"刷新的缓冲写入文件(即不通过memmap),性能现在已经从指数衰减变为恒定趋势.但是,它现在比以前的测试场景需要更长的时间.吞吐量是一个恒定的1.5 MB/s,在它开始之前大约20 MB/s,指数衰减到1.5 MB/s左右.看起来可能的解释是文件元数据也会在每次刷新时被刷新,从而导致完整的磁盘革命以寻找元数据的位置.
对于"直写"到文件场景,写入最后一个字节的结果现在是相同的,与霍华德的解释所期望的一致.
然而,对存储器映射的写入,有一个值得注意的例外,并没有真正改变,这是令人惊讶的.它们在写入性能方面仍然表现出相同的指数衰减(从大约20 MB/s衰减到1.8 MB/s开始).这表明正在发挥不同的机制.一个值得注意的例外是如果基础文件是在没有FILE_FLAG_WRITE_THROUGH的情况下创建的,则执行"fsync"刷新.此方案现在显示恒定(差)性能,吞吐量约为1.6 MB/s.由于我有些疑惑,我多次重复这种情况,每次给出相同的结果.
为了找出多一点,我也使用较小的文件(50000页,共计200 MB),以确认,该FSYNC性能(缓冲I/O)实际上是不依赖于文件大小重新运行该测试.结果显示如下,值得特别注意的结果用红色突出显示.

这些结果与较大文件的结果很好地相关.值得注意的变化是,对于那些突出显示的内容,写入更有效,它们似乎达到了大约7 MB/s的限制.
总结为基于对我的系统的观察到目前为止的高度推测性结论:
鉴于这种观察到的性能,至少在我的用例中,这两个I/O选项并不代表可行的解决方案.
根据Greg的建议,我将在关闭Windows磁盘缓存的情况下重新运行测试,并且我还将运行Howard提供的基准代码,以排除由于我自己的错误而导致结果偏差的可能性.
更新2 我已完成测试,目前正在编译结果.为了不写"完整的历史",我将用结果,发现和一些结论的摘要替换这个问题的当前内容.霍华德在这个问题上的答案,以及在.NET代码旁边运行他的c基准代码的能力是最有用的.应用程序的结果相关性很好.Rlb的回答帮助我更好地理解了与磁盘相关的"合理数字".谢谢.
问题的一部分仍未得到答复.特别与在写入存储器映射时观察到的降低(和依赖于文件大小)性能有关.这可能与寻找/元数据刷新,但目前尚不清楚,我为什么/如何.
我看了一下这个话题:"PostgreSQL与fsync.如何将PostgreSQL错误地使用fsync 20年,以及我们将采取什么措施." 通过https://fosdem.org/2019/schedule/event/postgresql_fsync/,并阅读https://lwn.net/Articles/752063/作为背景.
如果你调用fsync()并且它失败了,那么真正的简短和简化的摘要是使用Linux,不要认为你可以再次调用fsync()来修复它,因为第二次调用成功并且你将在磁盘上损坏数据(失败的缓冲区缓存页面在第一次失败的呼叫后标记为干净).关于为什么会发生这种情况有很多细节(支持取出USB的情况 - 你不想重试并保持永远无法成功的脏缓冲区缓存页面).
FlushFileBuffers()在这种情况下如何表现?我对通过CIFS访问的文件特别感兴趣,因为更可能出现故障.
此外,鉴于操作系统可以尝试在后台随时将脏缓冲区缓存页写入稳定存储,用户登陆程序如何通过Win32 API获取这些故障?
我正在编写一个小应用程序,它在SD卡上以恒定速率写入jpeg图像.我选择了一个EXT3文件系统,但是在EXT2文件系统中观察到了相同的行为.
我的写循环看起来像这样:
get_image()
fwrite()
fsync()
Run Code Online (Sandbox Code Playgroud)
或者像这样:
get_image()
fopen()
fwrite()
fsync()
fclose()
Run Code Online (Sandbox Code Playgroud)
我还显示了一些时序统计信息,我可以看到我的程序有时会被阻止几秒钟.平均速率仍然很好,因为如果我将传入的图像保持为fifo,那么我将在这样的停顿之后的短时间内写出许多图像.您知道操作系统是否存在问题,或者它是否与SD卡本身有关?我怎么能接近实时?我不需要强大的实时性,但是停滞几秒钟是不可接受的.
一些精度:是的,每个文件后都需要fsync,因为我希望图像在磁盘上,而不是在某些用户或内核缓冲区中.没有fsyncing,我有更好的吞吐量,但仍然是不可接受的失速.我不认为这是一个缓冲区问题,因为第一次失速发生在写入50 MB之后.根据手册页,fsync正是为了确保没有数据缓冲.
关于平均写入速率的精确度:我正在以我正在使用的卡可持续的速率写入.如果我在等待fsync完成时堆叠传入的图像,那么在此停顿之后写入传输速率将增加,我将很快回到平均速率.平均传输速率约为1.4 MB/s.
系统是一台现代笔记本电脑运行ubuntu 8.04与库存记录(2.6.24.19)
fsync(2) 联机帮助页告诉如果同步文件,则明确需要同步目录。
io包中Java的sync方法怎么样?是在意那个吗?它是否取决于操作系统和/或文件系统?
我在http://docs.oracle.com/javase/7/docs/api/java/io/FileDescriptor.html#sync 中发现没有任何帮助...
我有一个禁用写入缓存的SATA硬盘:
hdparm -W0 /dev/foo
Run Code Online (Sandbox Code Playgroud)
我正在ext4使用这些挂载选项(以及其他)的分区上运行:
data=ordered
auto_da_alloc
Run Code Online (Sandbox Code Playgroud)
Linux内核版本是2.6.32-5-686.
现在,我有一个我无法修改的外部程序,但我知道以下列方式创建一个文件:
int fd = open(path);
write(fd, data, data_size);
close(fd);
Run Code Online (Sandbox Code Playgroud)
即它在关闭之前不会fsync.所以在这一点上,数据可能在RAM中,在kernel/fs缓存中的某个地方.
注意:元数据还不是一个问题:在我确保数据已经到达磁盘盘片之后,最终的元数据将被写入并保存.数据本身就是问题所在.
所以问题是,我如何帮助数据到达实际的磁盘盘片?
我之后想过运行这个单独的程序:
int fd = open(path);
fsync(fd);
close(fd);
Run Code Online (Sandbox Code Playgroud)
这有助于刷新数据,还是应该使用不同的方法?
是否有替代Windows的fsync?(C++ builder)
Fsync需要包含unistd.h,它仅适用于unix系统
谢谢!
如何在不需要我的程序等待慢速物理媒体(例如 with fsync)的情况下自动更新文件?
我希望操作系统可以在 RAM 中“缓冲”典型fsync和rename操作,然后在方便时以正确的顺序将它们写入磁盘。
我正在开发在带有ext4文件系统的自定义嵌入式 Linux 环境中运行的软件。该程序定期更新磁盘上的文件。我需要在不牺牲应用程序性能的情况下保持该文件的完整性。
根据我的阅读,安全更新文件的公认做法如下:
fsync() 临时文件 fsync() 包含目录这个过程对我来说很有意义,但在我的特定应用程序中,我想避免对fsync(). 我不在乎数据何时写入磁盘,只要文件始终处于有效状态即可。如果文件已过期,那没关系。
似乎已经有相当多的讨论ext4和正确使用fsync. 如果我理解正确,我可能可以放弃使用fsyncif auto_da_allocis enabled for my filesystem ( link ),但我不相信这是最好的解决方案。
fsync ×10
linux ×5
windows ×4
c# ×2
ext4 ×2
mmap ×2
performance ×2
c ×1
c++ ×1
durability ×1
ext3 ×1
filesystems ×1
java ×1
linux-kernel ×1
posix ×1
real-time ×1
sata ×1
sd-card ×1
winapi ×1