小。 速度很快。 可靠。 选择任意三个选项。
SQLite中的隔离
数据库的“隔离”属性决定何时更改 一个操作的数据库对其他并发操作可见。
数据库连接之间的隔离
如果使用两个不同的 数据库连接 (两个不同 方形3 返回的对象 单独呼叫 sqlite3_open() )和两个数据库连接 没有 共享缓存 ,则读者只能 查看编写器提交的完整事务。 部分更改 作者所写的未经承诺的内容对读者来说是看不见的。 无论两个数据库连接是否在 同一线程,在同一进程的不同线程中,或在 不同的流程。 这个 是SQL数据库系统的常见和预期行为。
上一段也是正确的(单独的数据库连接是 相互隔离) 共享缓存模式 只要 read_uncommitted杂注 保持关闭状态 读取未提交的杂注 默认情况下处于关闭状态,因此如果应用程序未执行任何操作, 它将保持关闭状态。因此,除非 read_uncommitted杂注 已使用 要更改默认行为,请更改一个数据库连接 对共享 相同的缓存,直到写入程序提交其事务。
如果两个数据库连接共享同一缓存,并且读取器具有 启用了 read_uncommitted杂注 ,那么读者将能够 在writer事务提交之前,查看writer所做的更改。 联合使用 共享缓存模式 和 read_uncommitted杂注 是一个数据库连接可以看到未提交更改的唯一方式 在不同的数据库连接上。 在所有其他情况下 数据库连接彼此完全隔离。
除以下情况外 共享缓存 数据库连接 PRAGMA读_未提交 打开时,SQLite中的所有事务都会显示 “可序列化”隔离。 SQLite实现可序列化事务 通过实际序列化写入。 只能有一个作家 每次发送到SQLite数据库。 可以有多个数据库连接 同时打开,所有这些数据库连接都可以写入 到数据库文件,但他们必须轮流执行。 SQLite使用锁 自动序列化写入; 这不是什么 使用SQLite的应用程序需要考虑。
隔离和并发
SQLite使用实现隔离和并发控制(以及原子性) 与数据库文件出现在同一目录中的临时日志文件。 有两种主要的“日记模式”。 旧的“回滚模式”对应于使用“DELETE”、“PERSIST”、, 或“TRUNCATE”选项 日志模式杂注 。在回滚模式下, 更改直接写入数据库文件,同时 构造了一个单独的回滚日志文件,可以恢复 如果事务回滚,数据库将恢复到其原始状态。 回滚模式(特别是DELETE模式,这意味着回滚日志 在每个事务结束时从磁盘中删除)是当前的 默认行为。
自 版本3.7.0 (2010-07-21), SQLite还支持“ WAL模式 “.在WAL模式下, 更改不会写入原始数据库文件。 相反,更改 进入单独的“提前写日志”或“WAL”文件。 稍后,在交易之后 提交时,这些更改将从WAL文件移回 名为“检查点”的操作中的原始数据库。 WAL模式为 通过运行“ PRAGMA journal_mode=WAL ".
在回滚模式下,SQLite通过锁定数据库来实现隔离 文件并防止其他数据库连接进行任何读取 而每个写事务都在进行中。 读卡器可以在写入开始时,在任何内容之前处于活动状态 刷新到磁盘,而所有更改仍保留在写入程序的 私有内存空间。 但在对数据库文件进行任何更改之前 在磁盘上,所有读卡器都必须(暂时)被逐出,才能给写卡器 以独占方式访问数据库文件。 因此,禁止读者看到不完整的内容 事务,因为在 事务正在写入磁盘。 仅在交易完成后 允许读卡器完全写入并同步到磁盘并提交 返回数据库。 因此,读者永远没有机会看到部分内容 书面变更。
WAL模式允许同时读取和写入。 它可以做到这一点,因为 更改不会覆盖原始数据库文件,而是 到单独的预写日志文件中。 这意味着读者可以继续阅读 从原始数据库文件中读取旧的、原始的、未更改的内容 同时,写入程序正在向预写日志追加内容。 在 WAL模式 ,SQLite表现出“快照隔离”。 当读取事务 开始时,该读者继续看到数据库的不变“快照” 文件,因为它在读取事务启动时存在。 读取事务执行时提交的任何写入事务 active对read事务仍然不可见,因为reader 查看前一时刻的数据库文件快照。
示例:假设有两个数据库连接X和Y.X启动 读取事务使用 开始 后跟一个或多个 选择 声明。 然后Y跑过来 更新 语句来修改数据库。 X随后可以执行 选择 与Y修改但 X将看到旧的未修改条目,因为Y的更改都是 X持有读取事务时,X不可见。 如果X想看 Y所做的更改,然后X必须结束其读取事务,并且 开始一个新的(通过运行 承诺 接着是另一个 开始 .)
另一个示例:X使用以下命令启动读取事务 开始 和 选择 ,然后 Y使用对数据库进行更改 更新 然后X尝试生成 使用更改数据库 更新 。X试图升级其 从读取事务到写入事务的事务失败 SQLITE_BUSY_SNAPSHOT数据库 错误,因为数据库的快照 X查看的不再是数据库的最新版本。 如果X是 允许写入时,它将分叉数据库文件的历史记录,这是 SQLite不支持的内容。 为了让X写入数据库, 它必须首先释放其快照(使用 回降 例如)然后 使用后续事务启动新事务 开始 .
如果X启动一个最初只读取但X知道的事务 最终会想写作而不想被打扰 由于另一个连接而产生的可能SQLITE_BUSY_SNAPSHOT错误 排在前面,然后X可以发出 立即开始 开始 它的交易而不仅仅是一个普通的开始。 这个 立即开始 命令继续执行并启动写事务,从而阻止所有 其他作家。 如果 立即开始 操作成功,则否 该事务中的后续操作将永远失败 SQLITE_BUSY数据库 错误。
同一数据库连接上的操作之间没有隔离
SQLite在单独的数据库中提供操作之间的隔离 连接。 但是,以下操作之间没有隔离 发生在同一数据库连接中。
换句话说,如果X使用 立即开始 然后发布一个或多个 更新 , 删除 、和/或 插入 语句,然后这些更改对后续语句可见 选择 声明 在数据库连接X中计算的。 选择 关于的声明 不同的数据库连接Y在X之前不会显示任何更改 事务提交。 但是 选择 X中的语句将显示更改 在提交之前。
在单个数据库连接X中,SELECT语句总是可以看到 在开始SELECT之前完成的数据库更改 声明,无论是否提交。 和SELECT语句 显然没有看到SELECT语句后发生任何更改 完成。 但是,当SELECT语句 正在运行? 如果启动SELECT语句 sqlite3_step() 接口逐步完成大约一半的输出,然后 更新 语句由修改表的应用程序运行 SELECT语句正在读取,然后更多调用 sqlite3_step() 是制造的 完成SELECT语句? SELECT的后续步骤 语句是否看到UPDATE所做的更改? 答案是 此行为未定义。 特别是,无论SELECT语句是否 看到并发更改取决于SQLite的哪个版本 运行时,数据库文件的模式,无论是否 分析 有 以及查询的详细信息。 在某些情况下,这可能取决于 数据库文件的内容。 没有好办法知道 SELECT语句是否会看到对数据库所做的更改 在SELECT语句启动后通过相同的数据库连接。 因此,开发人员应该努力避免编写应用程序 对这种情况下会发生什么做出假设。
如果应用程序对单个表发出SELECT语句,如 " SELECT rowid,*FROM表WHERE。。。 “并开始逐步通过 该语句的输出使用 sqlite3_step() 并检查每个 行,则应用程序可以安全地删除当前行或 任何使用“DELETE FROM table WHERE rowid=?”的前一行。 它也很安全 (从某种意义上说,它不会损害数据库) 删除预期稍后在查询中出现但尚未出现的行 出现了。 但是,如果删除了未来的行,则可能会发生以下情况 该行出现在随后的sqlite3_step()之后,即使它已经 据称已被删除。 或者可能不会。 这种行为没有定义。 应用程序可以 当SELECT语句为 正在运行,但是否显示新行 在随后的sqlite3_step()中,查询的s未定义。 以及应用程序 可以更新当前行或任何之前的行,但这样做可能会导致 该行将在随后的sqlite3_step()中重新出现。 只要 应用程序准备处理这些模糊性 它们本身是安全的,不会损害数据库文件。
在前两段中,有两个数据库连接 有相同的 共享缓存 以及启用了 PRAGMA读_未提交 被认为是相同的数据库连接。
总结
SQLite中的事务是SERIALIZABLE。
在一个数据库连接中所做的更改对所有其他数据库都不可见 提交之前的连接。
查询可以查看在同一数据库连接上完成的所有更改 在查询开始之前,无论这些更改是否发生 已提交。
如果查询后同一数据库连接发生更改 开始运行,但在查询完成之前,是否 否则查询将看到这些更改。
如果查询后同一数据库连接发生更改 开始运行,但在查询完成之前,查询可能会返回 更改了多次的行,或者它可能返回以前的行 删除。
对于前四项,两个数据库连接 使用相同的 共享缓存 以及使 PRAGMA读_未提交 是 视为相同的数据库连接,而不是单独的数据库 连接。