在 C 中追加或创建文件时如何要求连续块(非碎片)?
How to ask for continuous blocks (non fragmented) when appending or creating files in C?
所以我想了解 DBMS 实现的工作原理
举个例子:
MySQL 用自己的页面实现每个 table,页面大小为 16KB
所以每个 table 都是一个文件,并且是 16KB 的倍数,考虑到它有多大,因此需要多少页
现在我在某处读到这些页面不会在磁盘映像或内存映像[=中碎片化31=],所以我的问题是,怎么做?
DBMS 开发人员如何告诉操作系统 "hey i just added a 16KB data (page) to this file, but make this page doesn't get fragmented"
是不是因为内存映像实际上没有显示字节在磁盘上的实际存储方式及其逻辑?
或者是因为这些 DBMS 以某种方式要求 O.S 确保这些 16KB 字节的块不会碎片化?
以及如何在 C 中执行此操作?
how to do this in C:
int add16k(void *My16kDataChunk) {
fd = open(“My.DataBase”, O_WRONLY|O_APPEND);
if (fd != -1) {
write(fd, My16kDataChunk, 16*1024);
close(fd);
}
return fd != -1;
}
但对于数据库,您可能希望缓存打开的文件描述符、能够以任意偏移量写入、确保数据被真实记录(*) 等。最重要的是,您希望确保多个请求互不干扰。相反,您需要:同步、fdatasync、pwrite。
memory image doesn’t show ...
It is because the memory image is identical to what is on disk. Two subsystems, the VM and FileSystem co-operate to achieve this. If I write 16k, and the file system has to collect 4 discontiguous 4k sectors to store it, it arranges my read() and write() calls to be oblivious to this layout -- it delivers the data to and from virtually contiguous areas. Similarly, if a 16k buffer requires 4 discontiguous 4k physical memory pages, the VM system arranges the page mappings to construct a contiguous virtual range.
就是说,一些文件系统支持预先分配磁盘的物理连续区域的机制[虽然卷管理、SAN、虚拟化这主要是假装的],这样文件系统就可以实现性能目标和开销便携性。
(*) - 这似乎是一个相当简单的想法,我可以调用 fsync()、fdatasync()、sync() 等。是的,不。 http://blog.httrack.com/blog/2013/11/15/everything-you-always-wanted-to-know-about-fsync/ 对此进行了很好的处理。 TL;DR - OS/FileSystem 人们对真理的松散观念会让编译器供应商脸红。
在严格符合的 C 代码中,你不能。标准 C 对文件是什么有一个非常模糊的概念。除了以二进制模式打开文件并逐字节读取并计数之外,甚至无法使用严格符合的 C 来完成文件的大小。*根本没有办法指定文件的大小存储在符合标准的 C 代码中。
因此您只能使用依赖于系统的方法。
POSIX 提供 the posix_fallocate()
function:
SYNOPSIS
#include <fcntl.h>
int posix_fallocate(int fd, off_t offset, off_t len); [Option End]
DESCRIPTION
The posix_fallocate()
function shall ensure that any required
storage for regular file data starting at offset and continuing for
len bytes is allocated on the file system storage media. If
posix_fallocate()
returns successfully, subsequent writes to the
specified file data shall not fail due to the lack of free space on
the file system storage media.
但请再次注意,无法确保底层系统如何确保后续写入文件成功。
如果文件 space 是连续的,它仍然是 "implementation-defined"。保留的 space 更有可能被文件系统分配为连续的块,但绝对不能保证。
Linux 提供 the fallocate()
function:
SYNOPSIS
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h>
int fallocate(int fd, int mode, off_t offset, off_t len);
DESCRIPTION
This is a nonportable, Linux-specific system call. For the portable,
POSIX.1-specified method of ensuring that space is allocated for a
file, see posix_fallocate(3)
.
fallocate()
allows the caller to directly manipulate the allocated
disk space for the file referred to by fd for the byte range starting
at offset and continuing for len bytes.
...
请注意如何将其明确列为 "nonportable, Linux-specific"。
甚至不可移植的、Linux 特定的 fallocate()
函数也绝对不能保证连续文件分配。
因为 space 的实际分配是文件系统相关操作。
XFS,例如,tries to preallocate file space所以数据存储在连续的块中。
但同样,没有任何保证。
只有 Oracle 的 HSM(Sun 的 SAM-QFS)和 IBM 的 Spectrum Scale(最初称为 GPFS)等高性能文件系统才能提供必要的控制级别,您甚至有机会获得 space 在文件中。
例如,the Oracle HSM/QFS setfa()
function:
NAME
sam_setfa - Sets attributes on a file or directory
SYNOPSIS
cc [ flag ... ] file ... -L/opt/SUNWsamfs/lib -lsam [library ... ]
#include "/opt/SUNWsamfs/include/lib.h"
int sam_setfa(const char *path, const char *ops);
DESCRIPTION
sam_setfa() sets attributes on a file or directory using a
SAM-QFS system call. path is the file on which to set the
attributes. ops is the character string of options, for
example: "ds1". Individual options are described below.
OPTIONS
A n Specifies the number of bytes to be allocated ahead of
a write to the file. The n must be an integer and must
be greater than or equal to one kilobyte and less than
4 terabytes. The n is rounded down to units of kilo-
bytes. This option is only valid for a regular file.
This option should be used when writing large files
where more sequential allocation is desired. Note,
when the file is closed the blocks are reset to the
size of the file.
...
l n Specifies the number of bytes to be preallocated to the
file. The n must be an integer. This option can only
be applied to a regular file. If an I/O event attempts
to extend a file preallocated with the L option, the
caller receives an ENXIO error. The l option allocates
using extent allocation. This means striping is not
supported and the file is allocated on 1 disk device or
1 striped group. The L and l options are mutually
exclusive. If the file has existing disk blocks, this
option is changed to the L option.
L n Specifies the number of bytes to be preallocated to the
file. The n must be an integer. This option is only
valid for a regular file. The L option allocates using
standard allocation. This means striping is supported.
This also means the file can be extended. The L and l
options are mutually exclusive.
...
即使在为文件在磁盘上的存储方式提供多种选择的高性能、复杂、专有文件系统上,也无法保证文件内的连续 space 分配。
这是高端数据库可以使用原始设备进行数据存储的原因之一——这确实是保证连续数据存储的唯一方法。
* 不,fseek()
/ftell()
不是严格符合的 C 代码。 fseek( fp, 0, SEEK_END )
是 explicitly undefined behavior on a binary stream, and ftell()
can't be used to get the number of bytes in a text file.
50 年前,您的问题是计算机科学与工程领域的热门话题。但不是今天。
几乎每个硬盘驱动器都有一个 512 字节的分配单元。 CD 的 AU 为 2KB。某些 SSD 在针对 MySQL 进行调整时具有 16KB 的 AU。
有很多不同的"filesystems"。 Windows 有(至少)FAT-32 和 NTFS。 *nix 有很多。每个 FS 都以在某件事上做得更好而自豪。但是自由空间管理与分配单元大小作斗争。还记得当磁盘变得越来越大时 DOS 的 FAT-16 带来的麻烦吗?名称中的“16”是指最多有 2^16 个块的磁盘。这迫使一个 2GB 的磁盘驱动器有一个 32KB 的分配单元!典型的系统有很多小文件,几乎浪费了一半的磁盘!
我说的是 "Allocation Units",因为这基本上是防止 OS 考虑将块分散在驱动器周围的唯一方法。
让我们从营销的角度来看你的问题。如果碎片如此重要,那么
- 新的、更好的文件系统会出现来解决这个问题——尽管不一定是以你提到的那种简单的方式。
- 操作系统已经知道这个问题,所以它们有 "trying" 的方法来分块分配。但他们总是愿意在必要时给你小件。
- MySQL 的 InnoDB(大约 2000 年)付出了很多努力在 "extents" 中分配 4MB(?),希望获得连续分配的磁盘。但是当它失败时,什么都不会崩溃。
- 软件会绕过这个问题,比如通过使用"raw drive access. But notice how that is not in the forefront of "如何优化你的数据库”?如果有的话,它被埋在"Oh, by the way"章节中。
几十年前,有一些 OS 可以让您预先分配 "contiguous" 的文件。最近没听说过。
企业系统通过使用带有电池备份写缓存的硬件 RAID 控制器解决了这个问题。不仅分散聚集对用户是隐藏的,而且由于崩溃安全缓存,写入变得 'instantaneous'。
SSD 没有任何寻道时间(与 HDD 不同),因此块是否被切碎真的无关紧要。当然,有一些代码可以处理它,但与传输、校验和、互斥锁、系统调用等时间相比,这真的是微不足道的。
我有一个经验法则:如果潜在的优化看起来不会帮助 10%,我会放弃它并继续做其他事情。我建议你继续。
Now i read somewhere that these pages don't get fragmented in disk image or memory image, so my question is, HOW?
数据库必须预先分配文件。
how do DBMS developers tell the operating system that "hey i just added a 16KB data (page) to this file, but make this page doesn't get fragmented"
那必须通过系统服务来完成。
is it because the memory image doesn't actually show how bytes are really stored on disk and its logical?
No.
or is it because these DBMSes somehow ask the O.S to make sure these chunks of 16KB bytes do not get fragmented?
同样,您必须进行连续扩展,这很可能会失败。
and how to do this in C?
您不能在标准 C 中执行此操作。但是,任何设计合理的操作系统都会提供允许您分配连续文件的服务。
所以我想了解 DBMS 实现的工作原理
举个例子:
MySQL 用自己的页面实现每个 table,页面大小为 16KB
所以每个 table 都是一个文件,并且是 16KB 的倍数,考虑到它有多大,因此需要多少页
现在我在某处读到这些页面不会在磁盘映像或内存映像[=中碎片化31=],所以我的问题是,怎么做?
DBMS 开发人员如何告诉操作系统 "hey i just added a 16KB data (page) to this file, but make this page doesn't get fragmented"
是不是因为内存映像实际上没有显示字节在磁盘上的实际存储方式及其逻辑?
或者是因为这些 DBMS 以某种方式要求 O.S 确保这些 16KB 字节的块不会碎片化?
以及如何在 C 中执行此操作?
how to do this in C:
int add16k(void *My16kDataChunk) {
fd = open(“My.DataBase”, O_WRONLY|O_APPEND);
if (fd != -1) {
write(fd, My16kDataChunk, 16*1024);
close(fd);
}
return fd != -1;
}
但对于数据库,您可能希望缓存打开的文件描述符、能够以任意偏移量写入、确保数据被真实记录(*) 等。最重要的是,您希望确保多个请求互不干扰。相反,您需要:同步、fdatasync、pwrite。
memory image doesn’t show ... It is because the memory image is identical to what is on disk. Two subsystems, the VM and FileSystem co-operate to achieve this. If I write 16k, and the file system has to collect 4 discontiguous 4k sectors to store it, it arranges my read() and write() calls to be oblivious to this layout -- it delivers the data to and from virtually contiguous areas. Similarly, if a 16k buffer requires 4 discontiguous 4k physical memory pages, the VM system arranges the page mappings to construct a contiguous virtual range.
就是说,一些文件系统支持预先分配磁盘的物理连续区域的机制[虽然卷管理、SAN、虚拟化这主要是假装的],这样文件系统就可以实现性能目标和开销便携性。
(*) - 这似乎是一个相当简单的想法,我可以调用 fsync()、fdatasync()、sync() 等。是的,不。 http://blog.httrack.com/blog/2013/11/15/everything-you-always-wanted-to-know-about-fsync/ 对此进行了很好的处理。 TL;DR - OS/FileSystem 人们对真理的松散观念会让编译器供应商脸红。
在严格符合的 C 代码中,你不能。标准 C 对文件是什么有一个非常模糊的概念。除了以二进制模式打开文件并逐字节读取并计数之外,甚至无法使用严格符合的 C 来完成文件的大小。*根本没有办法指定文件的大小存储在符合标准的 C 代码中。
因此您只能使用依赖于系统的方法。
POSIX 提供 the posix_fallocate()
function:
SYNOPSIS
#include <fcntl.h> int posix_fallocate(int fd, off_t offset, off_t len); [Option End]
DESCRIPTION
The
posix_fallocate()
function shall ensure that any required storage for regular file data starting at offset and continuing for len bytes is allocated on the file system storage media. Ifposix_fallocate()
returns successfully, subsequent writes to the specified file data shall not fail due to the lack of free space on the file system storage media.
但请再次注意,无法确保底层系统如何确保后续写入文件成功。
如果文件 space 是连续的,它仍然是 "implementation-defined"。保留的 space 更有可能被文件系统分配为连续的块,但绝对不能保证。
Linux 提供 the fallocate()
function:
SYNOPSIS
#define _GNU_SOURCE /* See feature_test_macros(7) */ #include <fcntl.h> int fallocate(int fd, int mode, off_t offset, off_t len);
DESCRIPTION
This is a nonportable, Linux-specific system call. For the portable, POSIX.1-specified method of ensuring that space is allocated for a file, see
posix_fallocate(3)
.
fallocate()
allows the caller to directly manipulate the allocated disk space for the file referred to by fd for the byte range starting at offset and continuing for len bytes....
请注意如何将其明确列为 "nonportable, Linux-specific"。
甚至不可移植的、Linux 特定的 fallocate()
函数也绝对不能保证连续文件分配。
因为 space 的实际分配是文件系统相关操作。
XFS,例如,tries to preallocate file space所以数据存储在连续的块中。
但同样,没有任何保证。
只有 Oracle 的 HSM(Sun 的 SAM-QFS)和 IBM 的 Spectrum Scale(最初称为 GPFS)等高性能文件系统才能提供必要的控制级别,您甚至有机会获得 space 在文件中。
例如,the Oracle HSM/QFS setfa()
function:
NAME
sam_setfa - Sets attributes on a file or directory
SYNOPSIS
cc [ flag ... ] file ... -L/opt/SUNWsamfs/lib -lsam [library ... ] #include "/opt/SUNWsamfs/include/lib.h" int sam_setfa(const char *path, const char *ops);
DESCRIPTION
sam_setfa() sets attributes on a file or directory using a SAM-QFS system call. path is the file on which to set the attributes. ops is the character string of options, for example: "ds1". Individual options are described below.
OPTIONS
A n Specifies the number of bytes to be allocated ahead of a write to the file. The n must be an integer and must be greater than or equal to one kilobyte and less than 4 terabytes. The n is rounded down to units of kilo- bytes. This option is only valid for a regular file. This option should be used when writing large files where more sequential allocation is desired. Note, when the file is closed the blocks are reset to the size of the file. ... l n Specifies the number of bytes to be preallocated to the file. The n must be an integer. This option can only be applied to a regular file. If an I/O event attempts to extend a file preallocated with the L option, the caller receives an ENXIO error. The l option allocates using extent allocation. This means striping is not supported and the file is allocated on 1 disk device or 1 striped group. The L and l options are mutually exclusive. If the file has existing disk blocks, this option is changed to the L option. L n Specifies the number of bytes to be preallocated to the file. The n must be an integer. This option is only valid for a regular file. The L option allocates using standard allocation. This means striping is supported. This also means the file can be extended. The L and l options are mutually exclusive. ...
即使在为文件在磁盘上的存储方式提供多种选择的高性能、复杂、专有文件系统上,也无法保证文件内的连续 space 分配。
这是高端数据库可以使用原始设备进行数据存储的原因之一——这确实是保证连续数据存储的唯一方法。
* 不,fseek()
/ftell()
不是严格符合的 C 代码。 fseek( fp, 0, SEEK_END )
是 explicitly undefined behavior on a binary stream, and ftell()
can't be used to get the number of bytes in a text file.
50 年前,您的问题是计算机科学与工程领域的热门话题。但不是今天。
几乎每个硬盘驱动器都有一个 512 字节的分配单元。 CD 的 AU 为 2KB。某些 SSD 在针对 MySQL 进行调整时具有 16KB 的 AU。
有很多不同的"filesystems"。 Windows 有(至少)FAT-32 和 NTFS。 *nix 有很多。每个 FS 都以在某件事上做得更好而自豪。但是自由空间管理与分配单元大小作斗争。还记得当磁盘变得越来越大时 DOS 的 FAT-16 带来的麻烦吗?名称中的“16”是指最多有 2^16 个块的磁盘。这迫使一个 2GB 的磁盘驱动器有一个 32KB 的分配单元!典型的系统有很多小文件,几乎浪费了一半的磁盘!
我说的是 "Allocation Units",因为这基本上是防止 OS 考虑将块分散在驱动器周围的唯一方法。
让我们从营销的角度来看你的问题。如果碎片如此重要,那么
- 新的、更好的文件系统会出现来解决这个问题——尽管不一定是以你提到的那种简单的方式。
- 操作系统已经知道这个问题,所以它们有 "trying" 的方法来分块分配。但他们总是愿意在必要时给你小件。
- MySQL 的 InnoDB(大约 2000 年)付出了很多努力在 "extents" 中分配 4MB(?),希望获得连续分配的磁盘。但是当它失败时,什么都不会崩溃。
- 软件会绕过这个问题,比如通过使用"raw drive access. But notice how that is not in the forefront of "如何优化你的数据库”?如果有的话,它被埋在"Oh, by the way"章节中。
几十年前,有一些 OS 可以让您预先分配 "contiguous" 的文件。最近没听说过。
企业系统通过使用带有电池备份写缓存的硬件 RAID 控制器解决了这个问题。不仅分散聚集对用户是隐藏的,而且由于崩溃安全缓存,写入变得 'instantaneous'。
SSD 没有任何寻道时间(与 HDD 不同),因此块是否被切碎真的无关紧要。当然,有一些代码可以处理它,但与传输、校验和、互斥锁、系统调用等时间相比,这真的是微不足道的。
我有一个经验法则:如果潜在的优化看起来不会帮助 10%,我会放弃它并继续做其他事情。我建议你继续。
Now i read somewhere that these pages don't get fragmented in disk image or memory image, so my question is, HOW?
数据库必须预先分配文件。
how do DBMS developers tell the operating system that "hey i just added a 16KB data (page) to this file, but make this page doesn't get fragmented"
那必须通过系统服务来完成。
is it because the memory image doesn't actually show how bytes are really stored on disk and its logical? No.
or is it because these DBMSes somehow ask the O.S to make sure these chunks of 16KB bytes do not get fragmented?
同样,您必须进行连续扩展,这很可能会失败。
and how to do this in C?
您不能在标准 C 中执行此操作。但是,任何设计合理的操作系统都会提供允许您分配连续文件的服务。