从 版本 3.3.0 (2006-01-11) 开始,SQLite 包含一种特殊的“共享缓存”模式(默认情况下禁用),旨在用于嵌入式服务器。如果启用了共享缓存模式并且线程建立了到同一数据库的多个连接,则这些连接将共享单个数据和模式缓存。这可以显着减少系统所需的内存和 IO 量。
在 版本 3.5.0 (2007-09-04) 中,共享缓存模式进行了修改,以便同一缓存可以跨整个进程共享,而不仅仅是在单个线程内共享。在此更改之前,在线程之间传递数据库连接存在限制。这些限制在 3.5.0 更新中已取消。本文档描述了 3.5.0 版本中的共享缓存模式。
共享缓存模式在某些情况下会更改锁定模型的语义。本文档描述了详细信息。假设您已经了解了正常的 SQLite 锁定模型的基本知识(有关详细信息,请参阅 SQLite 版本 3 中的文件锁定和并发性)。
共享缓存模式是一个过时的功能。不鼓励使用共享缓存模式。大多数共享缓存用例可以通过 WAL 模式 更好地解决。
共享缓存模式是在 2006 年应 Symbian 开发人员的要求而发明的。他们的问题是,如果手机上的联系人数据库正在同步,则会锁定数据库文件。然后,如果有来电,数据库锁定将阻止他们查询联系人数据库以查找用于来电的适当铃声或要在屏幕上显示的呼叫者照片等等。 WAL 模式(约 2010 年)是解决此问题的更好方法,因为它允许在不破坏事务隔离的情况下进行同步访问。
鼓励从源代码构建自己版本的 SQLite 的应用程序使用 -DSQLITE_OMIT_SHARED_CACHE 编译时选项,因为生成的二进制文件将更小且更快。
本文档中描述的共享缓存接口将继续在 SQLite 中得到支持,以确保完全向后兼容性。但是,不鼓励使用共享缓存。
从另一个进程或线程的角度来看,从外部来看,两个或多个使用共享缓存的 数据库连接 就像单个连接一样。用于在多个共享缓存或常规数据库用户之间进行仲裁的锁定协议在其他地方有描述。
图 1
图 1 描述了示例运行时配置,其中已建立三个数据库连接。连接 1 是正常的 SQLite 数据库连接。连接 2 和 3 共享缓存。正常的锁定协议用于在连接 1 和共享缓存之间序列化数据库访问。用于序列化(或不序列化,请参阅下面的“未提交读隔离模式”)连接 2 和 3 访问共享缓存的内部协议将在本节的其余部分中描述。
共享缓存锁定模型有三个级别:事务级锁定、表级锁定和模式级锁定。它们将在以下三个小节中描述。
SQLite 连接可以打开两种类型的交易:读交易和写交易。这不是显式完成的,交易隐式地是读交易,直到它第一次写入数据库表,此时它变为写交易。
在任何时候,最多只有一个连接到单个共享缓存可以打开写交易。这可以与任何数量的读交易共存。
当两个或多个连接使用共享缓存时,锁定用于在每个表的基础上序列化并发访问尝试。表支持两种类型的锁定:“读锁定”和“写锁定”。锁定被授予连接 - 在任何时候,每个数据库连接在每个数据库表上都有一个读锁定、写锁定或没有锁定。
在任何时候,单个表可以具有任何数量的活动读锁定或单个活动写锁定。要从表中读取数据,连接必须首先获得读锁定。要写入表,连接必须获得该表的写锁定。如果无法获得所需的表锁定,则查询将失败,并向调用方返回 SQLITE_LOCKED。
一旦连接获得表锁定,它就不会在当前交易(读或写)结束之前释放。
上述行为可以通过使用 read_uncommitted pragma 将隔离级别从序列化(默认值)更改为未提交读来稍微修改。
处于未提交读模式的数据库连接不会在从数据库表读取之前尝试获得读锁定,如上所述。如果另一个数据库连接在读取期间修改了表,这会导致查询结果不一致,但这同时也意味着处于未提交读模式的连接打开的读交易既不会阻塞也不会被任何其他连接阻塞。
未提交读模式对写入数据库表所需的锁定没有影响(即未提交读连接仍然必须获得写锁定,因此数据库写入仍然可以阻塞或被阻塞)。此外,未提交读模式不会影响以下规则枚举的 sqlite_schema 锁定(请参阅“模式 (sqlite_schema) 级锁定”部分)。
/* Set the value of the read-uncommitted flag: ** ** True -> Set the connection to read-uncommitted mode. ** False -> Set the connection to serialized (the default) mode. */ PRAGMA read_uncommitted = <boolean>; /* Retrieve the current value of the read-uncommitted flag */ PRAGMA read_uncommitted;
sqlite_schema 表 与所有其他数据库表一样支持共享缓存读锁定和写锁定(请参阅上面的描述)。还适用以下特殊规则
在 SQLite 版本 3.3.0 到 3.4.2 中,当启用了共享缓存模式时,数据库连接只能由调用 sqlite3_open() 创建它的线程使用。连接只能与同一线程中的另一个连接共享缓存。这些限制从 SQLite 版本 3.5.0 (2007-09-04) 开始取消。
在旧版本的 SQLite 中,共享缓存模式不能与虚拟表一起使用。此限制已在 SQLite 版本 3.6.17 (2009-08-10) 中删除。
共享缓存模式在每个进程的基础上启用。使用 C 接口,可以使用以下 API 来全局启用或禁用共享缓存模式
int sqlite3_enable_shared_cache(int);
每次调用 sqlite3_enable_shared_cache() 都会影响随后使用 sqlite3_open()、sqlite3_open16() 或 sqlite3_open_v2() 创建的数据库连接。已存在的数据库连接不受影响。每次调用 sqlite3_enable_shared_cache() 都会覆盖同一进程中所有先前的调用。
使用 sqlite3_open_v2() 创建的单个数据库连接可以通过使用第三个参数中的 SQLITE_OPEN_SHAREDCACHE 或 SQLITE_OPEN_PRIVATECACHE 标志来选择是否参与共享缓存模式。使用其中任何一个标志都会覆盖由 sqlite3_enable_shared_cache() 建立的全局共享缓存模式设置。不应使用多个标志;如果在 sqlite3_open_v2() 的第三个参数中使用 SQLITE_OPEN_SHAREDCACHE 和 SQLITE_OPEN_PRIVATECACHE 标志,则行为未定义。
当使用 URI 文件名 时,可以使用“cache”查询参数来指定数据库是否使用共享缓存。使用“cache=shared”启用共享缓存,使用“cache=private”禁用共享缓存。使用 URI 查询参数来指定数据库连接的缓存共享行为的能力允许在 ATTACH 语句中控制缓存共享。例如
ATTACH 'file:aux.db?cache=shared' AS aux;
从 SQLite 版本 3.7.13 (2012-06-11) 开始,可以使用 URI 文件名 创建的 内存数据库 共享缓存,前提是数据库是使用 URI 文件名 创建的。为了向后兼容性,如果使用未修饰的名称“:memory:”打开数据库,则始终禁用内存数据库的共享缓存。在 3.7.13 版本之前,始终禁用内存数据库的共享缓存,无论使用的数据库名称、当前系统共享缓存设置或查询参数或标志是什么。
为内存数据库启用共享缓存允许同一进程中的两个或多个数据库连接访问相同的内存数据库。当与该数据库的最后一个连接关闭时,处于共享缓存中的内存数据库将自动删除,并且内存将被回收。
此页面最后修改于 2023-01-02 14:22:42 UTC