我在我的 Android 项目中使用 ORMLite。我知道 Sqlite 负责文件级锁定。许多线程可以读取,一个可以写入。锁可以防止不止一次的写入。谁能解释一下如果一个线程正在更新某个记录而另一个线程正在尝试读取该记录会发生什么?线程(试图读取)是否会获得过时的数据?或者它会被锁定直到第一个线程完成它的写操作?据我所知,有 4 个事务隔离级别:可序列化、可重复读取、已提交读取、未提交读取。有没有办法在 SQLite 或 ORMLite 中改变它?
2 回答
SQLite 有 5 种不同的锁定级别 - http://www.sqlite.org/lockingv3.html:未锁定、共享、保留、挂起、独占。这个问题的重要锁是共享锁:
“共享 - 数据库可以读取但不能写入。任意数量的进程可以同时持有共享锁,因此可以有许多同时读取者。但是不允许其他线程或进程写入数据库文件,而一个或更多共享锁处于活动状态。”
锁是表级的(因此在对数据库中的单行执行某些操作时 - 整个表被锁定)。
因此,当您选择数据时,不允许其他进程更改数据。读取数据的加锁步骤为:UNLOCKED→PENDING→SHARED→UNLOCKED(可以在事务中运行select)。因此,不会发生您正在选择某些东西而有人会更改数据的情况。
您的问题是,如果您要更新数据库并在同一张表上进行选择,会发生什么情况。在自动提交模式下,写入/更新的锁定机制是:UNLOCKED →PENDING →SHARED →RESERVED →PENDING →EXCLUSIVE →UNLOCKED。在独占锁中,没有新的读取器(连接)可以连接到数据库。一次只能存在一个独占锁。然后,SQLite 将等到所有其他来自读取连接的 PENDING 锁被释放,并阻止任何新的锁。此时,它将开始写入数据。
所以,我的回答是——只要更新过程没有完成,你的其他进程当然会得到旧数据。一定要在事务中运行更新,这样就不会出现数据不一致的情况。SQLite 是 ACID 兼容的,因此不应该发生您获得部分更新和不一致数据的情况。
关于这方面的一本好书是“SQLite 权威指南”,尤其是交易章节。
SQLite 支持在编译时和运行时可选择的几个不同的隔离级别。
我假设 Android 的 SQLite 默认处于序列化模式。在多线程访问方面,它在文件系统级别进行读/写锁定,以允许多个读取器,但一次只允许一个写入器:
但是,ORMLite强烈建议并努力维护与数据库的单个连接,因此锁定问题可能无关紧要。
更具体地说,如果一个线程正在更新记录而另一个线程正在读取,那么它就是一种竞争条件。读取线程将在更新之前或更新完成之后获取记录。但是,读者不会得到部分更新的数据。但我怀疑你知道这一点。
就 SQLite 支持的隔离级别而言,默认值是如前所述的Serialized,但它看起来至少在某种程度上支持未提交读。
您可以使用ORMLite的Dao.executeRaw()方法来启用它:
dao.executeRaw("PRAGMA read_uncommitted = True;");
但是,我对此没有任何经验,我也不确定如果在同一个查询中多次访问它,它是否会为您提供更新的行的一致视图。它可能与写事务期间的一致视图有关。
在阅读了这个类似的问题之后,我不确定如果在同一个查询中多次访问它,是否可以保证表中的行的相同视图。