iOS SQLite 性能缓慢

5

我在我的iOS应用程序中使用SQLite,并且在用户与UI交互时需要进行大量的保存/加载操作。这是一个问题,因为它使UI非常抖动和缓慢。

我尝试在另一个线程中执行操作,但我认为在SQLite中不可能实现。如果我这样做,我会经常收到SQLITE_BUSY和SQLITE_LOCKED错误代码。

是否有一种在多线程中完成此操作而不出现这些错误代码的方法,或者我应该放弃SQLite?


1
你尝试过调整你使用的查询并查看查询计划以确定是否需要任何索引吗? - Frederick Cheung
2
当您进行多次写操作时,是否使用了事务?您应该这样做——这会有巨大的影响。 - Hot Licks
Frederick Cheung:我正在使用多个写入和检索操作。我会研究一下是否可以使用索引来帮助,但是要插入的记录很多,所以我不认为索引会有帮助。Hot Licks:当使用多个写入操作时,我没有进行事务处理。在使用相同锁定的数据库中,我应该为每个写入操作执行事务处理吗? - VTS12
你应该将一组写操作放在一个事务中。否则每个写操作都会启动事务、进行写入并结束事务,而这其中最耗时的是结束事务的步骤。(这在 SQLite 文档中已经明确阐述了)。 - Hot Licks
6个回答

3

完全可以实现,只需要在后台线程中对SQLite的访问进行串行化。

我在这篇最近的问题上的回答应该能指导您正确地方向。

正如其他地方提到的,SQLite对于并发读取是可以的,但对于写入则在数据库级别上加锁。这意味着如果您在不同的线程中进行读取和写入,您将获得SQLITE_BUSY和SQLITE_LOCKED错误。

避免这种情况的最基本方法是将所有的DB访问(读和写)串行化,可以使用调度队列或具有1个并发的NSOperationQueue。由于此访问不在主线程上进行,因此不会影响您的UI。

这显然会阻止读取和写入重叠,但也会阻止同时读取。目前尚不清楚这是否会对性能造成影响。

要初始化如上所述的队列:

NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init];

[backgroundQueue setMaxConcurrentOperationCount:1];

然后,您可以根据需要将操作添加到队列中。

我尝试过以这种方式去做,但仍然遇到了错误。现在我正在使用调度队列。 - VTS12
你现在一切都正常工作了吗?一个调度队列也应该可以。 - paulbailey
不,使用调度队列后,我经常收到SQLITE_BUSY和SQLITE_LOCKED错误代码。还有一些奇怪的行为,比如视图提前弹出等。 - VTS12
我们需要看到您的调度队列周围的代码。根据苹果文档,"提交给串行队列的块按FIFO顺序逐个执行"。 - paulbailey
问题在于即使队列已经完成,我仍然会收到SQLITE_BUSY和SQLITE_LOCKED的错误。 - VTS12
显示剩余7条评论

2

把所有操作放在一个专用的SQLite线程中,或者使用一次只执行一个操作的操作队列是很好的解决方案,特别是为了解决您的UI卡顿问题。另一种技术(可能对抖动没有帮助)是发现那些错误代码,然后简单地循环重试更新,直到获得成功的返回代码。


1
将SQLite置于WAL模式。然后读取不会被阻塞。但写入不是这样的-您需要对它们进行序列化。有各种方法可以实现它。其中之一是由SQLite提供的-WAL挂钩可用于发出信号,表示下一个写入可以开始。
WAL模式通常应该提高应用程序的性能。大多数事情都会变得更快。根本不会阻止读取。只有大型事务(几MB)会变慢。通常没有什么戏剧性的。

0
不要放弃SQLite。你绝对可以在与UI线程不同的线程中完成它,以避免变慢。只需确保一次只有一个线程访问数据库即可。SQLite在处理并发访问时表现不佳。

1
对于并发的读取来说没问题,但是由于比数据库级别更细粒度的锁定机制过于复杂,所以更新操作会锁定其他所有操作。 - Donal Fellows
我的问题是我无法控制用户何时访问/写入数据。例如,在一个视图上,它将读取和保存数据,但然后他们可能会单击详细信息并推送一个新视图,该视图也希望读取/写入数据。不确定如何确保一个线程同时访问数据库。 - VTS12

0

关闭:

你有没有看过:FMDB 它是一个sqlite封装库,而且是线程安全的。我在所有的sqlite项目中都使用它。


0

我建议使用基于sqlite的Core Data。我在多线程环境中使用它。这里有一份关于Concurrency with Core Data的指南。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接