2011-04-20 33 views
4

我正在编写一个使用由fmdatabase包装的sqlite3的iOS应用程序。我遇到了这样的问题:在某些时候,我的程序停留在FMDatabase库的一个循环中,特别是一个调用sqlite3_step并发现数据库正忙的函数,然后一遍又一遍地重试。如何调试锁定的sqlite3数据库

我正在寻找一般的调试工具和技巧,因为这将是太多,给我在这里的整个设置。有些事情可能会造成影响,我打开了一个数据库句柄来处理已经在另一个线程中有句柄的数据库。 sqlite3_threadsafe()返回2,所以我知道它的启用。我也通过制作一个非常简单的选择和更新语句来测试这个新的连接。当我让我的程序运行,并尝试运行数据库更新时,我卡住了。

我的程序自己创建的update语句没有错,因为当我不打开两个连接时,这个查询运行正常。然而,我没有看到我可能会出错的地方...

任何帮助或提示,我可能会错的地方将不胜感激。

+0

您是否提交了更新?另请参阅[解锁通知方案](http://www.sqlite.org/unlock_notify.html) – Benoit 2011-04-20 15:32:16

+0

fmdatabase调用sqlite3_finalize但不提交。我承诺它,但似乎没有任何区别 – Ying 2011-04-20 16:35:34

回答

21

SQLite在写入操作期间(即,在任何表上发生写入,其他写入,同时任何地方的任何表都不会发生)时,SQLite锁定整个数据库。某些数据库通过表级锁提供并发写入,或者有时通过行级锁提供并发写入。为了与SQLite的实现形成鲜明对比,表级锁定基本上意味着当您将数据写入给定的表时,其他线程不能同时写入该表中的任何记录(但是,写入其他表可以在某些情况下同时发生)。同样,行级锁进一步强化了它,并且只允许涉及的必要行被锁定,允许从多个线程同时写入同一个表。这里的想法是最大限度地减少写入操作需要锁定的数据量,这可以有效地增加数据库中可能的并发写入数量,并且取决于您的实现/如何使用数据库,这可以显着增加吞吐量。

现在,回到你的问题......

说SQLite是线程安全的,并不意味着多个线程可以同时写它的事实 - 这意味着它从多个线程处理访问的方式 - (a)允许超时/重试,以及(b)当数据库当前拥有一个锁时,返回一个有用的错误(SQLITE:Busy)。也就是说,线程安全只不过意味着“多线程可以以不会因同时访问而导致数据损坏的方式访问此数据。”

基本上,在代码中的某个地方,一个线程试图在另一个线程释放其对数据库的锁之前进行更新。这是SQLite常见的障碍,因为作者/文档会告诉你SQLite可以像冠军一样处理并发。现实情况是,SQLite认为“并发性支持”等于试图非常快速地使数据库上的锁只能保持很短的时间,因此在超时之前释放数据库上的锁。在很多情况下,这很好,永远不会妨碍你。但是,拥有非常短暂的锁与实际上允许从多个线程并发写入的方式不一样

想象一下,它像iOS的多任务处理方式(至少在我写这篇文章的时候是iOS 5) - 真的是让其他应用程序暂停并返回给它们。这具有以下效果:(a)由于CPU利用率较低,电池寿命更好;(b)每次启动时都不必从头开始创建应用程序。这很棒,但在iOS中使用的实际单词“多任务”在技术上并不意味着与其他环境(甚至是Mac OS X)中的“多任务”相同。

SQLite的方式是一样的。他们有“并发”支持吗?好吧,但他们定义单词“并发性”的方式并不是数据库世界其余部分定义“并发性”的方式。

没有人真的是错的,但在这样的情况下,它增加了执行混乱。

+0

是的,我不得不保护数据库与我自己的锁,我试图避免,但不能... – Ying 2011-04-27 15:33:49

+0

这一定会做到这一点。 – jefflunt 2011-04-27 21:50:26

+2

现在他们有了一种叫做WAL的东西。 – Alex 2013-01-17 21:17:58