删除sqlite的WAL文件是否安全?
在iOS应用程序中,Core Data有一个奇怪的问题,WAL文件有时会变得很大 (〜1GB)。 看来还有其他人有这个问题(例如, 当插入〜5000行时,Core Data的sqlite-wal文件会获得MASSIVE(> 7GB) )。
我最初的想法是在应用程序启动时删除WAL文件。 从阅读关于此事的sqlite文档看来,这将是好的。 但是,有没有人知道这样做的缺点?
我当然想深入了解WAL文件为什么会变得如此之大,但是我现在还不能深究,并且想深入研究这个问题。
值得指出的是我的Core Data数据库更像是一个caching。 因此,如果我丢失了WAL中的数据,这并不重要。 我真正需要知道的是,如果我删除WAL,数据库是否会完全损坏? 我的怀疑是否定的,否则WAL不符合其目的之一。
WAL模式有问题,不要使用它。 问题各不相同,但是报告的大小非常大,其他问题包括迁移期间的失败(使用NSPersistentStoreCoordinators migratePersistentStore)和导入iCloud事务日志期间的失败。 所以,虽然有报告的好处,直到这些错误被修复,使用WAL模式可能是不明智的。
而且,您不能删除写入日志,因为它包含最新的数据。
设置数据库使用回滚日志模式,我想你会发现你在加载大量数据时不再有这些非常大的文件。
这是解释WAL如何工作的摘录。 除非您可以保证您的应用程序运行检查点,否则我不会看到如何删除WAL文件,而不存在删除已提交事务的风险。
如何WAL工作
传统的回滚日志通过将原始未更改的数据库内容的副本写入单独的回滚日志文件,然后将更改直接写入数据库文件而工作。 如果发生崩溃或ROLLBACK,则回滚日志中包含的原始内容将被回放到数据库文件中,以将数据库文件恢复到原始状态。 在回滚日志被删除时发生COMMIT。
WAL方法反转了这一点。 原始内容保存在数据库文件中,并将更改附加到单独的WAL文件中。 当指示提交的特殊logging被附加到WAL时,发生COMMIT。 因此,COMMIT可以在没有写入原始数据库的情况下发生,这允许读者在原始未改变的数据库中继续操作,同时将改变同时提交到WAL中。 多个事务可以附加到单个WAL文件的末尾。
检查点
当然,我们希望最终将所有附加在WAL文件中的事务转移回原始数据库。 将WAL文件事务移回数据库称为“检查点”。
另一种思考回滚和预写日志差别的方法是在回滚日志方法中,有两个原始操作,即读和写,而对于预写日志,现在有三个原始操作:读,写和检查点。
默认情况下,当WAL文件达到1000页的阈值大小时,SQLite自动执行检查点。 (SQLITE_DEFAULT_WAL_AUTOCHECKPOINT编译时选项可用于指定不同的默认值。)使用WAL的应用程序无需执行任何操作就可以执行这些检查点。 但是,如果他们想要,应用程序可以调整自动检查点阈值。 或者他们可以closures自动检查点并在空闲时间或在单独的线程或进程中运行检查点。
我已经看到了很多关于iOS 7中WAL的负面报道。我不得不在几个项目上禁用它,直到我有时间更全面地探讨这些问题。
我不会删除日志文件,但可以使用SQLite文件进行清理,这将导致SQLite“使用”日志文件。 您可以通过在将NSSQLiteManualVacuumOption
添加到NSSQLiteManualVacuumOption
时添加NSSQLiteManualVacuumOption
作为选项的NSPersistentStore
来执行此NSPersistentStoreCoordinator
。
如果这最终耗时,那么我会build议禁用WAL。 我还没有看到任何不良影响,以禁用它(还)。
在这个主题上有相当好的答案,但我添加了这个链接到苹果官方QnA关于iOS7核心数据日记模式: https : //developer.apple.com/library/ios/qa/qa1809/_index html的
他们给出不同的解决scheme:
要安全地备份和恢复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文件都存在,则使用回滚日志模式将商店添加到持久存储区协调器将强制Core Data执行检查点操作, -wal文件中的数据存储到存储文件中。 这实际上是执行检查点操作的核心数据方式。 另一方面,如果-wal文件不存在,使用这种方法来添加存储不会导致任何exception,但logging在missing -wal文件中的事务将会丢失。
非常重要的编辑
如果您的某些用户使用的是iOS 8.1
并且您select了第一个解决scheme(Apple推荐的解决scheme),请注意,他们many-to-many
数据关系将被完全丢弃。 丢失。 删除。 在整个迁移的数据库中。
这是一个很明显在iOS 8.2
修复的恶意错误。 更多信息在这里http://mjtsai.com/blog/2014/11/22/core-data-relationships-data-loss-bug/
几件事:
-
你当然可以删除WAL文件。 您将丢失任何未被检查点返回到主文件的已提交事务。 (因此违反了ACID的“耐久性”部分,但也许你不在意)。
-
您可以使用journal_size_limit附注控制磁盘上WAL文件的大小(如果它困扰你的话)。 您可能也想更频繁地手动检查点。 请参阅“避免过大的WAL文件”: https : //www.sqlite.org/wal.html
-
我不喜欢所有迷信的WAL模式。 WAL模式更快,更协同,更简单,因为它不需要使用回滚日志的所有locking级别(以及大多数“数据库繁忙”问题)。 WAL模式在几乎所有情况下都是正确的select。 (唯一有问题的地方是不支持共享内存映射的文件访问的闪存文件系统,在这种情况下,可以使用“非官方的”SQLITE_SHM_DIRECTORY编译指令将.shm文件移动到不同types的文件系统 – 例如tmpfs – 但这不应该是iOS的问题。)
你不应该删除sqlite WAL文件,它包含尚未写入实际sqlite文件的事务。 而是强制数据库检查点,然后为您清理WAL文件。
在CoreData中,最好的方法是使用DELETE日志模式编译指示来打开数据库。 这将检查点,然后删除WAL文件给你。
NSDictionary *options = @{ NSSQLitePragmasOption: @{ @"journal_mode": @"DELETE"}}; [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:_storeURL options:options error:&localError];
为了干净起见,当你这样做时,你应该确保你只有一个到持久存储的连接,即在一个持久存储协调器中只有一个持久存储实例。
在您的特定情况下,您可能希望将TRUNCATE或OFF用于初始数据库导入,并切换到WAL进行更新。