【问题标题】:SQLite simultaneous reading and writingSQLite同时读写
【发布时间】:2011-12-27 15:17:31
【问题描述】:

我读了很多主题,但无法弄清楚问题的答案:是否可以同时读写?

我有更新一些数据的后台线程,并且 UI 需要存储在 DB 中的一小部分数据。所以在 UI 线程中执行 SELECT 操作。但是当更新正在进行时它会阻塞。结果,UI 冻结了几秒钟。

有没有人在写的时候从数据库中读取成功?


可以在 iPhone 上读取和写入 DB。造成这种差异的原因是在原生 sqlite 函数上同步实现包装器吗?

【问题讨论】:

  • "所以在 UI 线程中执行了 SELECT 操作" -> 从 UI 线程中取出。
  • 有一些简单的操作比如SELECT name FROM some_table WHERE _id = 几秒钟不显示名字也好不了冻结

标签: android database multithreading sqlite


【解决方案1】:

在 Android 3.0 和更高版本的 SQLiteDatabases 支持 WAL 模式(预写日志记录):

如果未启用预写日志记录(默认),则不会 数据库上可能同时发生读取和写入 时间。在修改数据库之前,writer 隐式获取一个 数据库上的排他锁,阻止读者访问 数据库,直到写入完成。

相比之下,当启用预写日志记录时,写入操作 发生在允许继续读取的单独日志文件中 同时。在写入过程中,其他线程上的读者 将感知数据库的状态,就像它在写入之前一样 开始了。写入完成后,其他线程上的读取器将 感知数据库的新状态。

http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#enableWriteAheadLogging()

要在 WAL 模式下启动事务,请使用 beginTransactionNonExclusive() 而不是 beginTransaction()。 当 beginTransaction() 以 EXCLUSIVE 模式启动事务时, beginTransactionNonExclusive() 以 IMMEDIATE 模式启动一个事务

  • EXCLUSIVE 模式使用排他锁 (http://www.sqlite.org/lockingv3.html#excl_lock),这意味着除了 read_uncommitted 连接之外,没有其他数据库连接能够读取数据库,并且在事务完成之前,没有任何其他无异常连接能够写入数据库
  • IMMEDIATE 模式使用保留锁 (http://www.sqlite.org/lockingv3.html#reserved_lock),这意味着没有其他数据库连接能够写入数据库或执行 BEGIN IMMEDIATE 或 BEGIN EXCLUSIVE,但是其他进程可以继续从数据库中读取。

简单来说:在 IMMEDIATE 模式下调用 beginTransactionNonExclusive() ,我们可以在另一个线程正在写入时读取(写入事务开始之前的状态,因为我们不会使用 read_uncommitted 连接 -> http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Dirty_reads)。

【讨论】:

  • WAL 模式将使我无法支持低于 API 16 的所有 API。还有其他解决方案吗?
  • 从 API 11 on (Honeycomb) 开始支持 WAL 模式。 API 11 和 API 16 之间的区别在于,您实际上可以使用 Context.MODE_ENABLE_WRITE_AHEAD_LOGGING 模式在 API 16 中以 WAL 模式打开数据库,而您必须以 API11-15 中的可用模式之一打开它,然后切换到 WAL 模式使用 SQLiteDatabase.enableWriteAheadLogging()。在 WAL 模式下打开要快得多,但这就是你所得到的。在 Honeycomb 之前,没有 WAL 模式,这意味着应用程序运行速度会变慢,但“应该”至少没有其他副作用。
【解决方案2】:

从 API 11 开始,Android 支持 WAL 模式。它在事务期间保持原始数据不受影响,因此其他线程可以在事务运行时读取。关于 WAL 模式的更多细节可以查看我的文章:

http://www.skoumal.net/en/parallel-read-and-write-in-sqlite/

您还应该避免在 UI 线程中运行数据库查询。它总是会变得迟缓并阻塞您的用户界面。

【讨论】:

    【解决方案3】:

    你不能同时读和写。 SQLite 是一个无服务器、基于文件的数据库。

    来自SQLite FAQ:

    “当任何进程想要写入时,它必须在更新期间锁定整个数据库文件。但这通常只需要几毫秒。其他进程只是等待写入者完成然后继续他们的业务。其他嵌入式 SQL 数据库引擎通常只允许单个进程一次连接到数据库。”

    【讨论】:

    • 投了反对票,因为答案是错误的。从 Honeycomb 开始(在给出答案时已经出来了),可以“同时”读/写(见我自己的答案)
    • 我在原始问题中没有看到任何“in Honeycomb”
    • 问题是:“可以同时读写吗?”,你的答案是:“你不能同时读写”。答案是错误的,因为如果您使用的是 Honeycomb 或更高版本,则可以。
    【解决方案4】:

    不能同时读写。但是,如果您的 SQLite 数据库类设置正确(您的数据库和助手类的单个实例),不同的线程应该能够同步获取数据库连接,这样就不会出现任何明显的延迟。

    听起来您正在尝试使用您的 UI 线程进行后端工作(写入数据库)。你不应该这样做。创建一个 AsyncTask 来处理它,而不是让你的 UI 线程处理它。

    请参阅SQLiteOpenHelper 文档。这是之前的一篇文章也谈到了这一点:What are the best practices for SQLite on Android?

    【讨论】:

    • It is not possible to read & write simultaneously -> 你听说过WAL 模式吗?
    • @tuancoltech - WAL 模式是在 2018 年推出的 OS 9 的 Android 上引入的。这个答案是在 2011 年写的。是的,事情发生了变化。 ;) 出于历史目的而保留答案。
    猜你喜欢
    • 1970-01-01
    • 2012-08-07
    • 1970-01-01
    • 1970-01-01
    • 2011-05-08
    • 2011-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多