Small. Fast. Reliable.
Choose any three.

SQLite作为应用程序文件格式

执行摘要

具有定义模式的SQLite数据库文件通常是一种出色的应用程序文件格式。这样做的原因有十几个:

  1. 简化应用程序开发
  2. 单文件文件
  3. 高级查询语言
  4. 无障碍内容
  5. 跨平台
  6. 原子交易
  7. 增量和持续更新
  8. 容易扩展
  9. 表现
  10. 多个进程同时使用
  11. 多种编程语言
  12. 更好的应用

在首先更仔细地考虑“应用程序文件格式”的含义之后,下面将对这些要点中的每一个进行更详细的描述。另请参见本白皮书的简短版本

什么是应用程序文件格式?

“应用程序文件格式”是用于将应用程序状态保存到磁盘或在程序之间交换信息的文件格式。今天有成千上万种应用程序文件格式在使用。这里只是几个例子:

我们区分“文件格式”和“应用程序格式”。文件格式用于存储单个对象。因此,例如,GIF或JPEG文件存储单个图像,而XHTML文件存储文本,因此它们是“文件格式”而不是“应用程序格式”。相反,EPUB文件同时存储文本和图像(包含在XHTML和GIF / JPEG文件中),因此被视为“应用程序格式”。本文是关于“应用程序格式”的。

文件格式和应用程序格式之间的界限是模糊的。本文将JPEG称为文件格式,但对于图像编辑器,JPEG可能被视为应用程序格式。在很大程度上取决于上下文。对于本文,我们假设一种文件格式存储一个对象,而一种应用程序格式存储许多不同的对象及其相互之间的关系。

大多数应用程序格式都属于以下三类之一:

  1. 完全自定义格式。 自定义格式是专门为单个应用程序设计的。DOC,DWG,PDF,XLS和PPT是自定义格式的示例。自定义格式通常包含在单个文件中,以便于传输。它们通常也是二进制的,尽管DWG格式是一个明显的例外。自定义文件格式需要专门的应用程序代码来读取和写入,并且通常无法从常用工具(例如UNIX命令行程序和文本编辑器)访问。换句话说,自定义格式通常是“不透明斑点”。要访问自定义应用程序文件格式的内容,需要一种专门设计用于读取和/或写入该格式的工具。

  2. 一堆文件格式。 有时,应用程序状态存储为文件层次结构。Git是一个很好的例子,尽管这种现象在一次性和定制的应用程序中经常发生。文件堆格式本质上将文件系统用作键/值数据库,将一小部分信息存储到单独的文件中。这具有使内容更易于普通实用程序(例如文本编辑器或“ awk”或“ grep”)访问的优点。但是,即使许多文件格式的文件都易于阅读,但通常还是有些文件具有自己的自定义格式(例如,Git“ Packfiles”),因此是不可读或不透明的“不透明斑点”无需专门工具即可写。将一堆文件从一个地方或机器移动到另一个地方也不太方便,而不是移动单个文件。例如,很难将一堆文件变成电子邮件附件。最后,一堆文件格式打破了“文档隐喻”:用户可以指向的文件就是“文档”。

  3. 包装文件堆格式。 某些应用程序使用文件堆,然后将文件堆封装到某种单文件容器中,通常是ZIP存档。EPUB,ODT和ODP是这种方法的示例。EPUB图书实际上只是一个ZIP存档,其中包含书籍章节文本的各种XHTML文件,艺术品的GIF和JPEG图像,以及专门的目录文件,该文件告诉eBook阅读器所有XML和图像文件如何组合在一起。OpenOffice文档(ODT和ODP)也是ZIP归档文件,其中包含表示其内容的XML和图像以及“目录”文件,这些文件显示了组成部分之间的相互关系。

    包装的文件堆格式是完全自定义文件格式和纯文件堆格式之间的折衷。打包的文件堆格式与自定义格式在意义上不是透明的blob,因为仍然可以使用任何常见的ZIP归档程序来访问组成部分,但是格式不像纯堆文件那样易于访问。 -files格式,因为仍然需要ZIP存档程序,并且通常不能在不首先将其解压缩之前就在文件层次结构上使用“ find”之类的命令行工具。另一方面,包装的文件堆格式确实通过将所有内容放入单个磁盘文件中来保留文档隐喻。而且由于它是压缩的,所以打包的文件堆格式趋向于更紧凑。

    与自定义文件格式一样,不同于纯文件堆格式,打包的文件堆格式也不那么容易编辑,因为通常必须重写整个文件才能更改任何组成部分。

本文档的目的是主张使用第四类新的应用程序文件格式:SQLite数据库文件。

SQLite作为应用程序文件格式

可以记录在文件堆中的任何应用程序状态也可以使用简单的键/值模式记录在SQLite数据库中,如下所示:

创建表文件(文件名TEXT PRIMARY KEY,内容BLOB);
如果压缩了内容,则此类SQLite存档数据库 的大小与等效的ZIP存档大小相同(±1%),并且具有无需更新整个文档即可更新单个“文件”的优点。

但是,SQLite数据库不限于简单的键/值结构,例如文件堆数据库。一个SQLite数据库可以有数十个或数百个或数千个不同的表,每个表具有数十个或数百个或数千个字段,每个字段具有不同的数据类型和约束以及特定的含义,它们相互交叉引用,并被适当地自动索引以进行快速检索,并将所有文件高效而紧凑地存储在单个磁盘文件中。而且所有这些结构都通过SQL模式为人类简洁地记录了下来。

换句话说,SQLite数据库可以完成文件堆或打包文件堆格式可以完成的所有工作,而且功能更多,而且更加清晰。与键/值文件系统或ZIP存档相比,SQLite数据库是一种用途更广泛的容器。(有关详细示例,请参阅 OpenOffice案例研究文章。)

从理论上讲,SQLite数据库的功能可以使用自定义文件格式来实现。但是,任何像关系数据库一样具有表现力的自定义文件格式,都可能需要庞大的设计规范和成千上万的代码行来实现。最终结果将是一个“不透明的斑点”,如果没有专门的工具将无法访问。

因此,与其他方法相比,使用SQLite数据库作为应用程序文件格式具有令人信服的优势。以下列举并阐述了其中一些优点:

  1. 简化的应用程序开发。 读取或写入应用程序文件不需要任何新代码。只需链接到SQLite库,或将 单个“ sqlite3.c”源文件包含在其余应用程序C代码中,SQLite将负责所有应用程序文件I / O。这样可以减少数千行的应用程序代码大小,并相应节省开发和维护成本。

    SQLite是世界上 最常用的软件库之一。每天,在智能手机和小工具以及桌面应用程序中,每天都有数百亿个SQLite数据库文件正在使用。SQLite经过仔细测试并证明是可靠的。它不是需要大量调整或调试的组件,它使开发人员可以专注于应用程序逻辑。

  2. 单文件文档。 SQLite数据库包含在一个文件中,可以轻松地对其进行复制,移动或附加。“文档”隐喻被保留。

    SQLite没有任何文件命名要求,因此应用程序可以使用它希望帮助将文件标识为“属于”应用程序的任何自定义文件后缀。SQLite数据库文件的标题中包含一个4字节的应用程序ID,可以将其设置为应用程序定义的值,然后用于为实用程序(例如file(1))标识文档的“类型”,从而进一步增强了文档的隐喻性。

  3. 高级查询语言。 SQLite是一个完整的关系数据库引擎,这意味着应用程序可以使用高级查询访问内容。应用程序开发人员无需花时间思考“如何”从文档中检索所需的信息。开发人员编写SQL来表达他们想要的“什么”信息,并让数据库引擎找出如何最好地检索该内容的方法。这有助于开发人员进行“精打细算”,并始终专注于解决用户的问题,并避免花费时间“精打细算”来摆弄低级文件格式详细信息。

    可以将文件堆格式视为键/值数据库。键/值数据库总比没有数据库要好。但是,如果没有事务或索引,高级查询语言或适当的架构,则使用键/值数据库比关系数据库要困难得多,而且容易出错。

  4. 无障碍内容。 可使用常用的开源命令行工具访问SQLite数据库文件中包含的信息,这些工具默认情况下已在Mac和Linux系统上安装,并且在Windows上作为独立的EXE文件免费提供。与自定义文件格式不同,不需要特定于应用程序的程序来读取或写入SQLite数据库中的内容。SQLite数据库文件不是不透明的Blob。的确,诸如文本编辑器或“ grep”或“ awk”之类的命令行工具在SQLite数据库上没有用,但是SQL查询语言是一种更强大,更方便的检查内容的方法,因此无法使用“ grep”和“ awk”之类的东西不被视为损失。

    SQLite数据库是一种定义明确且有据可查的 文件格式,实际上已被数百万个应用程序广泛使用,并且与2004年成立以来向后兼容,并且有望在未来数十年内继续兼容。SQLite数据库文件的寿命对于定制应用程序尤为重要,因为它允许在将来丢失了原始应用程序的所有痕迹很长时间之后就可以访问文档内容。数据的寿命比代码更长。美国国会图书馆建议使用SQLite数据库 作为长期保存数字内容的存储格式。

  5. 跨平台。 SQLite数据库文件可在32位和64位计算机之间,big-endian和little-endian体系结构之间以及Windows和Unix之类的各种操作系统之间移植。使用SQLite应用程序文件格式的应用程序可以存储二进制数值数据,而不必担心整数或浮点数的字节顺序。文本内容可以使用UTF-8,UTF-16LE或UTF-16BE进行读写,SQLite会即时自动执行任何必要的翻译。

  6. 原子事务。 写入SQLite数据库是原子的。它们要么完全发生,要么根本没有发生,即使在系统崩溃或电源故障期间也是如此。因此,不存在仅在将更改写入磁盘的同时突然断电的情况下损坏文档的危险。

    SQLite是事务性的,这意味着可以将多个更改组合在一起,以使全部更改或全部不发生,并且如果在提交之前发现问题,则可以回滚更改。这使应用程序可以进行增量更改,然后在将更改提交到磁盘之前对生成的数据运行各种完整性检查和一致性检查。该 化石DVCS 使用这种技术 来验证没有仓库的历史前已经向每一个变化丢失。

  7. 增量和连续更新。 写入SQLite数据库文件时,仅将文件中实际更改的那些部分写到磁盘中。这使写入过程更快,并节省了SSD的磨损。与自定义文件格式和打包文件格式相比,这是一个巨大的优势,这两种格式通常都需要重写整个文档才能更改单个字节。纯文件堆格式也可以在一定程度上进行增量更新,尽管使用文件堆格式(单个文件)的写入粒度通常比使用SQLite(单个页面)的写入粒度大。

    SQLite还支持持续更新。无需收集内存中的更改,然后仅通过“文件/保存”操作将它们写到磁盘上,而是可以在更改发生时将其写回磁盘。这样可以避免因系统崩溃或电源故障而造成的工作损失。使用触发器管理的自动撤消/重做堆栈可以保存在磁盘数据库中,这意味着撤消/重做可以跨会话边界发生。

  8. 易于扩展。 随着应用程序的增长,只需​​将新表添加到架构中或将新列添加到现有表中,即可将新功能添加到SQLite应用程序文件格式中。添加列或表不会更改先前查询的含义,因此在确保保留旧有列和表的含义的过程中要格外小心,以保持向后兼容性。

    当然,也可以扩展自定义或文件堆格式,但是这样做通常要困难得多。如果添加了索引,则必须找到并修改所有更改相应表的应用程序代码,以使这些索引保持最新。如果添加了列,则必须定位并修改访问相应表的所有应用程序代码,以考虑新的列。

  9. 表现。 在许多情况下,SQLite应用程序文件格式将比文件 堆格式或自定义格式更快。除了可以更快地进行原始读写外,SQLite还可以大大缩短启动时间,因为该应用程序不必执行查询操作即可提取仅用于初始屏幕所需的信息,而不必将整个文档读取并解析到内存中。随着应用程序的进行,它只需要加载绘制下一个屏幕所需的材料,就可以丢弃先前不再使用的屏幕中的信息。这有助于控制应用程序的内存占用量。

    可以像SQLite一样以增量方式读取文件堆格式。但是,许多开发人员惊讶地发现,SQLite可以从其数据库读取和写入较小的BLOB(大小小于100KB)的速度比将相同的Blob作为独立于文件系统的文件进行读取或写入的速度快。(有关更多信息,请参见 比文件系统内部BLOB快35%。)与操作关系数据库引擎相关的开销很大,但是,不应假定直接文件I / O比SQLite数据库I / O快,因为通常不是。

    无论哪种情况,如果在SQLite应用程序中确实出现性能问题,通常可以通过 向该模式添加一个或两个CREATE INDEX语句或运行一次ANALYZE而无需触摸任何一行应用程序代码来解决这些问题。但是,如果以自定义或文件格式出现性能问题,则此修复程序通常需要对应用程序代码进行大量更改,以添加和维护新索引或使用不同算法来提取信息。

  10. 多个进程同时使用。 SQLite自动协调从多个线程和/或进程对同一文档的并发访问。两个或多个应用程序可以同时连接和读取同一文档。写入是序列化的,但是由于写入通常只需要几毫秒,因此应用程序只是轮流进行写入。SQLite自动确保文档的低级格式没有损坏。相比之下,使用自定义或文件堆格式实现相同功能需要在应用程序中提供广泛的支持。支持并发所需的应用程序逻辑是一个臭名昭著的bug-磁铁。

  11. 多种编程语言。 尽管SQLite本身是用ANSI-C编写的,但几乎可以想到的所有其他编程语言都存在接口:C ++,C#,Objective-C,Java,Tcl,Perl,Python,Ruby,Erlang,JavaScript等。因此,程序员可以使用他们最熟悉的语言以及最符合项目需求的语言进行开发。

    如果存在单独程序的集合或“联合”,这些程序通常用不同的语言和不同的开发团队编写,则SQLite应用程序文件格式是一个很好的选择。这通常出现在研究或实验室环境中,其中一个团队负责数据采集,而其他团队负责分析的各个阶段。每个团队都可以使用他们最喜欢的任何硬件,操作系统,编程语言和开发方法,并且只要所有程序都使用具有通用模式的SQLite数据库,它们就可以互操作。

  12. 更好的应用程序。 如果应用程序文件格式是SQLite数据库,则该文件格式的完整文档将由数据库架构组成,其中可能还包含一些有关每个表和列所代表的内容的词汇。另一方面,自定义文件格式的描述通常可以运行数百页。一堆文件格式虽然比完全自定义格式更容易描述,但与SQL模式转储相比,它仍然倾向于更大,更复杂,因为仍然必须描述各个文件的名称和格式。

    这不是无关紧要的。清晰,简洁且易于理解的文件格式是任何应用程序设计的关键部分。弗雷德·布鲁克斯(Fred Brooks)在其有史以来最畅销的计算机科学课本中, 《神话人月》The Mythical Man-Month)说:

    表示法是计算机编程的本质。
    ...
    告诉我您的流程图并隐藏您的表格,我将继续感到困惑。给我看你的表,我通常不需要你的流程图。他们会很明显。

    罗伯·派克(Rob Pike)在他的 《编程规则》中以这种方式表达了同样的想法:

    数据占主导地位。如果您选择了正确的数据结构并组织得当,那么这些算法几乎总是不言而喻的。数据结构而非算法是编程的核心。

    在2006-06-27的Git邮件列表上,Linus Torvalds使用了不同的单词来表达相同的内容:

    糟糕的程序员会担心代码。好的程序员担心数据结构及其关系。

    关键是:SQL数据库模式几乎总是在定义和组织表,数据结构及其关系方面做得更好。而且,具有清晰,简洁和定义明确的表示形式几乎总是可以使应用程序性能更好,问题更少并且更易于开发和维护。

结论

SQLite并非在每种情况下都是完美的应用程序文件格式。但是在许多情况下,与自定义文件格式,文件堆或包装文件堆相比,SQLite是更好的选择。SQLite是一种高级,稳定,可靠,跨平台,广泛部署,可扩展,高性能,可访问的并发文件格式。在下一个应用程序设计中,它值得您考虑作为标准文件格式。