网络上的 SQLite,
注意事项和注意事项

介绍

SQLite 库的用户,尤其是应用程序开发人员,如果想从通过网络连接的不同系统访问 SQLite 数据库,通常会试图通过指定引用网络文件系统中某处数据库文件的文件名来简单地打开数据库连接。(此处为“远程数据库”)然后通过 OS API 访问此“文件”,该 API 允许从/到本地文件的 I/O 错觉。这种错觉是好的,但在重要方面并不完美。

这种简单的“远程数据库”方法通常不是从多个系统使用单个 SQLite 数据库的最佳方法(即使它看起来“有效”),因为它经常导致各种麻烦和悲伤。由于这些问题在某些使用中是不可避免的,但并不频繁或可重复,因此应用程序开发人员不应依赖早期测试的成功来决定他们的远程数据库使用是否会按预期工作。

远程数据库文件出现的问题

此图显示了组件及其链接,供以下讨论参考:

Client Application SQLite Database Engine Database File(s) SQLite API Calls DB Engine File I/O

问题来自上述三个块之间的两个数据/控制通道的属性和使用。

渠道流量

“API 调用”通道携带的信息少于“文件 I/O”通道。提交查询或指定数据修改的 API 调用通常需要来回传递的位比传输到数据库文件或从数据库文件传输以存储或查找数据的位要少得多。查询结果检索通常需要比 API 流量更多的文件流量,因为如果不读取未请求的数据,很少能找到要返回的数据。

信道带宽

API 调用通道以处理器主内存速度(千兆字/秒)运行,数据通常通过引用传递(因此不被复制)。相比之下,即使是最快的文件 I/O 通道也更慢。它们需要复制数据,通常是通过需要位序列化的介质。对于旋转磁介质,传输等待盘片旋转和磁头移动,然后受旋转速度限制。

当文件 I/O 通道包括网络连接时(除了在其远端的一些真正的文件 I/O 之外),会造成额外的缓慢。即使在原始传输速率不限制带宽的情况下,流量仍必须在两端进行打包和缓冲。额外的 I/O 处理程序层增加了调度延迟。然而,传输速度变慢是网络文件系统中最不重要的问题。

渠道可靠性

“API 调用”渠道是高度可靠的,错误率可以忽略不计,可以忽略不计。只有在系统断电时通道才会失效(陨石等除外)

“文件 I/O”通道,当它直接到达本地存储设备时,也是高度可靠的。(旋转存储 MTBF 超过 100 万小时,NVRAM 持续时间更长。)本地设备还具有一个特性,该特性对于使数据库管理软件能够设计为确保ACID行为至关重要:当对设备的所有进程写入都已完成时,(当 POSIX fsync() 或 Windows FlushFileBuffers() 调用 return),然后文件系统已经存储了“写入的”数据,或者将在存储任何后续写入的数据之前存储。

当网络文件系统设备和软件层介于文件系统客户端和实际存储设备上的文件系统之间时,会引入重要的故障源和不当行为。虽然网络数据传输可以很好地进行错误检查,但传输数据包在发送后并非都可靠地到达目的地。一些数据包被其他数据包破坏,必须重新发送。在数据包破坏条件下,重复重试可能会造成超过类似数据到达本地存储所需的延迟。客户端写入的某些部分最终可能会以相对于其他写入部分的时间顺序乱序存储。

由于在网络文件系统写入中会发生无序和彻底的数据丢失,因此在后续的文件写入集开始之前准确地知道文件写入集已经完成是至关重要的。这种保证是通过使用稳健设计和正确实现的 fsync()(或等效的)操作系统函数获得的。不幸的是,对于某些应用程序,网络文件系统同步操作可能不如本地文件系统同步稳健。在网络数据包传输错误的情况下实现稳健的同步是很困难的,为了提高性能有时会放松安全措施。

网络文件系统中的文件锁定也会产生类似的危险。SQLite 依赖于写操作的独占锁,并且已知那些对于某些网络文件系统的操作不正确。这导致了数据库损坏。随着此类设计者更改其实现以适应更常见的用例,这种情况可能会再次发生。

底线是网络文件系统同步和锁定可靠性因实现和安装而异。它所依赖的设计假设在测试应用程序的地方可能比它所依赖的地方更真实。 依赖它需要您(和您的客户)承担风险。 请参阅如何破坏您的数据库文件

性能和可靠性问题

从上面的图表和讨论中可以明显看出,将网络链接插入两个通道之一会降低性能(也称为“速度”)。考虑 API 调用通道和文件 I/O 通道之间的相对流量表明,此类插入对 API 调用通道的性能影响较小。

可靠性影响的考虑更容易,结果更清晰:将网络链接插入 API 调用通道有时也会导致调用失败。但是,如果客户端应用程序懒得正确使用 SQL/SQLite 事务,则此类故障只会导致事务失败并回滚,而不会损害数据的完整性。相反,如果将网络链接插入文件 I/O 通道,事务可能会失败(对于 API 调用插入),但会造成远程数据库损坏的额外影响。

通过在回滚模式下使用 SQLite,可以完全或在可接受的程度上缓解这些网络不可靠性问题。但是,SQLite 库没有在跨网络场景中进行测试,这也不合理。因此,使用远程数据库的风险由用户承担

建议

通常,如果您的数据通过网络与应用程序分开,您会希望使用客户端/服务器数据库。这是因为数据库引擎充当数据库流量的带宽减少过滤器。

如果您的数据通过网络与应用程序分开,您希望低流量链接跨网络,而不是高流量链接。这意味着数据库引擎需要与数据库本身位于同一台机器上。像 PostgreSQL 这样的客户端/服务器数据库就是这种情况。SQLite 的不同之处在于,数据库引擎与应用程序运行在同一台机器上,这就迫使更高流量的链接在远程数据库场景中遍历网络。这通常会导致性能下降。

网络文件系统不支持在保持数据库一致性的同时进行同步读写的能力。因此,如果您在多台不同机器上有多个客户端需要同时进行数据库读写,您有以下选择:

1. 使用客户端/服务器数据库引擎。 PostgreSQL 是一个很好的选择。一个变体是:

2. 以WAL 模式 托管一个 SQLite 数据库,但在存储数据库文件的同一台机器上的进程中执行所有读取和写入操作。实现一个在数据库机器上运行的代理,它中继来自远程机器的读/写请求。

3.在回滚模式下 使用SQLite 这意味着您可以同时拥有多个读者或一个作家,但不能同时拥有读者和作家。

应用程序程序员应该意识到他们的应用程序用户可能会选择使用远程数据库(如果可以的话)。除非上述选择之一已经生效,或者一次使用一个独占访问,否则程序员应该考虑阻止该选择,除非可靠性不太重要。

概括

选择适合您和您的客户的技术。如果您的数据与您的应用程序位于不同的机器上,那么您应该考虑客户端/服务器数据库。SQLite 专为数据和应用程序共存于同一台机器上的情况而设计。SQLite 仍然可以在许多远程数据库情况下工作,但客户端/服务器解决方案通常在这种情况下工作得更好。