【问题标题】:Reliable writes in linuxlinux中的可靠写入
【发布时间】:2012-04-29 12:26:05
【问题描述】:

我的要求是将输入的可变大小二进制消息的永无止境的流写入文件系统。平均大小为 2KB 的消息以 1000 条消息/秒的速度到达。因此,在一小时内,消息总数将为 3600*1000*2 = 6.8 GB。 消息的主要目的如下 1. 存档以供审核 2.提供搜索界面

我的问题是

  1. 有解决这个问题的开源软件吗
  2. 如果进程写入块大小的倍数并且进程在写入块的过程中崩溃会发生什么样的错误
  3. 可能会发生什么样的错误,应用程序已写入块大小,但文件系统尚未将数据刷新到磁盘。
  4. inode 在任何情况下都会损坏
  5. linux 有文件大小限制吗?
  6. 是否有理想的文件大小?大文件(GB)与中文件(MB)的优缺点是什么
  7. 还有其他需要注意的地方吗?
  8. 我的偏好是使用 C++,但如果需要,我可以切换到 C。

【问题讨论】:

    标签: linux io storage


    【解决方案1】:

    这里的问题是,场景描述不准确。 因此,有些答案是猜测:

    1. 是的 - 它被称为“g++”。 ;-)
    2. 很多不同。恕我直言,通过为您的程序编写大量优秀的测试用例来避免这种情况。
    3. 根据您的系统和程序,“仅”写入内存缓冲区是正常的处理方式。应该没有问题。
    4. 这取决于故障场景和使用的文件系统。 (也有没有 inode 的文件系统。)
    5. 每个文件系统都有其文件大小限制。正确答案(可能对你没用)是:是的。
    6. 否 - 这在很大程度上取决于您的应用程序和环境(如硬盘、备份系统、IO 系统......)
    7. 需要更多信息来回答这个问题。
    8. 不是问题。

    希望这对第一步有所帮助。 如果你决定了你要去的方向,请添加信息 问题 - 你给出的要求越多,答案就越好 可以。

    【讨论】:

      【解决方案2】:

      你手头有一个有趣的问题。我不是这些领域的专家,但有足够的知识来评论这些。

      如果您还没有阅读这篇文章,您可以阅读这篇文章,以了解各种文件系统 linux 的优缺点、限制等。 Comparison of FileSystems in Linux

      1) 我在 Python/Perl 中遇到过自动轮换日志文件库,当然在 C/c++ 中也可用。 2/3/4) 日志文件系统在更大程度上防止文件系统崩溃。他们也支持记录数据,但并没有使用太多。

      Check this for more info on journaling

      【讨论】:

        【解决方案3】:

        一旦writewritev 返回(即操作系统已接受),操作系统负责将数据写入磁盘。这不再是您的问题,并且无论您的进程是否崩溃,它都会发生。请注意,您无法控制一次接受或实际写入的数据量,也无法控制它是否发生在多个文件系统块中,或者它是否具有任何特定大小。您向write 发送请求,它会告诉您它实际接受了多少,它会自行决定将其写入磁盘。
        这可能会以块大小的倍数发生,因为操作系统这样做是有意义的,但不能以任何方式保证(在许多系统上,包括 Linux,读取和写入是通过或与文件映射紧密耦合)。

        同样的“不必关心”保证适用于文件映射(理论上的例外是,崩溃的应用程序原则上仍然可以写入仍然映射的区域,但是一旦您取消映射区域,就不会发生这种情况甚至理论上)。除非您拔掉插头(或内核崩溃),否则数据将被写入,并且是一致的。
        数据只会被写入文件系统块的倍数,因为内存页面是设备块的倍数,而文件映射不知道其他任何东西,它就是这样工作的。

        您可以某种(忽略任何可能的无缓冲磁盘写入缓存)使用fdatasync 对磁盘上的内容进行一些控制。当该函数返回时,之前缓冲区中的内容已被发送到磁盘。
        但是,这仍然不能阻止您的进程同时在另一个线程中崩溃,也不能阻止有人拔掉插头。 fdatasyncfsync 更可取,因为它不会触及 inode 附近的任何东西,这意味着它更快更安全(您可能会丢失在后续崩溃中写入的最后数据,因为长度尚未更新,但您永远不应该销毁/损坏整个文件)。

        C 库函数 (fwrite) 进行自己的缓冲并让您控制写入的数据量,但“写入”数据仅意味着它存储在 C 库拥有的缓冲区中(在您的进程中) )。如果进程死了,数据就消失了。无法控制数据如何到达磁盘,或者如果有的话。 (注意:您确实有一些控制权fflush,这将在返回之前立即将缓冲区的内容传递给底层的写入函数,很可能是writev。这样,你又回到了第一段。)

        异步 ​​IO (kernel aio) 将绕过内核缓冲区,通常直接从您的进程中提取数据。你的进程死了,你的数据消失了。 Glibc aio 使用在write 上阻塞的线程,适用于第 1 段。

        如果您随时拔掉插头或按下“关闭”开关会怎样?没有人知道。
        通常会丢失一些数据,一个操作系统可以提供很多保证,但它不能发挥神奇的作用。尽管从理论上讲,您可能有一个用电池缓冲 RAM 的系统,或者一个具有巨大的专用磁盘高速缓存的系统,该高速缓存也由电池供电。谁也说不清。无论如何,请为丢失数据做好准备。
        也就是说,如果您继续附加到文件,曾经写入的内容通常不应该被破坏(虽然,真的任何事情都可能发生,“不应该”并不意味着很多)。

        总而言之,在追加模式下使用write 或文件映射应该已经足够好了,无论如何它们都已经足够好了。除了突然断电之外,它们还可靠且高效。
        如果电源故障是一个问题,UPS 将提供比任何软件解决方案更好的保证。

        至于文件大小,我认为没有任何理由人为地限制文件大小(假设一个相当新的文件系统)。 “标准”Linux 文件系统(如果有的话)的通常文件大小限制在 TB 范围内。
        无论哪种方式,如果您对因任何原因损坏一个文件可能会破坏 30 天的数据的想法感到不安,请每天创建一个新文件。无需额外费用。

        【讨论】:

        • 至少在Linux系统上,你可以使用fflush结合fsyncsync来确保所有数据都写入磁盘——突然断电(或其他硬件故障) ) 在写作过程中当然可能会违反此保证...
        • @jofel:是的,我在你写这篇文章的时候进行了编辑。 :-) 不过,还有更多。首先它会阻塞,除非你有多个线程,否则每秒有 1000 条消息到达是不行的。对于多个线程,同步无法保证任何事情,因为另一个线程可能会在同步过程中结束您的进程。此外,磁盘上的写入缓存具有未知的电源故障行为(希望是一个好的,但谁知道)。现在当然也有非阻塞同步,但是嗯...
        【解决方案4】:

        您应该使用 SQLite。它解决了你需要的一切。如果您正确使用该数据库,包括速度。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-10-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-01-28
          相关资源
          最近更新 更多