在同一数据库上使用两个 FMDB 队列(读/写)
Use two FMDB queues (read / write) on the same database
我相信我的用例相当普遍,但我找不到权威的答案。
我有一个在后台运行并将数据写入数据库的同步机制。此同步可能需要很长时间(我使用 FTS)。为此,我使用 FMDatabaseQueue
。当我想读取数据库时,我使用相同的队列进行查询。
现在,当同步进程已经将大量事务排入队列时,应用程序想要进行读取,它必须等待所有写入事务完成才能进行查询,因为这个是一个串行队列。代码可能如下所示:
FMDatabaseQueue *queue = [self getDatabaseQueue];
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Very slow process
[db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Very slow process
[db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Very slow process
[db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];
[queue inDatabase:^(FMDatabase *db) {
FMResultSet *resultSet = [db executeQuery:@"SELECT name..."];
}];
我想立即获得查询结果(即使未完成同步),而不是等待 UPDATE
完成。
我能否创建两个 FMDatabaseQueue
,一个用于写入查询,一个用于读取查询?如果读取查询恰好在写入事务的中间开始,会发生什么情况?
代码可能如下所示:
FMDatabaseQueue *writeQueue = [self getWriteDatabaseQueue];
FMDatabaseQueue *readQueue = [self getReadDatabaseQueue];
[writeQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Very slow process
[db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];
[writeQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Very slow process
[db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];
[writeQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Very slow process
[db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];
[readQueue inDatabase:^(FMDatabase *db) {
FMResultSet *resultSet = [db executeQuery:@"SELECT name..."];
}];
编辑:
此外,让我感到困惑的是文档,其中指出:
It has always been OK to make a FMDatabase object per thread. Just don't share a single instance across threads.
所以我从中了解到,我可以使用相同的数据库创建两个实例,但我只需要将其保留在它们自己的线程中。
不,您不想让两个 FMDatabaseQueue
与同一个数据库交互。 FMDatabaseQueue
的全部目的是提供一个队列,用于通过共享的单个串行队列协调来自不同线程的数据库调用。
不过,我想知道 "very slow process" 块中的代码。如果那不是特定于数据库的东西,那么它不应该在 inTransaction
and/or inDatabase
调用中。
NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init];
FMDatabaseQueue *databaseQueue = [FMDatabaseQueue databaseWithPath:path];
[backgroundQueue addOperationWithBlock:^{
// very slow process
[databaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];
}];
[backgroundQueue addOperationWithBlock:^{
// very slow process
[databaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];
}];
当这些排队时 运行,主队列可以进行自己的 databaseQueue
调用,使用 FMDatabaseQueue
确保以这样的方式进行协调它不会与上面的inTransaction
块同时发生:
[databaseQueue inDatabase:^(FMDatabase *db) {
FMResultSet *resultSet = [db executeQuery:@"SELECT name..."];
while (![resultSet next]) {
....
}
}];
显然,您应该管理您的后台任务,但是适合您的应用程序(我使用了并发操作队列,但您可以使用串行队列或调度队列或任何您想要的)。不要拘泥于上述 backgroundQueue
的细节,因为您的实施会有所不同。
关键观察是您不应该使用 databaseQueue
来管理您的 "very slow process" 任务以及数据库交互。从 inTransaction
和 inDatabase
调用中取出任何与数据库无关的内容。使用您自己的队列来管理您自己的非数据库相关代码。始终尽可能快地进出 databaseQueue
并让单个共享 FMDatabaseQueue
协调与多个线程发生的数据库交互。
我相信我的用例相当普遍,但我找不到权威的答案。
我有一个在后台运行并将数据写入数据库的同步机制。此同步可能需要很长时间(我使用 FTS)。为此,我使用 FMDatabaseQueue
。当我想读取数据库时,我使用相同的队列进行查询。
现在,当同步进程已经将大量事务排入队列时,应用程序想要进行读取,它必须等待所有写入事务完成才能进行查询,因为这个是一个串行队列。代码可能如下所示:
FMDatabaseQueue *queue = [self getDatabaseQueue];
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Very slow process
[db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Very slow process
[db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Very slow process
[db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];
[queue inDatabase:^(FMDatabase *db) {
FMResultSet *resultSet = [db executeQuery:@"SELECT name..."];
}];
我想立即获得查询结果(即使未完成同步),而不是等待 UPDATE
完成。
我能否创建两个 FMDatabaseQueue
,一个用于写入查询,一个用于读取查询?如果读取查询恰好在写入事务的中间开始,会发生什么情况?
代码可能如下所示:
FMDatabaseQueue *writeQueue = [self getWriteDatabaseQueue];
FMDatabaseQueue *readQueue = [self getReadDatabaseQueue];
[writeQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Very slow process
[db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];
[writeQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Very slow process
[db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];
[writeQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Very slow process
[db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];
[readQueue inDatabase:^(FMDatabase *db) {
FMResultSet *resultSet = [db executeQuery:@"SELECT name..."];
}];
编辑:
此外,让我感到困惑的是文档,其中指出:
It has always been OK to make a FMDatabase object per thread. Just don't share a single instance across threads.
所以我从中了解到,我可以使用相同的数据库创建两个实例,但我只需要将其保留在它们自己的线程中。
不,您不想让两个 FMDatabaseQueue
与同一个数据库交互。 FMDatabaseQueue
的全部目的是提供一个队列,用于通过共享的单个串行队列协调来自不同线程的数据库调用。
不过,我想知道 "very slow process" 块中的代码。如果那不是特定于数据库的东西,那么它不应该在 inTransaction
and/or inDatabase
调用中。
NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init];
FMDatabaseQueue *databaseQueue = [FMDatabaseQueue databaseWithPath:path];
[backgroundQueue addOperationWithBlock:^{
// very slow process
[databaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];
}];
[backgroundQueue addOperationWithBlock:^{
// very slow process
[databaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];
}];
当这些排队时 运行,主队列可以进行自己的 databaseQueue
调用,使用 FMDatabaseQueue
确保以这样的方式进行协调它不会与上面的inTransaction
块同时发生:
[databaseQueue inDatabase:^(FMDatabase *db) {
FMResultSet *resultSet = [db executeQuery:@"SELECT name..."];
while (![resultSet next]) {
....
}
}];
显然,您应该管理您的后台任务,但是适合您的应用程序(我使用了并发操作队列,但您可以使用串行队列或调度队列或任何您想要的)。不要拘泥于上述 backgroundQueue
的细节,因为您的实施会有所不同。
关键观察是您不应该使用 databaseQueue
来管理您的 "very slow process" 任务以及数据库交互。从 inTransaction
和 inDatabase
调用中取出任何与数据库无关的内容。使用您自己的队列来管理您自己的非数据库相关代码。始终尽可能快地进出 databaseQueue
并让单个共享 FMDatabaseQueue
协调与多个线程发生的数据库交互。