【问题标题】:Is it safe to delete sqlite's WAL file?删除 sqlite 的 WAL 文件是否安全?
【发布时间】:2014-01-25 00:56:27
【问题描述】:

我在 iOS 应用程序中遇到了一个奇怪的 Core Data 问题,有时 WAL 文件会变成 huge (~1GB)。似乎还有其他人遇到了问题(例如Core Data sqlite-wal file gets MASSIVE (>7GB) when inserting ~5000 rows)。

我最初的想法是在应用启动时删除 WAL 文件。从阅读有关此事的 sqlite 文档看来,这会很好。但是有人知道这样做有什么缺点吗?

我当然想了解 WAL 文件为何会变得如此之大的原因,但我现在无法深入了解它,我想在深入研究时提出一种解决方法问题。

值得指出的是,我的 Core Data 数据库更像是一个缓存。因此,如果我丢失 WAL 中的数据也没关系。我真正需要知道的是,如果我删除 WAL,数据库会完全损坏吗?我的怀疑是否定的,否则 WAL 无法达到其目的之一。

【问题讨论】:

  • 关于“否则 WAL 不能满足其目的之一”——WAL 的目的不是保护数据库不因删除 WAL 而损坏。如果不再存在,它怎么可能保护任何东西?看我的回答。这就像说您可以从汉明编码数据中删除冗余位,因为这些位的目的是保护数据免受损坏。

标签: ios core-data sqlite


【解决方案1】:

WAL 模式有问题,不要使用。问题各不相同,但您的报告是一个非常大的问题,其他问题包括迁移期间的失败(使用 NSPersistentStoreCoordinators migratePersistentStore)和导入 iCloud 事务日志期间的失败。因此,虽然在修复这些错误之前有报告的好处,但使用 WAL 模式可能是不明智的。

不,您不能删除预写日志,因为它包含最新的数据。

将数据库设置为使用回滚日志模式,我想您会发现在加载大量数据时不再拥有这些非常大的文件。

这是解释 WAL 工作原理的摘录。除非您可以保证您的应用程序已运行检查点,否则我看不出如何删除 WAL 文件而不冒删除已提交事务的风险。

WAL 的工作原理

传统的回滚日志通过编写副本来工作 原始未更改的数据库内容到单独的回滚日志中 文件,然后将更改直接写入数据库文件。在里面 发生崩溃或 ROLLBACK 事件时,包含在 回滚日志回放到数据库文件中以恢复 数据库文件恢复到其原始状态。 COMMIT 发生时 回滚日志被删除。

WAL 方法颠倒了这一点。原始内容保存在 数据库文件和更改附加到单独的 WAL 文件。当指示提交的特殊记录是 附加到 WAL。因此,COMMIT 可以在不写信的情况下发生 原始数据库,允许读者继续操作 原始未更改的数据库,同时发生更改 致力于 WAL。多个交易可以附加到 单个 WAL 文件的结尾。

检查点

当然,人们希望最终转移所有交易 在 WAL 文件中附加到原始数据库中。移动 回到数据库的 WAL 文件事务称为 “检查点”。

另一种思考回滚和回滚区别的方式 write-ahead log 是在回滚日志方法中,有 两个原始操作,读取和写入,而使用 write-ahead log 现在有三个原始操作:读取, 编写和检查点。

默认情况下,SQLite 会在 WAL 文件时自动执行检查点 达到 1000 页的阈值大小。 (这 SQLITE_DEFAULT_WAL_AUTOCHECKPOINT 编译时选项可用于 指定不同的默认值。)使用 WAL 的应用程序不必这样做 为了使这些检查点发生的任何事情。但如果他们想要 到,应用程序可以调整自动检查点阈值。要么 他们可以关闭自动检查点并在期间运行检查点 空闲时间或在单独的线程或进程中。

【讨论】:

  • WAL 有好处。如果它真的那么不好,那么为什么Apple选择它作为iOS 7的默认设置?我不想随机删除 WAL,我想在应用打开时删除它。可以吗?
  • 不,你永远不能删除它,阅读 SQLite 文档以了解它是什么。
  • 是的,我知道它是什么。到目前为止,我还没有在应用启动时将其删除,但我仍在测试中。
  • 如果我说我不在乎丢失数据怎么办?在这个应用程序中,它更多地用作从互联网下载的数据的缓存。
  • 在我们的数据库多次损坏后(主要是由于 -shm 文件以某种方式不同步,或者由于某些突然的应用程序崩溃等,即使同步模式设置为 FULL),我可以安全地说 WAL 有几个好处,但像玻璃一样脆弱。考虑到我们在过去几个月收到的投诉数量,我现在希望回到 DELETE 模式。
【解决方案2】:

我在 iOS 7 中看到了很多关于 WAL 的负面报道。我不得不在几个项目中禁用它,直到我有时间更彻底地探索这些问题。

我不会删除日志文件,但您可以选择清理 SQLite 文件,这将导致 SQLite “消耗”日志文件。当您将NSPersistentStore 添加到NSPersistentStoreCoordinator 时,您可以通过将NSSQLiteManualVacuumOption 添加为选项的一部分来执行此操作。

如果这最终很耗时,那么我建议禁用 WAL。我还没有看到禁用它的任何不良影响。

【讨论】:

  • 谢谢马库斯!关于删除 WAL 文件是否可以,我仍然需要找到一个真正明确的答案。在我看来,它会很好,因为它的工作方式。尽管如此,你关于吸尘的想法给了我一些思考。禁用 WAL 听起来像是一种可能性 - 但我需要再次检测。我认为我看到 WAL 的性能得到了极大的改善(当读取正在进行时,我也写了很多东西)。
  • 我不会删除日志,因为该文件中存在写入不完整的风险。我要么吸尘,要么关掉它。删除有很多理论上的风险。
  • 如果我说我不在乎丢失数据怎么办?在这个应用程序中,它更多地用作从互联网下载的数据的缓存。
  • 那我会同时删除sqlite文件。我见过替换 sqlite 而不是替换日志会损坏 sqlite 文件的情况。按理说反之亦然。我还没有看到相反的情况,但只有 6 个月左右:)
  • 呵呵,说得有道理。谢谢你的马库斯!非常感谢。
【解决方案3】:

您永远不应该删除 sqlite WAL 文件,它包含尚未写入实际 sqlite 文件的事务。而是强制数据库检查点,然后为您清理 WAL 文件。

在 CoreData 中,最好的方法是使用 DELETE journal mode pragma 打开数据库。这将检查点,然后为您删除 WAL 文件。

NSDictionary *options = @{ NSSQLitePragmasOption: @{ @"journal_mode": @"DELETE"}};
[psc addPersistentStoreWithType:NSSQLiteStoreType
                  configuration:nil
                            URL:_storeURL
                        options:options
                          error:&localError];

出于理智考虑,您应该确保在执行此操作时只有一个与持久性存储的连接,即单个持久性存储协调器中只有一个持久性存储实例。

在您的特定情况下,您可能希望使用 TRUNCATE 或 OFF 进行初始数据库导入,并切换到 WAL 进行更新。

http://www.sqlite.org/pragma.html#pragma_journal_mode

【讨论】:

  • 问题在于加载 1GB WAL 文件所需的时间太长,所以没有办法做到这一点。而且我不关心任何尚未提交的数据。它纯粹用作缓存。这会改变你的答案吗?
  • 我的答案的第二部分仍然有效,使用 OFF 所以根本没有日志。从而完全避免了这个问题。不利的一面是,如果数据库处于提交过程中,它会崩溃。但如果这只是一个缓存,你可以随时重建它。
【解决方案4】:

在这个线程上有很好的答案,但我添加了这个链接到 Apple 官方 QnA 关于 iOS7 Core Data 中的日志模式: https://developer.apple.com/library/ios/qa/qa1809/_index.html

他们提供不同的解决方案:

要安全备份和恢复 Core Data SQLite 存储,您可以执行以下操作 以下:

使用 NSPersistentStoreCoordinator 类的如下方法,而不是 比文件系统 API 来备份和恢复 Core Data 存储:

- (NSPersistentStore *)migratePersistentStore:(NSPersistentStore *)store toURL:(NSURL *)URL options:(NSDictionary *)options withType:(NSString *)storeType error:(NSError **)error 

请注意,这是我们推荐的选项。

将商店添加到 如果您必须复制存储文件,则为持久存储协调器。 清单 1 是显示如何执行此操作的代码:

清单 1 添加持久存储时使用回滚日志模式

NSDictionary *options = @{NSSQLitePragmasOption:@{@"journal_mode":@"DELETE"}}; if (! [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                            configuration:nil
                                            URL:storeURL
                                            options:options
                                            error:&error]) 
{
    // error handling. 
} 

对于以 WAL 模式加载的存储,如果主存储文件和 对应的-wal文件 存在,使用回滚日志模式将存储添加到持久化 store coordinator 将强制 Core Data 执行检查点 操作,将-wal文件中的数据合并到store文件中。 这实际上是 Core Data 执行检查点操作的方式。 另一方面,如果 -wal 文件不存在,使用这个 添加商店的方法不会导致任何异常,但是 丢失的 -wal 文件中记录的事务将丢失。

非常重要的编辑

如果您的一些用户在iOS 8.1 上并且您选择了第一个解决方案(Apple 推荐的那个),请注意他们的many-to-many 数据关系将被完全丢弃。丢失。已删除。在整个迁移的数据库中。

这是iOS 8.2 中明显修复的一个讨厌的错误。更多信息在这里http://mjtsai.com/blog/2014/11/22/core-data-relationships-data-loss-bug/

【讨论】:

    【解决方案5】:

    几件事:

    1. 您当然可以删除 WAL 文件。您将丢失任何尚未被检查点返回到主文件的已提交事务。 (因此违反了 ACID 的“持久性”部分,但也许你不在乎。)

    2. 您可以使用 journal_size_limit 杂注控制磁盘上 WAL 文件的大小(如果它困扰您)。您可能还想更频繁地手动检查点。请参阅此处的“避免过大的 WAL 文件”:https://www.sqlite.org/wal.html

    3. 我不喜欢所有对 WAL 模式的迷信抨击。 WAL 模式更快、更并发、更简单,因为它省去了所有与回滚日志相关的锁定级别恶作剧(以及大多数“数据库繁忙”问题)。 WAL 模式几乎在任何情况下都是正确的选择。 (唯一有问题的地方是不支持共享内存映射访问文件的闪存文件系统。在这种情况下,“非官方”SQLITE_SHM_DIRECTORY 编译指令可用于将 .shm 文件移动到不同类型的文件系统-- 例如 tmpfs -- 但这在 iOS 上不应该是一个问题。)

    【讨论】:

    • 我喜欢这个答案。 WAL 是正确的选择。它具有更好的并发特性。与日志模式相比,锁定数据库中的项目更少。
    • 由于这是最受好评的答案,我需要发表评论。您当然必须删除 WAL 文件。有关详细信息,请参阅我的答案。您不仅会失去耐用性,还会失去原子性和一致性部分。我同意其余的答案,但这应该更正。
    【解决方案6】:

    让我感到困惑的是,这里有多少人建议删除 WAL 文件是安全的,甚至没有看到他们的方向。

    The documentation 明确将此列为破坏数据库的官方方法之一。它并没有说删除热 WAL 可能会导致您丢失最近的交易或类似的良性事情。它说它可能会损坏数据库。

    为什么?因为应用程序可能在检查点操作的过程中崩溃了。发生这种情况时,除非与 WAL 中包含的新数据配对,否则数据库文件本身处于无效状态。

    所以答案是肯定的。 不要删除 WAL 文件。

    清除文件的方法是调用PRAGMA schema.wal_checkpoint(TRUNCATE);

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-08-07
      • 2022-10-25
      • 2013-02-04
      • 2021-01-13
      • 2014-08-09
      • 2023-02-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多