【问题标题】:how to debug locked sqlite3 database如何调试锁定的sqlite3数据库
【发布时间】:2012-12-14 04:44:42
【问题描述】:

我正在 iOS 上编写一个使用 fmdatabase 包装的 sqlite3 的应用程序。我遇到的问题是,在某些时候,我的程序卡在 FMDatabase 库内的循环中,特别是一个调用 sqlite3_step 并发现数据库繁忙的函数,然后一遍又一遍地重试。

我正在寻找一般的调试工具和技巧,因为在这里给出我的整个设置会太过分了。一些可能会产生影响的事情,我打开了一个数据库句柄,该句柄指向一个已经在另一个线程中拥有句柄的数据库。 sqlite3_threadsafe() 返回 2,所以我知道它已启用。我还通过一个非常简单的选择和更新语句测试了这个新连接。当我让我的程序运行时,当它尝试在数据库上运行更新时,我卡住了。

我的程序自己创建的更新语句没有错误,因为当我不打开两个连接时,此查询运行良好。然而,我看不出哪里出错了……

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

【问题讨论】:

  • 你提交更新了吗?另见the unlock notify scheme
  • fmdatabase 调用 sqlite3_finalize 但不提交。我承诺了,但似乎没有任何区别

标签: ios sqlite


【解决方案1】:

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

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

SQLite 是线程安全的这一事实并不意味着多个线程可以同时写入它——这意味着它有一种处理来自多个线程的访问的方式——即 (a) 允许超时/重试,并且 (b ) 以在数据库当前持有锁时返回有用的错误 (SQLITE:Busy)。也就是说,threadsafe 无非就是“多个线程可以以一种不会因同时访问而导致数据损坏的方式访问这些数据。”

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

把它想象成 iOS 处理多任务的方式(至少在我写这篇文章的时候是 iOS 5)——实际上它所做的是让其他应用程序暂停,然后返回它们。这样做的效果是(a)由于 CPU 使用率较低,电池寿命要长得多,并且(b)您不必每次启动应用程序时都从头开始。这很好,但是在 iOS 中使用的“多任务”这个词在技术上与其他环境(甚至是 Mac OS X)中的“多任务”并不相同。

SQLite 也是如此。他们有“并发”支持吗?好吧,但是他们定义“并发”这个词的方式并不是 DB 世界中其他人定义“并发”的方式。

没有人真的是错的,但在这种情况下,它会增加实现的混乱。

【讨论】:

  • 是的,我必须用自己的锁来保护数据库,我试图避免但无法...
  • 现在他们有了一种叫做 WAL 的东西。
  • 预写日志有助于减轻症状,但仍不能处理多个并发写入的情况。
  • 这是我在任何地方都能找到的关于 SQLite 多线程的最简洁和有用的概述。非常有帮助,谢谢。
  • @Richard - 谢谢。我已经多年没有使用 SQLite 了,所以我希望它仍然是一个准确的 SQLite 实现的答案。
猜你喜欢
  • 2015-11-22
  • 1970-01-01
  • 1970-01-01
  • 2011-05-05
  • 2012-10-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多