An Asynchronous I/O Module For SQLite
An Asynchronous I/O Module For SQLite
注意:
将PRAGMA同步设置为NORMAL的WAL模式可避免在事务提交期间调用fsync(),并且仅在检查点操作期间调用fsync()。使用WAL模式很大程度上避免了对这种异步I / O模块的需求。因此,这个模块不再被支持。源代码继续存在于SQLite源代码树中,但它不是任何标准构建的一部分,也不再维护。本文档保留以供历史参考。
通常,当SQLite写入数据库文件时,它会等到写操作完成后再将控制权返回给调用应用程序。由于与CPU绑定操作相比,写入文件系统通常非常缓慢,因此这可能是性能瓶颈。异步I / O后端是一种扩展,它使SQLite使用在后台运行的单独线程执行所有写入请求。虽然这不会减少整个系统资源(CPU,磁盘带宽等),但它确实允许SQLite在写入数据库时快速将控制权返回给调用者。
1.0功能
使用异步I / O,写入请求由在后台运行的单独线程处理。这意味着启动数据库写入的线程不必等待(有时很慢)的磁盘I / O发生。写作似乎很快就会发生,尽管实际上它在背景中以通常缓慢的速度发生。
异步I / O似乎提供了更好的响应能力,但价格昂贵。你失去了耐久性财产。使用SQLite的默认I / O后端,一旦写入完成,您就知道您写入的信息在磁盘上是安全的。对于异步I / O,情况并非如此。如果程序崩溃或数据库写入后但在异步写入线程完成之前发生断电,则数据库更改可能永远不会将其写入磁盘,并且数据库的下一个用户可能看不到您的更改。
你失去了异步I / O的耐久性,但你仍然保留着ACID的其他部分:原子,一致和孤立。许多应用程序相处良好,没有耐久性。
1.1它是如何工作的
异步I / O通过创建SQLite VFS对象并将其注册到sqlite3_vfs_register()来工作。当通过这个VFS打开的文件被写入(使用vfs xWrite()方法)时,数据不会直接写入磁盘,而是被放置在由后台线程处理的“写入队列”中。
当使用vfs xRead()方法读取使用异步VFS打开的文件时,将从磁盘上的文件和写入队列中读取数据,以便从vfs读取器的角度来看xWrite()似乎已经完成。
异步I / O VFS通过调用API函数sqlite3async_initialize()和sqlite3async_shutdown()来注册(和未注册)。有关详细信息,请参见下面的“编译和使用”部分。
1.2限制
为了获得有关异步IO的主要想法的经验,这个实现是故意保持简单的。未来可能会添加其他功能。
例如,正如当前实现的那样,如果写操作发生在超过后台写入器线程的I / O能力的稳定流上,未决写入操作的队列将无限制地增长。如果持续时间足够长,主机系统可能会耗尽内存。一个更复杂的模块可以跟踪等待写入的数量,并在等待写入队列变得太大时停止接受新的写入请求。
1.3锁定和并发
使用此异步IO实现的单个进程中的多个连接可以同时访问单个数据库文件。从用户的角度来看,如果所有连接都来自单个进程,那么“正常”SQLite和使用异步后端的SQLite提供的并发性没有区别。
如果启用了文件锁定(默认启用),则来自多个进程的连接也可以读取和写入数据库文件。但并发性降低如下:
- 当使用异步IO的连接开始数据库事务时,数据库立即被锁定。但是,直到写入队列中的所有相关操作都被刷新到磁盘后,锁才会被释放。这意味着(例如)在发出“COMMIT”或“ROLLBACK”后数据库可能会保持锁定一段时间。
- 如果使用异步IO的应用程序快速连续执行事务,则其他数据库用户可能会被有效地锁定在数据库之外。这是因为在执行BEGIN时,立即建立数据库锁。但是,当相应的COMMIT或ROLLBACK发生时,只有在写入队列的相关部分被刷新后才会释放锁。因此,如果在刷新写入队列之前COMMIT之后是BEGIN,那么数据库永远不会解锁,从而阻止其他进程访问数据库。
可以在运行时使用sqlite3async_control()API禁用文件锁定(请参见下文)。这可以提高NFS性能或其他网络文件系统的性能,例如避免建立文件锁定所需的服务器同步往返。但是,如果禁用文件锁定时多个连接尝试访问同一个数据库文件,则应用程序崩溃和数据库损坏可能是结果。
2.0编译和使用
异步IO扩展由位于SQLite源代码树的子文件夹中的单个C代码文件(sqlite3async.c)和头文件(sqlite3async.h)组成,该文件定义应用程序用于激活和控制的C API模块功能。 ext/async/
要使用异步IO扩展,请将sqlite3async.c编译为使用SQLite的应用程序的一部分。然后使用sqlite3async.h中定义的API来初始化和配置模块。
在sqlite3async.h中的注释中详细描述了异步IO VFS API。使用API通常包含以下步骤:
- 通过调用sqlite3async_initialize()函数向SQLite注册异步IO VFS。
- 创建后台线程来执行写入操作并调用sqlite3async_run()。
- 使用普通的SQLite API通过异步IO VFS读写数据库。
有关详细信息,请参阅sqlite3async.h头文件中的注释。
3.0 PORTING
目前,异步IO扩展与支持pthreads接口的win32系统和系统兼容,包括Mac OS X,Linux和其他各种Unix。
要将异步IO扩展移植到另一个平台,用户必须为新平台实现互斥和条件变量原语。目前没有外部可用的接口来允许这一点,但修改sqlite3async.c中的代码以包含新平台的并发原语相对容易。有关详细信息,请在sqlite3async.c中搜索注释字符串“PORTING FUNCTIONS”。然后实施以下每个版本的新版本:
static void async_mutex_enter(int eMutex
static void async_mutex_leave(int eMutex
static void async_cond_wait(int eCond, int eMutex
static void async_cond_signal(int eCond
static void async_sched_yield(void
上述每个函数所需的功能在sqlite3async.c的注释中进行了描述。
SQLite在公共领域。