当前位置: 首页 > news >正文

长春火车站现在正常通车吗个人社保缴费标准

长春火车站现在正常通车吗,个人社保缴费标准,上海市场调研公司,购物平台网站建设返回#xff1a;SQLite—系列文章目录 上一篇:SQLite数据库成为内存中数据库#xff08;三#xff09; 下一篇#xff1a;SQLite使用的临时文件#xff08;二#xff09; 1. 引言 SQLite等事务数据库的一个重要特性 是“原子提交”。 原子提交意味着所有数据库都在… 返回SQLite—系列文章目录    上一篇:SQLite数据库成为内存中数据库三 下一篇SQLite使用的临时文件二 1. 引言 SQLite等事务数据库的一个重要特性 是“原子提交”。 原子提交意味着所有数据库都在一个数据库中更改 事务发生或均未发生。通过原子提交它 就好像对数据库的不同部分有许多不同的写入 文件瞬间同时发生。 真正的硬件将写入序列化为大容量存储并将写入 单个扇区需要有限的时间。 因此不可能真正写出一个 数据库文件同时和/或瞬时。 但是内部的原子提交逻辑 SQLite使它看起来好像是事务的更改 都是即时和同时写入的。 SQLite具有事务出现的重要属性 即使事务被 操作系统崩溃或电源故障。 本文介绍 SQLite 用于创建 原子提交的错觉。 本文中的信息仅适用于 SQLite 运行时 在“回滚模式”下或者换句话说当 SQLite 不是 使用预写日志。SQLite在以下情况下仍然支持原子提交 预写日志记录已启用但它通过以下方式完成原子提交 与本文中描述的机制不同的机制。看 预写日志文档了解有关操作方法的其他信息 SQLite支持该上下文中的原子提交。 2. 硬件假设 在本文中我们将大容量存储设备称为“磁盘” 即使大容量存储设备可能真的是闪存。 我们假设磁盘是以块的形式写入的我们称之为“扇区”。 无法修改小于扇区的磁盘的任何部分。 要更改小于扇区的磁盘部分必须读入 包含要更改的部分的完整扇区使 更改然后写回整个扇区。 在传统的旋转盘上扇区是最小的传输单位 在两个方向上阅读和写作。然而在闪存上 读取的最小大小通常比最小写入大小小得多。 SQLite 只关注最小写入量因此 本文的目的当我们说“部门”时我们指的是最低金额 可以一次性写入大容量存储的数据。 在 SQLite 版本 3.3.14 之前512 字节的扇区大小为 在所有情况下都假设。有一个编译时选项可以更改 但代码从未使用过更大的值进行测试。这 512字节扇区假设似乎是合理的因为直到最近 所有磁盘驱动器在内部都使用 512 字节扇区。但是有 最近一直在推动将磁盘的扇区大小增加到 4096 字节。还有扇区大小 对于闪存通常大于 512 字节。由于这些原因 从 3.3.14 开始的 SQLite 版本在操作系统中有一个方法 接口层询问底层文件系统以查找 真实扇区大小。按照当前实现的版本 3.5.0这 方法仍然返回 512 字节的硬编码值因为有 不是发现真实扇区大小的标准方法 Unix 或 Windows。但该方法可用于嵌入式设备 厂家根据自己的需要进行调整。我们有 保留了填写更有意义的实现的可能性 将来在 Unix 和 Windows 上。 SQLite传统上认为扇区写入不是原子的。 但是SQLite始终假设扇区写入是线性的。通过“线性” 我们的意思是SQLite假设在写入扇区时硬件开始 在数据的一端逐个字节写入直到到达 另一端。写入可能从头到尾或从 从结束到开始。如果在 扇区写入可能是部分扇区被修改 另一部分保持不变。SQLite的关键假设 是如果扇区的任何部分发生变化那么 第一个或最后一个字节将被更改。所以硬件会 永远不要在中间开始写一个部门然后朝着 结束。我们不知道这个假设是否总是正确的但它 似乎很合理。 上一段指出SQLite不假设 扇区写入是原子的。默认情况下这是 true 的。但截至 SQLite 版本 3.5.0有一个新接口称为 虚拟文件系统 VFS 接口。VFS是唯一的手段 SQLite通过它与底层文件系统进行通信。这 代码附带了适用于 Unix 和 Windows 的默认 VFS 实现 并且有一种用于创建新的自定义 VFS 实现的机制 在运行时。在这个新的 VFS 接口中有一个称为 xDeviceCharacteristics。此方法询问基础 文件系统来发现各种属性和行为 文件系统可能会也可能不会出现。xDeviceCharacteristics 方法可能指示扇区写入是原子的如果是 所以表明SQLite将尝试利用这一事实。但 Unix 和 Windows 的默认 xDeviceCharacteristics 方法 不表示原子扇区写入因此这些优化 通常被省略。 SQLite 假设操作系统将缓冲写入和 写入请求将在实际存储数据之前返回 在大容量存储设备中。 SQLite 进一步假设写入操作将按 操作系统。 出于这个原因SQLite在键上执行“flush”或“fsync”操作 点。SQLite 假设 flush 或 fsync 不会返回直到 对于正在刷新的文件所有挂起的写入操作都具有 完成。我们被告知 flush 和 fsync 原语 在某些版本的 Windows 和 Linux 上已损坏。这是不幸的。 它使SQLite面临以下数据库损坏的可能性 提交过程中的断电。但是什么都没有 SQLite可以做来测试或补救这种情况。SQLite的 假定运行它的操作系统以 广告。如果情况并非如此那么希望您 不会经常断电。 SQLite 假设当文件长度增长时新的 文件空间最初包含垃圾后来被填充 实际写入数据。换句话说SQLite假设 文件大小在文件内容之前更新。这是一个 悲观的假设和SQLite必须做一些额外的工作才能使 确保在断电时不会导致数据库损坏 在文件大小增加的时间和 编写新内容。的 xDeviceCharacteristics 方法 VFS 可能指示文件系统将始终写入 数据然后再更新文件大小。这是 为那些正在寻找的读者提供SQLITE_IOCAP_SAFE_APPEND属性 在代码中。当 xDeviceCharacteristics 方法指示 文件内容是在文件大小增加之前写入的 SQLite可以放弃一些迂腐的数据库保护步骤 从而减少执行 犯。然而目前的实现没有做出这样的假设 用于 Windows 和 Unix 的默认 VFS。 SQLite 假设文件删除是原子的用户进程的观点。我们的意思是如果SQLite 请求删除文件并在 删除操作一旦电源恢复文件将如果其原始内容未改变则完全存在或者否则该文件将根本不会在文件系统中看到。如果电源恢复后文件仅部分删除 如果其某些数据已被更改或删除 或者文件已被截断但未完全删除则可能会导致数据库损坏。 SQLite 假设检测和/或纠正 由宇宙射线、热噪声、量子引起的位误差 波动、设备驱动程序错误或其他机制是底层硬件和操作系统的责任。 SQLite不会向数据库文件添加任何冗余检测损坏或 I/O 错误的目的。 SQLite假设它读取的数据与数据完全相同它之前写过。 默认情况下SQLite 假定操作系统调用要写入字节范围不会损坏或更改该范围之外的任何字节 即使在写入过程中发生断电或操作系统崩溃。我们 将此称为“PowerSafe Overwrite”属性。 在版本 3.7.9 2011-11-01 之前SQLite 没有假设 powersafe覆盖。但是有了标准在大多数磁盘驱动器上扇区大小从 512 字节增加到 4096 字节它 为了维护 历史性能水平因此 powersafe 覆盖由 在最新版本的 SQLite 中是默认的。powersafe 的假设 如果出现以下情况可以在编译时或运行时禁用 overwrite 属性 期望。有关更多信息请参阅 powersafe 覆盖文档 详。 3. 单文件提交 我们首先概述SQLite采取的步骤以便对单个数据库执行事务的原子提交文件。用于防止损坏的文件格式的详细信息 电源故障和执行原子提交的技术多个数据库将在后面的章节中讨论。 3.1. 初始状态 数据库连接时计算机的状态首次打开在概念上显示在 右。 最右边的图表区域标记为“磁盘”代表存储在大容量存储设备上的信息。每个矩形是 一个部门。蓝色表示扇区包含原始数据。 中间区域是操作系统磁盘缓存。在 在我们的示例开始时缓存是冷的这表示为通过将磁盘缓存的矩形留空。 图的左侧区域显示了使用 SQLite 的进程。数据库连接具有刚刚打开还没有读取任何信息所以 用户空间为空。 3.2. 获取读锁 在SQLite可以写入数据库之前它必须首先读取 数据库来查看已经存在的内容。即使它只是 附加新数据SQLite仍然需要读取数据库 schema from the “sqlite_schema” table 以便它可以知道 如何解析 INSERT 语句并发现 数据库文件应存储新信息。 从数据库文件读取的第一步 正在获取数据库文件的共享锁。一个“共享” lock 允许两个或多个数据库连接从 数据库文件。但是共享锁可以防止 写入数据库文件的另一个数据库连接当我们阅读它时。这是必要的因为如果另一个 数据库连接正在写入数据库文件位于当我们从数据库文件读取时我们可能会读取 更改前的一些数据和更改后的其他数据。 这将使它看起来好像是另一个人所做的更改 过程不是原子的。 请注意共享锁位于操作系统上 磁盘缓存而不是磁盘本身。文件锁 真的只是操作系统内核中的标志通常。详细信息取决于特定的操作系统层接口。因此如果操作系统崩溃或断电。它通常也是这样一种情况如果创建锁的进程退出。 3.3. 从数据库中读取信息 获取共享锁后我们可以开始阅读 数据库文件中的信息。在这种情况下我们假设是冷缓存所以信息必须首先 然后从大容量存储读取到操作系统缓存中从操作系统缓存传输到用户空间。 在随后的读取中部分或全部信息可能 已经在操作系统缓存中找到所以只有需要转移到用户空间。 通常只有数据库文件中页面的子集 被读取。在此示例中我们展示了三个 正在阅读的八页。在典型应用中 数据库将有数千个页面查询通常会只接触这些页面的一小部分。 3.4. 获取保留锁 在对数据库进行更改之前首先要 SQLite 获取数据库文件的“保留”锁。保留锁与共享锁类似因为两者都是保留锁 和共享锁允许其他进程从数据库中读取数据 文件。单个备用锁可以与多个共享锁共存来自其他进程的锁。但是只能有一个 数据库文件上的单个保留锁。因此只有单个进程可能正在尝试写入数据库 一次。 保留锁背后的想法是它发出信号 进程打算在 NEAR 中修改数据库文件 未来但尚未开始进行修改。 而且由于修改尚未开始其他 进程可以继续从数据库中读取数据。然而 任何其他进程也不应开始尝试写入 数据库。 3.5. 创建回滚日志文件 在对数据库文件进行任何更改之前SQLite 首先创建一个单独的回滚日志文件并写入 回滚日志原始日志 要更改的数据库页的内容。 回滚日志背后的想法是它包含将数据库还原回其原始状态。 回滚日志包含一个小标题显示为绿色 在图中记录数据库的原始大小 文件。因此如果更改导致数据库文件增长我们 仍将知道数据库的原始大小。页面 数字与每个数据库页一起存储即写入回滚日志。 创建新文件时大多数桌面操作系统 Windows、Linux、Mac OS X实际上不会写入任何内容 磁盘。新文件是在操作系统磁盘中创建的 仅限缓存。直到某个时候该文件才会在大容量存储上创建 稍后当操作系统有空闲时间时。这将创建 给用户的印象是 I/O 的发生速度比 在执行真实磁盘 I/O 时是可能的。我们在 右边的图显示了新的回滚日志仅出现在操作系统磁盘缓存中而不在磁盘本身。 3.6. 更改用户空间中的数据库页面 在回滚中保存原始页面内容后 日志页面可以在用户内存中修改。每个数据库 连接有自己的用户空间的私有副本因此更改 在用户空间中创建的内容仅对数据库连接可见 那就是做出改变。其他数据库连接仍会看到 操作系统磁盘缓存缓冲区中的信息具有 尚未更改。因此即使一个进程很忙 修改数据库后其他进程可以继续读取其 拥有原始数据库内容的副本。 3.7. 将回滚日志文件刷新到大容量存储 下一步是刷新回滚日志的内容 文件到非易失性存储。 正如我们稍后将看到的 这是确保数据库能够生存的关键步骤 意外断电。 这一步也需要很多时间因为写入非易失性 存储通常是一个缓慢的操作。 此步骤通常比简单的冲洗更复杂 将日志回滚到磁盘。在大多数平台上两个独立的 flush(或 fsync())操作是必需的。第一次刷新写入输出基本回滚日志内容。然后是修改回滚日记帐以显示 回滚日志。然后将标头刷新到磁盘。细节关于我们为什么要这样做提供了标题修改和额外的刷新在本文的后面部分。 3.8. 获取独占锁 在对数据库文件本身进行更改之前我们必须 获取数据库文件的独占锁。获取 独占锁实际上是一个两步过程。首先 SQLite 获得 “待定”锁。然后它将挂起的锁升级为 独占锁。 挂起的锁允许其他进程已经具有共享锁继续读取数据库文件。但它阻止建立新的共享锁。理念挂起锁的背后是为了防止写入器饥饿造成的由一大群读者。可能有几十个甚至几百个 尝试读取数据库文件的其他进程。每个过程在开始读取之前获取共享锁读取它的内容 needs然后释放共享锁。但是如果有许多不同的进程都从同一个数据库读取它可能会发生新进程始终在之前获取其共享锁的情况 上一个进程释放其共享锁。所以有当数据库上没有共享锁时永远不会有瞬间 文件因此作者永远没有机会 抓住专属锁。挂起的锁旨在防止 通过允许现有共享锁继续进行但阻止建立新的共享锁。最终 所有共享锁都将清除待处理的锁将被清除 能够升级为独占锁。 3.9. 对数据库文件的写入更改 一旦持有排他性锁我们就知道没有其他锁了 进程正在从数据库文件中读取它是安全地将更改写入数据库文件。通常这些更改仅适用于操作系统磁盘 缓存不要一直到大容量存储。 3.10. 0 对大容量存储的刷新更改 必须进行另一次刷新更改以确保所有数据库更改将写入非易失性存储器。 这是确保数据库将在断电后幸存下来没有损坏。但是因为写入磁盘或闪存的固有速度慢 此步骤与回滚日志文件一起刷新 以上 3.7 占用了完成 SQLite中的事务提交。 3.11. 1 删除回滚日志 数据库更改后所有更改都安全地在质量上存储设备则删除回滚日志文件。 这是交易提交的那一刻。 如果在此之前发生电源故障或系统崩溃点然后恢复过程将在后面描述使看起来好像从未对数据库进行过任何更改 文件。如果在以下情况下发生电源故障或系统崩溃回滚日志被删除然后看起来好像所有更改都已写入磁盘。因此SQLite 给出没有对数据库进行任何更改的外观文件或已对数据库文件取决于是否回滚 日志文件存在。 删除文件并不是真正的原子操作而是 它似乎是从用户进程的角度来看的。 一个进程总是能够要求操作系统“做 这个文件存在吗“该过程将返回”是“或”否” 答。在 事务提交SQLite会询问操作系统 回滚日志文件是否存在。如果 答案是“是”则交易不完整并且是 回滚。如果答案是“否”则意味着交易 确实犯了。 交易的存在取决于是否或 回滚日志文件不存在并且删除 的文件似乎是原子操作 用户空间进程的视图。因此 事务似乎是原子操作。 在许多系统上删除文件的行为成本很高。 作为优化可以将SQLite配置为截断 日志文件的长度为零字节 或用零覆盖日志文件头。在任一 情况下生成的日志文件不再能够滚动 回来所以交易仍然提交。截断文件 长度为零就像删除文件一样被假定为原子 从用户进程的角度进行操作。覆盖 带有零的日记帐的标题不是原子的但如果有的话 部分标题格式不正确日志不会回滚。 因此可以说提交发生在标头之后 已充分更改以使其无效。通常会发生这种情况 一旦标头的第一个字节归零。 3.12. 2 释放锁 提交过程的最后一步是释放独占锁定以便其他进程可以再次 开始访问数据库文件。 在右图中我们显示了 当解锁锁时用户空间中保留的内容将被清除。 对于旧版本的SQLite来说这曾经是正确的。但 SQLite的更新版本保留了用户空间信息 在内存中以防在开始时再次需要它 下一笔交易。重用以下信息的成本更低 已经在本地内存中而不是将信息传输回 从操作系统磁盘缓存中读取它或从 再次磁盘驱动器。在用户空间中重用信息之前 我们必须首先重新获得共享锁然后我们必须检查 以确保没有其他进程修改数据库文件而 我们没有拿着锁。第一页有一个计数器 每次数据库文件递增的数据库 被修改。我们可以找出另一个进程是否修改了 数据库通过检查该计数器。如果数据库已修改 然后必须清除并重新读取用户空间缓存。但事实确实如此 通常情况下没有进行任何更改并且用户空间缓存可以重复使用从而显著降低性能。 4. 回滚 原子提交应该是瞬间发生的。但是处理 上述描述显然需要有限的时间。 假设计算机的电源被切断了 在上述提交操作的中途。挨次为了保持变化是瞬间的错觉我们 必须“回滚”任何部分更改并将数据库还原到 它在交易开始之前所处的状态。 4.1. 当出现问题时... 假设发生了断电 在上面的步骤 3.10 中 当数据库更改被写入磁盘时。 电力恢复后情况可能会有所不同 就像右边显示的那样。我们试图改变 数据库文件的三页但只有一页是 写得好。另一页是部分写的 第三页根本没有写。 回滚日志在磁盘上完成且完好无损符合以下条件电源恢复。这是一个关键点。原因 步骤 3.7 中的刷新操作是绝对确保 所有回滚日志都安全地位于非易失性存储器上 在对数据库文件本身进行任何更改之前。 4.2. 热回滚日志 任何 SQLite 进程第一次尝试访问 数据库文件它获取上文第 3.2 节中所述的共享锁。 但随后它注意到有一个 回滚日志文件存在。然后SQLite 检查是否 回滚日志是“热门日志”。热门期刊是 需要回放的回滚日志 将数据库还原到正常状态。仅热期刊 当早期进程处于提交过程中时存在 崩溃或断电时的交易。 如果满足以下所有条件则回滚日志是“热”日志 是真的 回滚日志存在。回滚日志不是空文件。主数据库文件上没有保留锁。回滚日志的标题格式正确特别是 尚未归零。回滚日志不 包含超级日志文件的名称请参阅下面的第 5.5 节或者如果包含 包含超级期刊的名称然后包含该超级期刊的名称 文件存在。 热期刊的存在是我们的指示 以前的进程正在尝试提交事务但 在完成之前由于某种原因中止了 犯。热门期刊意味着 数据库文件处于不一致状态需要 在使用前进行修复通过回滚。 4.3. 获取数据库的独占锁 处理热门期刊的第一步是 获取数据库文件的独占锁。这样可以防止两个 或尝试回滚同一热日志的更多进程 同时。 4.4. 回滚不完整的更改 一旦进程获得独占锁它就被允许了 写入数据库文件。然后它继续读取 页面的原始内容从回滚日志中取出并写入 该内容返回到数据库文件中的来源位置。 回滚日志的标题记录了原始日志 开始中止之前数据库文件的大小 交易。SQLite 使用此信息截断 数据库文件恢复到其原始大小如果 事务不完整导致数据库增长。在 在此步骤结束时数据库的大小应相同且 包含与开始之前相同的信息 中止的事务。 4.5. 删除热门日志 回滚日志中的所有信息都已 播放到数据库文件中并刷新到磁盘以防万一 我们又遇到了一次电源故障热回滚日志 可以删除。 如第 3.11 节所述日记 文件可能被截断为零长度或者其标题可能被截断 被零覆盖作为对以下系统的优化 删除文件的成本很高。无论哪种方式日记都不是 此步骤后更热。 4.6. 继续就好像未完成的写入从未发生过一样 最后的恢复步骤是减少独占锁定 到共享锁。一旦发生这种情况数据库就会回到 声明如果中止的事务从未发生过就会如此 开始。由于所有这些恢复活动都完全发生 自动且透明地它显示给程序使用 SQLite就好像中止的事务从未开始一样。 5. 多文件提交 SQLite允许与单个数据库连接进行通信 两个或多个数据库文件同时通过使用 ATTACH DATABASE 命令。 在单个数据库文件中修改多个数据库文件时 事务所有文件都以原子方式更新。 换言之要么更新所有数据库文件要么 否则都不是。 跨多个数据库文件实现原子提交是 比对单个文件这样做更复杂。本节 描述了 SQLite 如何工作。 5.1. 为每个数据库单独回滚日志 当事务中涉及多个数据库文件时 每个数据库都有自己的回滚日志和每个数据库 单独锁定。右图显示了一个方案 其中修改了三个不同的数据库文件 一笔交易。这一步的情况类似于 步骤 3.6 中的单文件事务方案。每个数据库文件都有 保留锁。对于每个数据库页面的原始内容 正在更改的内容已写入回滚日志 对于该数据库但期刊的内容尚未 已刷新到磁盘。未对数据库进行任何更改 文件本身尽管可能正在举行更改 在用户内存中。 为简洁起见本节中的图表从 那些以前来的。蓝色仍然表示原创内容 粉红色仍然表示新内容。但是个别页面 在回滚日志和数据库文件中未显示并且 我们没有区分 操作系统缓存和磁盘上的信息。所有 这些因素仍然适用于多文件提交方案。他们 只是在图表中占用了大量空间并且不会添加 任何新信息因此此处省略。 5.2. 超级日志文件 多文件提交的下一步是创建一个 “超级日志”文件。超级日志文件的名称是 与原始数据库文件名数据库 使用 sqlite3_open 接口打开 不是 ATTACHed 辅助人员之一 databases并附加文本“-mjHHHHHHHHH”其中 HHHHHHHH 是随机的 32 位十六进制数。这 每个新的超级期刊的随机 HHHHHHHH 后缀更改。 注意计算超级期刊文件名的公式 上一段中给出的对应于实现为 SQLite 版本 3.5.0。但这个公式不是SQLite的一部分 规范并可能在将来的版本中发生变化。 与回滚日志不同超级日志不包含 任何原始数据库页面内容。取而代之的是超级期刊包含 每个数据库的回滚日志的完整路径名 参与交易。 超级日志构建完成后其内容将被刷新 在执行任何进一步操作之前先磁盘。在 Unix 上目录 包含超级日志的也同步以确保 超级日志文件将出现在目录中在 电源故障。 超级期刊的目的是确保多文件 在断电期间事务是原子的。但是如果数据库文件 具有其他影响断电事件完整性的设置 例如 PRAGMA synchronousOFF 或 PRAGMA journal_modeMEMORY 则 作为优化省略了超级期刊的创建。 5.3. 更新回滚日志头 下一步是记录超级日志文件的完整路径名 在每个回滚日志的标题中。空间来容纳 超级日志文件名保留在每个回滚日志的开头 创建回滚日志时。 每个回滚日志的内容都会刷新到磁盘 并且将超级日志文件名写入回滚后 日志标题。进行这两种冲洗都很重要。幸运 第二次冲洗通常很便宜因为通常只有一次 日志文件的页面第一页已更改。 此步骤类似于单文件提交中的步骤 3.7 上述场景。 5.4. 更新数据库文件 将所有回滚日志文件刷新到磁盘后它 可以安全地开始更新数据库文件。我们必须获得一个 在写入更改之前对所有数据库文件进行独占锁定。 写入所有更改后请务必刷新 对磁盘进行更改以便在发生以下情况时保留它们 电源故障或操作系统崩溃。 此步骤对应于单文件提交中的步骤 3.8、3.9 和 3.10 前面描述的场景。 5.5. 删除超级日志文件 下一步是删除超级日志文件。 这是多文件事务提交的点。 此步骤对应于单个文件中的步骤 3.11 删除回滚日志的提交方案。 如果此时发生电源故障或操作系统崩溃 点当系统重新启动时事务不会回滚 即使存在回滚日志。这 区别在于 回滚日志。重新启动后SQLite 仅考虑日志 很热只有在没有的情况下才会播放日记 标题中的超级日志文件名就是这种情况 单文件提交或者如果超级日志文件仍然 存在于磁盘上。 5.6. 清理回滚日志 多文件提交的最后一步是删除 单个回滚日志并删除独占锁定 数据库文件以便其他进程可以看到更改。 这对应于单文件中的步骤 3.12 提交序列。 此时交易已经提交所以时机 在删除回滚日志时并不重要。 当前实现删除单个回滚日志 然后解锁相应的数据库文件然后再继续 到下一个回滚日志。但在未来我们可能会改变 这样所有回滚日志都会在任何数据库之前被删除 文件已解锁。只要之前删除了回滚日志 其对应的数据库文件已解锁在什么情况下并不重要 删除回滚日志或数据库文件的顺序解锁。 6. 提交过程的其他细节 上面的第 3.0 节概述了 原子提交如何在SQLite中工作。但它掩盖了许多重要细节。以下小节将尝试填充在缝隙中。 6.1. 始终记录完整的扇区 当数据库页的原始内容写入 回滚日志如第 3.5 节所示 SQLite 总是写入一个完整的数据扇区即使 数据库的页面大小小于扇区大小。 从历史上看SQLite 中的扇区大小已硬编码为 512 字节并且由于最小页面大小也是 512 字节因此从未 是一个问题。但是从 SQLite 版本 3.3.14 开始这是可能的 使 SQLite 使用扇区大小大于 512 的大容量存储设备 字节。因此从版本 3.3.14 开始每当 扇区被写入日志文件中所有页面都在同一扇区中 与它一起存储。 在回滚中存储扇区的所有页面非常重要 日志以防止数据库损坏。 写扇区时的损失。假设第 1、2、3 和 4 页是 全部存储在扇区 1 中并且第 2 页被修改。为了写 对第 2 页的更改底层硬件还必须重写 第 1、3 和 4 页的内容因为硬件必须写入完整的 扇形。如果此写入操作因断电而中断 第 1、3 或 4 页中的一个或多个可能会留下不正确的数据。 因此为了避免对数据库的持久损坏原始内容 所有这些页面都必须包含在回滚日志中。 6.2. 处理写入日志文件的垃圾 当数据追加到回滚日志的末尾时 SQLite通常会做出悲观的假设即文件 首先使用无效的“垃圾”数据进行扩展然后 正确的数据取代了垃圾。换句话说SQLite假设 首先增加文件大小然后增加内容 写入文件。如果文件后发生电源故障 大小已增加但在写入文件内容之前 回滚日志可以保留包含垃圾数据。如果在之后 电源恢复另一个 SQLite 进程看到回滚日志 包含垃圾数据并尝试将其回滚到原始数据中 数据库文件它可能会将一些垃圾复制到数据库文件中 从而损坏数据库文件。 SQLite使用两种防御措施来解决这个问题。首先 SQLite 在标题中记录回滚日志中的页数 的回滚日志。此数字最初为零。所以在 尝试回滚不完整可能已损坏的回滚 journal则进行回滚的进程将看到 journal 包含零页因此不会对数据库进行任何更改。事先 对于提交回滚日志将刷新到磁盘以确保 所有内容都已同步到磁盘没有“垃圾” 只有这样标题中的页数才会从 回滚日志中的页数为零到真实页数。回滚日志 页眉始终保存在与任何页面数据不同的扇区中以便 它可以被覆盖和刷新而不会有损坏数据的风险 页面如果发生停电。请注意回滚日志 刷新到磁盘两次一次用于写入页面内容第二次用于写入页面内容 是时候在标题中写入页数了。 上一段描述了当 同步编译指示设置为“已满”。 PRAGMA 同步FULL; 默认同步设置是 full 所以上面是通常的 发生。但是如果同步设置降低到“正常” SQLite 仅在页数达到 被写了。 这会带来腐败的风险因为可能会发生 修改后的非零页计数首先到达磁盘表面 的数据确实如此。数据将首先写入但 SQLite 假定底层文件系统可以对写入请求进行重新排序并且 页数可以先烧成氧化物即使它 写入请求最后发生。因此作为第二道防线SQLite 还对回滚中的每一页数据使用 32 位校验和 杂志。在回滚期间将针对每个页面计算此校验和 在第 4.4 节中描述回滚日志时。如果校验和不正确 被看到回滚被放弃。请注意校验和 不能保证页面数据正确因为有一个小 但校验和可能是正确的概率有限即使数据是正确的 腐败。但是校验和至少使这种错误不太可能发生。 请注意回滚日志中的校验和不是必需的 如果同步设置为 FULL。我们只依赖于校验和 当同步降低到 NORMAL 时。尽管如此校验和 永远不会受到伤害因此无论如何它们都包含在回滚日志中 的同步设置。 6.3. 提交前的缓存溢出 第 3.0 节中显示的提交过程假定所有数据库更改都适合内存直到需要 犯。这是常见的情况。但有时更大的变化会 在事务提交之前溢出用户空间缓存。在那些 情况下缓存必须在事务之前溢出到数据库 已完成。 在缓存溢出开始时数据库的状态 连接如步骤 3.6 所示。 原始页面内容已保存在回滚日志中并且 页面的修改存在于用户内存中。要溢出缓存 SQLite 执行步骤 3.7 到 3.9。换言之回滚日志刷新到磁盘获取独占锁更改 写入数据库。但其余步骤被推迟 直到事务真正提交。新的日记帐标题是 附加到回滚日志的末尾在其自己的扇区中 并保留独占数据库锁但以其他方式进行处理 返回到步骤 3.6。当交易 提交或者如果发生另一个缓存溢出步骤 3.7 和 3.9 是 重复。步骤 3.8 在第二个省略 以及由于已持有独占数据库锁而进行的后续传递 由于第一次通过。 缓存溢出导致数据库文件上的锁定 从保留升级到独占。这会降低并发性。 缓存溢出还会导致额外的磁盘刷新或 fsync 操作 发生并且这些操作很慢因此缓存溢出可能会 严重降低性能。 由于这些原因应尽可能避免缓存溢出。 7. 优化 分析表明对于大多数系统和大多数情况 SQLite 大部分时间都花在磁盘 I/O 上。因此 我们可以采取任何措施来减少磁盘 I/O 的数量都可能有一个 对SQLite的性能有很大的积极影响。本节 描述了 SQLite 用来尝试减少 磁盘 I/O 的数量降至最低同时仍保留原子提交。 7.1. 事务之间保留的缓存 提交过程的步骤 3.12 显示 一旦共享锁被释放所有用户空间缓存 必须丢弃数据库内容的图像。这样做是因为 如果没有共享锁其他进程可以自由修改数据库 文件内容因此该内容的任何用户空间图像都可能成为 过时。因此每笔新交易都将从重读开始 之前已读取的数据。这并不像听起来那么糟糕 起初由于正在读取的数据仍然可能在操作中 系统文件缓存。因此“读取”实际上只是数据的副本 从内核空间到用户空间。但即便如此这仍然需要时间。 从 SQLite 版本 3.3.14 开始添加了一种机制 以尝试减少不必要的数据重读。在较新的版本中 用户空间寻呼机缓存中的数据在以下情况下保留 数据库文件上的锁定被释放。后来在 共享锁是在下一次交易开始时获得的 SQLite检查是否有任何其他进程修改了数据库 文件。如果数据库自锁定以来以任何方式更改 上次发布时用户空间缓存将在此时擦除。 但通常数据库文件保持不变用户空间缓存 可以保留可以避免一些不必要的读取操作。 为了确定数据库文件是否已更改 SQLite 在数据库标头中使用计数器以字节 24 到 27 为单位 在每次更改操作期间递增。SQLite 保存副本 在释放其数据库锁之前此计数器。然后之后 获取下一个数据库锁它比较保存的计数器值 针对当前计数器值如果值 不同如果它们相同则重用缓存。 7.2. 独占访问模式 SQLite 版本 3.3.14 增加了“独占访问模式”的概念。 在独占访问模式下SQLite 保留独占 数据库在每个事务结束时锁定。这可以防止 访问数据库的其他进程但在许多部署中 只有一个进程使用数据库所以这不是一个 严重的问题。独占访问模式的优点是 可以通过三种方式减少磁盘 I/O 没有必要在 第一个事务之后的事务的数据库标头。这 通常会将第一页的写入保存到回滚中 日志和主数据库文件。 没有其他进程可以更改数据库因此永远不会更改 需要检查更改计数器并清除用户空间缓存 在交易开始时。 每个事务都可以通过覆盖回滚来提交 日志标题为零而不是删除日志文件。 这样就避免了修改日志文件的目录条目 并且它避免了必须取消分配与 杂志。此外下一个事务将覆盖现有的 日志文件内容而不是附加新内容并且在大多数系统上 覆盖比追加快得多。 第三个优化将日志文件头归零而不是删除回滚日志文件 不依赖于始终持有独占锁。 此优化可以独立于独占锁定模式进行设置 使用下面第 7.6 节中描述的journal_mode编译指示。 7.3. 不要记录自由列表页面 从 SQLite 数据库中删除信息时使用的页面 要保留已删除的信息则将其添加到“自由列表”中。随后的 插入将从此自由列表中抽取页面而不是扩展 数据库文件。 一些自由列表页面包含关键数据;具体位置 其他自由列表页面。但是大多数自由列表页面不包含任何有用的东西。 这些后一种自由列表页面称为“叶子”页面。我们可以自由地 修改数据库中叶自由列表页面的内容而不修改 以任何方式更改数据库的含义。 由于叶子自由列表页面的内容并不重要因此 SQLite 避免在回滚日志中存储叶自由列表页面内容 在提交过程的步骤 3.5 中。 如果叶空闲列表页面已更改并且该更改未回滚 在事务恢复期间数据库不会因遗漏而受到损害。 同样新的自由列表页面的内容永远不会被写回 在步骤 3.9 中进入数据库也不是 从步骤 3.3 中的数据库中读取。 这些优化可以大大减少发生的 I/O 量 对包含可用空间的数据库文件进行更改时。 7.4. 单页更新和原子扇区写入 从 SQLite 版本 3.5.0 开始新的虚拟文件系统 VFS 接口包含一个名为 xDeviceCharacteristics 的方法该方法报告 在底层大容量存储设备的特殊属性上 可能有。在特殊属性中 xDeviceCharacteristics 可能报告的是执行 原子部门写。 回想一下默认情况下SQLite假定扇区写入是 线性但不是原子的。线性写入从 扇区并逐字节更改信息直到它到达 该行业的另一端。如果在中间发生断电 线性写入则扇区的一部分可能会被修改而 另一端保持不变。在原子扇区中写入要么整个 扇区被覆盖否则扇区中的任何内容都不会更改。 我们相信大多数现代磁盘驱动器都实现了原子扇区 写。当断电时驱动器使用存储在电容器中的能量 和/或圆盘盘片的角动量以提供动力 完成任何正在进行的操作。然而有这么多 写入系统调用和板载磁盘驱动器之间的层 我们在 Unix 和 w32 VFS 中采用安全方法的电子产品 实现并假设扇区写入不是原子的。在 另一只手设备 对其文件系统拥有更多控制权的制造商可能希望 考虑启用 xDeviceCharacteristics 的原子写入属性 如果他们的硬件真的可以进行原子写入。 当扇区写入是原子的并且数据库的页面大小为 与扇区大小相同当数据库发生变化时 只触及单个数据库页面然后SQLite跳过整个 日记和同步过程并简单地写入修改后的页面 直接进入数据库文件。第一个中的更改计数器 数据库文件的页面被单独修改因为没有伤害 如果在更新更改计数器之前断电则完成。 7.5. 具有安全附加语义的文件系统 SQLite 3.5.0 版中引入的另一个优化使 使用基础磁盘的“安全追加”行为。 回想一下SQLite假设当数据被附加到文件中时 特别是回滚日志文件的大小 首先增加其次编写内容。所以 如果在文件大小增加后但在 内容被写入文件包含无效的“垃圾” 数据。但是VFS 的 xDeviceCharacteristics 方法可能 指示文件系统实现了“安全追加”语义。 这意味着内容是在文件大小 增加使垃圾无法引入 由于断电或系统崩溃而进入回滚日志。 当为文件系统指示安全附加语义时 SQLite 始终存储页数的特殊值 -1 在回滚日志的标题中。-1 页计数值 告诉任何尝试回滚日志的进程 日记帐中的页数应从日记帐中计算 大小。此 -1 值永远不会更改。这样当提交 发生时我们保存了单个刷新操作和一个扇区写入 日志文件的第一页。此外当缓存 发生溢出我们不再需要附加新的日志标题 到日记的末尾;我们可以简单地继续附加 现有日记帐末尾的新页面。 7.6. 持久回滚日志 在许多系统上删除文件是一项代价高昂的操作。 因此作为优化可以配置 SQLite 以避免 删除第 3.11 节的操作。 而不是为了提交事务而删除日志文件 文件的长度被截断为零字节或者其 标头被零覆盖。将文件截断为零 length 省去了对包含 文件因为该文件不会从目录中删除。 覆盖标头可以节省额外的费用因为没有 更新文件的长度在许多系统的“inode”中 并且不必处理新释放的磁盘扇区。此外 在下一个事务中将通过覆盖创建日记帐 现有内容而不是将新内容附加到末尾 覆盖通常比追加快得多。 SQLite可以配置为通过覆盖来提交事务 带有零的日记标题而不是删除日志文件 通过使用 PRAGMA journal_mode设置“PERSIST”日记模式。 例如 PRAGMA journal_modePERSIST;使用持久日志模式可提供显著的性能 许多系统的改进。当然缺点是 日志文件保留在磁盘上占用磁盘空间和杂乱无章 目录在事务提交后很久。唯一安全的方法 删除持久日志文件就是提交事务 将日记模式设置为 DELETE PRAGMA journal_modeDELETE; BEGIN EXCLUSIVE; COMMIT;谨防通过任何其他方式删除持久性日志文件 由于日志文件可能很热在这种情况下删除它将 损坏相应的数据库文件。 从 SQLite 版本 3.6.4 2008-10-15 开始 TRUNCATE 日志模式是 还支持 PRAGMA journal_modeTRUNCATE;在截断日志模式下通过截断来提交事务 日志文件长度为零而不是删除日志文件 如在 DELETE 模式下或通过将标头归零如在 PERSIST 模式下。 TRUNCATE 模式具有 PERSIST 模式的优点即目录 包含日志文件和数据库的数据库不需要更新。 因此截断文件通常比删除文件更快。TRUNCATE 有 它后面没有 系统调用例如fsync将更改同步到磁盘。它可能 如果是这样会更安全。 但是在许多现代文件系统上截断是原子和 同步操作因此我们认为 TRUNCATE 通常是安全的 面对停电。如果您不确定是否或 不是 TRUNCATE 将在您的文件系统上同步和原子化它是 对您来说重要的是您的数据库在断电或运行时能够幸存下来 在截断操作期间发生的系统崩溃那么您可能会 请考虑使用其他日记模式。 在具有同步文件系统的嵌入式系统上TRUNCATE 结果 行为比 PERSIST 慢。提交操作的速度相同。 但是在 TRUNCATE 之后后续事务会变慢因为它是 覆盖现有内容比追加到文件末尾更快。 新的日志文件条目将始终附加在 TRUNCATE 之后但 通常会用 PERSIST 覆盖。 8. 测试原子提交行为 SQLite的开发人员相信它是健壮的 面对电源故障和系统崩溃因为 自动测试程序对 SQLite从模拟功率损耗中恢复的能力。 我们称之为“碰撞测试”。 SQLite 中的崩溃测试使用修改后的 VFS可以模拟 通电期间发生的文件系统损坏类型 丢失或操作系统崩溃。碰撞测试 VFS 可以模拟 扇区写入不完整页面充满垃圾数据因为 写入未完成写入顺序不正常全部发生 在测试场景的不同时间点。执行碰撞测试 事务一遍又一遍改变模拟的时间 发生功率损失和造成的损坏的性质。 然后每个测试在模拟崩溃后重新打开数据库并 验证事务是否完全发生 或者根本没有并且数据库完全处于 一致状态。 SQLite中的崩溃测试发现了许多非常 恢复机制中的细微错误现已修复。一些 这些错误非常晦涩难懂不太可能被发现 仅使用代码检查和分析技术。从这里 经验SQLite的开发人员对任何其他人都充满信心 不使用类似碰撞测试系统的数据库系统 可能包含未检测到的 bug这些 bug 将导致数据库 系统崩溃或电源故障后的损坏。 9. 可能出错的事情 SQLite中的原子提交机制已被证明是健壮的 但它可以通过一个足够有创意的人来规避 对手或足够损坏的操作系统实现。 本节介绍 SQLite 数据库的几种方式 可能因电源故障或系统崩溃而损坏。 另请参阅如何损坏数据库文件。 9.1. Broken Locking 实现 SQLite 使用文件系统锁来确保只有一个 进程和数据库连接正在尝试修改数据库 一次。实现文件系统锁定机制 在 VFS 层中并且每个操作系统都不同。 SQLite取决于此实现是否正确。如果有什么 出错两个或多个进程能够写入相同的内容 数据库文件否则会造成严重损坏。 我们收到了关于这两项措施实施情况的报告 Windows 网络文件系统和 NFS其中锁定 微妙地破碎了。我们无法验证这些报告但作为 在网络文件系统上很难正确锁定 我们没有理由怀疑他们。建议您 首先避免在网络文件系统上使用 SQLite 因为性能会很慢。但是如果您必须使用 网络文件系统来存储 SQLite 数据库文件请考虑 使用辅助锁定机制防止同时发生 即使本机文件系统也写入同一数据库 锁定机构故障。 Apple 上预装的 SQLite 版本 Mac OS X 计算机包含的 SQLite 版本已 扩展为使用适用于以下问题的替代锁定策略 Apple 支持的所有网络文件系统。这些扩展 只要所有进程都在访问Apple 就可以很好地工作 以同样的方式处理数据库文件。不幸的是锁定 机制不会相互排斥因此如果一个过程是 使用例如AFP 锁定和其他 进程可能在另一台机器上使用点文件锁 这两个进程可能会发生冲突因为 AFP 锁不排除 点文件锁定反之亦然。 9.2. 磁盘刷新不完整 SQLite 在 Unix 和 FlushFileBuffers 上使用 fsync 系统调用 在 W32 上调用系统以将文件系统缓冲区同步到磁盘上 氧化物如步骤3.7和步骤3.10所示。不幸的是我们收到了 报告说这些接口都不像许多接口所宣传的那样工作 系统。我们听说 FlushFileBuffers 可以完全禁用 在某些 Windows 版本上使用注册表设置。一些 历史的 Linux 版本包含 fsync 的版本这些版本对 我们被告知一些文件系统。即使在以下系统上 据说 FlushFileBuffers 和 fsync 经常工作 IDE磁盘控制撒谎并说数据已到达氧化物 而它仍然仅保存在易失性控制缓存中。 在 Mac 上您可以设置以下编译指示 PRAGMA fullfsyncON; 在 Mac 上设置 fullfsync 将保证数据确实有效 在冲洗时被推到圆盘盘上。但是实施 的 fullfsync 涉及重置磁盘控制器。所以不仅 它是否非常慢它还减慢了其他不相关的磁盘 I/O。 因此不建议使用它。 9.3. 部分文件删除 SQLite 假设文件删除是 用户进程的观点。如果电源在中间发生故障 删除文件然后在电源恢复后 SQLite 希望看到 要么是整个文件其所有原始数据都完好无损要么 预计根本找不到文件。事务可能不是原子的 在无法以这种方式工作的系统上。 9.4. 写入文件的垃圾 SQLite数据库文件是普通的磁盘文件可以 由普通用户进程打开和写入。流氓进程 可以打开SQLite数据库并用损坏的数据填充它。 损坏的数据也可能被引入SQLite数据库 操作系统或磁盘控制器中的错误;特别是 由电源故障触发的 Bug。SQLite无能为力 做来防御这类问题。 9.5. 删除或重命名热门日志 如果确实发生崩溃或断电并且热日志保持打开状态 磁盘上必须要有原始的数据库文件和热的 日志以其原始名称保留在磁盘上直到数据库 文件由另一个 SQLite 进程打开并回滚。 在步骤 4.2 的恢复期间SQLite 定位 热日志 通过查找与 正在打开的数据库其名称派生自 文件正在打开。如果原始数据库文件或 热门日记已被移动或重命名则热门日记将 不被看到数据库也不会回滚。 我们怀疑SQLite恢复的常见故障模式发生了 像这样发生电源故障。电力恢复后一个善意的 用户或系统管理员开始在磁盘上四处查找 损伤。他们看到名为“important.data”的数据库文件。此文件 也许是他们熟悉的。但崩盘之后还有一个 名为“important.data-journal”的热门期刊。然后用户删除 热门期刊认为他们正在帮助清理系统。 我们知道除了用户教育之外没有其他方法可以防止这种情况。 如果数据库文件有多个硬链接或符号链接 日记帐将使用链接的名称创建通过该链接 文件已打开。如果发生崩溃并再次打开数据库 使用其他链接将找不到热日志也不会找到 将发生回滚。 有时电源故障会导致文件系统损坏 这样最近更改的文件名就会被遗忘而文件是 移至“/lostfound”目录。当这种情况发生时热 找不到日记帐也不会进行恢复。 SQLite试图阻止这种情况 通过打开并同步包含回滚日志的目录 同时它同步日志文件本身。但是 将文件移动到 /lostfound 可能是由不相关的进程引起的 在与主数据库文件相同的目录中创建不相关的文件。 而且由于这是在SQLite的控制之下所以什么都没有 SQLite可以做些什么来防止它。如果您在以下系统上运行 容易受到这种文件系统命名空间损坏的影响大多数 我们相信现代日志文件系统是免疫的那么你可能会 想考虑将每个 SQLite 数据库文件放在自己的私有文件中 子目录。 10. 未来的方向和结论 时不时有人会发现一种新的故障模式 SQLite中的原子提交机制开发人员必须 放入补丁。这种情况越来越少而且 故障模式变得越来越模糊。但它会 仍然愚蠢地假设 SQLite是完全没有错误的。开发人员致力于修复 这些错误可能会尽快被发现。 开发人员也在寻找新的方法 优化提交机制。当前的 VFS 实现 对于 UnixLinux 和 Mac OS X和 Windows 做出悲观的假设 这些系统的行为。咨询专家后 关于这些系统是如何工作的我们也许可以放松一些 对这些系统的假设并允许它们运行得更快。在 特别是我们怀疑大多数现代文件系统都表现出 安全附加属性其中许多可能支持原子 部门写道。但在确定这一点之前SQLite 将 采取保守的方法做最坏的打算。
http://www.zqtcl.cn/news/224505/

相关文章:

  • 河北手机网站制作企业网页设计的基本步骤和流程
  • 企业网站内容如何更新软件开发公司网站模板
  • 北京网站建设收费长沙有哪个学校可以学网站建设
  • 南江网站建设中国最好的app开发公司
  • 简单旅游网站开发建立网站的三种方式
  • 大连网站的优化网站设计 优帮云
  • 梧州网站seo表白网站在线生成免费
  • 网站制作体会php网站开发答案
  • 南阳响应式网站淘宝上成都网站建设
  • 深圳做手机网站设计wordpress禁用wp-cron
  • 如何自己建公司网站搜索引擎排名2020
  • 济南建站商业网站开发入门选课
  • 济南网络免费推广网站四川建设厅官方网站查询
  • 中国建设银行网站首页wordpress安装多个
  • 中国住建厅网站官网怎么建立网站快捷方式
  • 天津协会网站建设怎么用dw做带登陆的网站
  • 南宁做网站服务商苏州网站建设品牌
  • 做平台销售网站上海市普陀区建设规划局网站
  • 网站的积分系统怎么做电影在线观看
  • 成都网站建设方案服务旅游网站建设报价单
  • 京东网址seo排名查询工具
  • 南京制作网站速成班外贸营销信模板
  • 简单网站建设规划方案物联网网站设计
  • 做360网站官网还是百度四川平台网站建设方案
  • 做网站的主题有哪些精品课程网站建设情况
  • 帝国网站开发电商平台搭建
  • 建设工程网站tcwordpress 标题入库
  • 网站开发简直广州网站制作后缀
  • 上海短视频seo优化网站wordpress 构建知识库
  • 做的网站图片不显示2018做网站赚钱不