返回资讯中心
外部精选
软件工程
#开发者实时流

SQLite读写小数据块比文件系统快35%

来源标题:35% Faster Than the Filesystem

SQLite读写小数据块(如缩略图)比直接读写单个文件快约35%。原因是使用数据库时open()和close()系统调用只执行一次,而每个文件都需要调用;同时单个SQLite数据库存储10KB数据块比独立文件节省约20%磁盘空间。测试基于2017年6月发布的SQLite 3.19.2至3.20.0之间版本。

Hacker News 最新prakashqwerty2026/11/3 · 昨天30 分钟阅读

以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。

SQLite 读写小 blob(例如缩略图)的速度,比用 fread() 或 fwrite() 从磁盘上的单个文件读写同样的 blob 快 35%¹。此外,用一个 SQLite 数据库存放 10KB 的 blob,比把 blob 存放在单个文件中节省约 20% 的磁盘空间。我们认为,性能差异的产生是因为:使用 SQLite 数据库时,open() 和 close() 系统调用只被调用一次;而使用存放在单个文件中的 blob 时,每个 blob 都要调用一次 open() 和 close()。看起来,调用 open() 和 close() 的开销比使用数据库的开销更大。空间的节省则源于:单个文件会被填充到文件系统块大小的下一个倍数,而 blob 则更紧凑地打包进 SQLite 数据库。

本文中的测量是在 2017-06-05 那一周使用 3.19.2 到 3.20.0 之间的某个 SQLite 版本进行的。可以预期,未来版本的 SQLite 表现会更好。

¹ 上面 35% 这个数字是近似值。实际耗时因硬件、操作系统和实验细节而异,也会受到真实硬件上随机性能波动的影响。详见下文。

你可以自己尝试这些实验。如有显著偏差,请在 SQLite 论坛上报告。35% 这个数字是基于在作者手边每台机器上运行测试得出的。本文的一些审阅者报告说,在他们的系统上 SQLite 的延迟比直接 I/O 更高。我们目前还不理解这种差异。我们还看到一些迹象表明,在使用冷文件系统缓存运行实验时,SQLite 的表现不如直接 I/O。所以,你可以这样理解:SQLite 的读/写延迟与磁盘上单个文件的读/写延迟具有竞争力。通常 SQLite 更快。有时 SQLite 几乎一样快。无论哪种情况,本文都推翻了“关系数据库一定比直接文件系统 I/O 慢”这一常见假设。

2022 年的一项研究(GitHub 上的备用链接)发现,在真实工作负载下,SQLite 在 Linux 上的速度大约是 Btrfs 和 Ext4 的两倍。

Jim Gray 等人研究了 Microsoft SQL Server 中 BLOB 与文件 I/O 的读取性能,发现当 BLOB 大小小于 250KiB 到 1MiB 之间时,从数据库读取 BLOB 更快。(论文)。在那项研究中,即使内容保存在单独的文件中,数据库仍然存储内容的文件名。因此,每个 BLOB 都要查询数据库,哪怕只是为了提取文件名。而在本文中,BLOB 的键就是文件名,所以不需要预先访问数据库。由于本文在从单个文件读取内容时完全不使用数据库,因此直接文件 I/O 变得更快的那条阈值比 Gray 论文中的更小。

本网站上的《内部 BLOB 与外部 BLOB》一文是一项更早的研究(大约 2011 年),它采用了与 Jim Gray 论文相同的方法——将 blob 文件名作为条目存储在数据库中——但针对的是 SQLite 而不是 SQL Server。

I/O 性能使用 SQLite 源码树中的 kvtest.c 程序测量。要编译这个测试程序,首先将 kvtest.c 源文件与 SQLite 合并源文件 sqlite3.c 和 sqlite3.h 收集到同一个目录中。然后在 Unix 上运行类似下面的命令:

gcc -Os -I. -DSQLITE_DIRECT_OVERFLOW_READ
kvtest.c sqlite3.c -o kvtest -ldl -lpthread

或者在 Windows 上使用 MSVC:

cl -I. -DSQLITE_DIRECT_OVERFLOW_READ kvtest.c sqlite3.c

针对 Android 的编译说明见下文。

使用生成的 kvtest 程序,通过类似下面的命令生成一个包含 100,000 个随机不可压缩 blob 的测试数据库,每个 blob 的大小在 8,000 到 12,000 字节之间随机:

./kvtest init test1.db --count 100k --size 10k --variance 2k

如果需要,可以运行以下命令验证新数据库:

./kvtest stat test1.db

接下来,使用类似下面的命令将所有 blob 复制到某个目录下的单个文件中:

./kvtest export test1.db test1.dir

此时,你可以测量 test1.db 数据库占用的磁盘空间,以及 test1.dir 目录及其所有内容占用的空间。在标准的 Ubuntu Linux 桌面上,数据库文件的大小为 1,024,512,000 字节,而 test1.dir 目录将占用 1,228,800,000 字节的空间(根据 "du -k"),比数据库多约 20%。

上面创建的 "test1.dir" 目录将所有 blob 放在一个文件夹中。有人推测,当单个目录包含 100,000 个对象时,某些操作系统会表现不佳。为了测试这一点,kvtest 程序还可以将 blob 存储在一个文件夹层级中,每个文件夹中的文件和/或子目录不超过 100 个。可以使用 "export" 命令的 --tree 命令行选项创建 blob 的另一种磁盘表示形式,如下所示:

./kvtest export test1.db test1.tree --tree

test1.dir 目录将包含 100,000 个文件,文件名如 "000000"、"000001"、"000002" 等等,而 test1.tree 目录将把这些相同的文件放在子目录中,如 "00/00/00"、"00/00/01" 等等。test1.dir 和 test1.test 目录占用的空间大致相同,不过由于额外的目录条目,test1.test 略大一些。

接下来的所有实验在 "test1.dir" 或 "test1.tree" 上运行时结果相同。无论哪种操作系统,测得的性能差异都很小。

使用以下命令测量从数据库和从单个文件读取 blob 的性能:

./kvtest run test1.db --count 100k --blob-api ./kvtest run test1.dir --count 100k --blob-api ./kvtest run test1.tree --count 100k --blob-api

(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →