SQLite剖析之异步IO模式、共享缓存模式和解锁通知

本文详细介绍了SQLite的异步I/O模式,该模式利用后台线程处理写请求,提高响应速度但牺牲了持久性。接着讨论了共享缓存模式,允许多个连接共享数据和模式缓存,减少内存和I/O消耗。最后,解释了解锁通知机制,如何在并发访问时避免死锁,确保事务隔离。
摘要由CSDN通过智能技术生成

SQLite剖析之异步IO模式、共享缓存模式和解锁通知

1、异步I/O模式
    通常,当SQLite写一个数据库文件时,会等待,直到写操作完成,然后控制返回到调用程序。相比于CPU操作,写文件系统是非常耗时的,这是一个性能瓶颈。异步I/O后端是SQLite的一个扩展模块,允许SQLite使用一个独立的后台线程来执行所有的写请求。虽然这并不会减少整个系统的资源消耗(CPU、磁盘带宽等),但它允许SQLite在正在写数据库时立刻返回到调用者,从用户角度看,无疑提高了前端的响应速度。对异步I/O,写请求在一个独立的后台线程中被处理,这意味着启动数据库写操作的线程不必等待磁盘I/O的发生。写操作看起来似乎很快就发生了,但实际上速度跟通常是一样的,只不过在后台进行。
    异步I/O似乎提供了更好的响应能力,但这是有代价的。你会失去ACID中的持久性(Durable)属性。在SQLite的缺省I/O后端中,一旦写操作完成,你知道更改的数据已经安全地在磁盘上了。而异步I/O却不是这样的情况。如果应用程序在数据写操作之后,异步写线程完成之前发生崩溃或掉电,则数据库更改可能根本没有被写到磁盘,下一次使用数据库时就看不到更改。
    异步I/O失去了持久性,但仍然保持ACID的其他三个属性:原子性(Atomic)、一致性(Consistent)和隔离性(Isolated)。很多应用程序没有持久性也能很好地工作。
    我们通过创建一个SQLite VFS对象并且用sqlite3_vfs_register()注册它来使用异步I/O模式。当用这个VFS打开数据库文件并进行写操作时(使用vfs的xWrite()方法),数据不会立刻写到磁盘,而是放在由后台线程维护的写队列中。当用异步VFS打开数据库文件并进行读操作时(使用vfs的xRead()方法),数据从磁盘读出,而写队列从vfs读进程的角度看,其xWrite()已经完成了。异步I/O的虚拟文件系统(VFS)通过sqlite3async_initialize()来注册,通过sqlite3async_shutdown()来关闭。
    为了积累经验,异步I/O的实现有意保持简单。更多的功能会在将来的版本中添加。例如,在当前的实现中,如果写操作正在一个稳定的流上发生,而这个流超过了后台写线程的I/O能力,则挂起的写操作队列将会无限地增长,可能会耗尽主机系统的内存。复杂一点的模块则可以跟踪挂起的写操作数量,在超过一定数目后停止接收新的写请求。
    在单个进程中、使用异步IO的多个连接可以并发地访问单个数据库。从用户的角度看,如果所有连接都位于单个进程中,则正常SQLite和使用异步IO的SQLite,其并发性并没有什么不同。如果文件锁是激活的(缺省是激活的),来自多个进程的连接都要读和写数据库文件,则并发性在下面的情况下会减弱:
    (1)当使用异步IO的连接启动一个数据库事务时,数据库会立刻被锁住。然而锁只有在写队列中的所有操作已经刷新到磁盘后才能释放。这意味着有时即使在一个"COMMIT"或"ROLLBACK"执行完后,数据库可能仍然处于锁住状态。
    (2)如果应用程序使用异步IO连续地执行多个事务,其他数据库用户可能会因为数据库一直被锁住而不能使用数据库。这是因为当一个BEGIN执行后,数据库锁会立刻建立起来。但当对应的COMMIT或ROLLBACK发生时,锁不一定释放了,要到后台写队列全部刷新到磁盘后才能释放。如果后台写队列还没刷新完,数据库就一直处于锁住状态,其他进程不能访问数据库。
    文件锁可以在运行时通过sqlite3async_control()函数禁用。对NFS这可以提高性能,因为可以避免对服务器的来回异步操作建立文件锁。但是如果多个连接尝试访问同一个数据库,而文件锁被禁用了,则应用程序崩溃和数据库损坏就可能发生。
    异步IO扩展模块由单个源文件sqlite3async.c,和一个头文件sqlite3async.h组成,位于源码树的ext/async/子目录下。应用程序可以用其中定义的C API来激活和控制这个模块的功能。为了使用异步IO扩展,把sqlite3async.c编译成使用SQLite的应用程序的一部分,然后使用sqlite3async.h中定义的API来初始化和配置这个模块。这些API在sqlite3async.h的注释中有详细说明,使用这些API通常有以下步骤:
    (1)调用sqlite3async_initialize()来给SQLite注册异步IO VFS(虚拟文件系统)。
    (2)创建一个后台线程来执行写操作,并调用sqlite3async_run()。
    (3)通过异步IO VFS,使用正常的SQLite API来读写数据库。
    当前的异步IO扩展兼容win32系统和支持pthread接口的系统,包括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的注释中有详细描述。


2、共享缓存模式
    从3.3.0版开始,SQLite包含一个特别的“共享缓存”模式(缺省情况下禁用),主要用在嵌入式服务器中。如果共享缓存模式激活,并且一个线程在同一个数据库上建立多个连接,则这些连接共享一个数据和模式缓存。这能够显著减少系统的内存和IO消耗。在3.5.0版中,共享缓存模式被修改以便同一缓存的共享可以跨越整个进程而不只是

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值