我有一个基于 SQLite 数据库的系统。每个客户端都有一个本地数据库,有时更新会从主服务器到达,只是一个小的 delta .db 文件。任务是使用 delta 文件合并到本地数据库,两者的架构是相同的。
对于我的数据库管理,我使用可以在此处找到的 fmdb 包装器。在主线程中,我保持与本地数据库的连接打开。增量文件到达后台,我想在后台进行合并以避免这可能导致的任何用户界面冻结。
至于合并本身,我发现的唯一选择是将增量数据库附加到本地数据库,然后插入/更新行,最后分离增量。这不像我预期的那样顺利。
代码说明:
- 每当准备好处理增量数据库(从服务器到达并保存在可读位置)时,都会在后台线程中调用onDeltaGenerated方法。
- deltaDBPath是 delta 数据库在文件系统中的绝对位置。
- db变量引用打开 FMDataBase 连接。
代码:
- (void)onDeltaGenerated:(NSNotification*)n {
NSString* deltaDBPath = [[n userInfo] objectForKey:@"deltaPath"];
@synchronized(db) {
[db executeUpdate:@"ATTACH DATABASE ? AS delta", deltaDBPath];
if ([db hadError]) {
NSLog(@" ****ERROR*** %d: %@", [db lastErrorCode], [db lastErrorMessage]);
} else {
NSLog(@"Delta attached from %@", deltaDBPath);
}
[db beginTransaction];
BOOL update1 = NO;
BOOL update2 = NO;
BOOL transaction = NO;
update1 = [db executeUpdate:@"INSERT OR REPLACE INTO equipment SELECT * FROM delta.equipment"];
if (!update1) {
NSLog(@" *** ERROR *** update 1 failed!");
NSLog(@" ****ERROR*** %d: %@", [db lastErrorCode], [db lastErrorMessage]);
}
update2 = [db executeUpdate:@"INSERT OR REPLACE INTO equipmentExt SELECT * FROM delta.equipmentExt"];
if (!update2) {
NSLog(@" *** ERROR *** update 2 failed!");
NSLog(@" ****ERROR*** %d: %@", [db lastErrorCode], [db lastErrorMessage]);
}
transaction = [db commit];
if (!transaction) {
NSLog(@" *** ERROR *** transaction failed!");
NSLog(@" ****ERROR*** %d: %@", [db lastErrorCode], [db lastErrorMessage]);
}
[db executeUpdate:@"DETACH DATABASE delta"];
if ([db hadError]) {
NSLog(@" ****ERROR*** %d: %@", [db lastErrorCode], [db lastErrorMessage]);
} else {
NSLog(@"Delta detached");
}
}
}
在第一次调用此方法后,一切似乎都很好,直到我尝试分离数据库。当我尝试这样做时,我收到以下错误:
2012-01-11 12:08:52.106 DBApp[1415:11507] Error calling sqlite3_step (1: SQL logic error or missing database) SQLITE_ERROR
2012-01-11 12:08:52.107 DBApp[1415:11507] DB Query: DETACH delta
2012-01-11 12:08:52.107 DBApp[1415:11507] ****ERROR*** 1: database delta is locked
我也尝试了相同的方法,但没有将插入插入事务,结果是相同的。另一件事是删除 @synchronized 子句,但也没有运气。我的猜测是,如果尝试从后台线程访问本地数据库连接时失败,那么它是如何设法附加和插入的?任何帮助表示赞赏。
编辑
我将代码移至主线程,因此现在只能从主线程访问数据库。问题依然存在。
编辑2
好的,所以在尝试了一切之后,我暂时放弃了这个,然后当第一个答案出现在这里时又回来了。令人惊讶的是,现在一切似乎都运行良好,所以我的代码一定是正确的。我怀疑这是锁定文件的不同线程的问题,因为我使用 XCode、SQLiteDatabaseBrowser 和我的应用程序打开数据库。即使lsof显示文件没有被锁定,我认为这是错误的,XCode 或 SQLiteDatabaseBrowser 正在锁定它。我认为问题解决了,从中吸取的教训是不要过多地推动 lsof 并更好地计划下次调试。