gpt4 book ai didi

sqlite - 如何防止 SQLite 数据库锁定?

转载 作者:IT王子 更新时间:2023-10-29 06:21:37 24 4
gpt4 key购买 nike

从sqlite FAQ我知道:

Multiple processes can have the same database open at the same time. Multiple processes can be doing a SELECT at the same time. But only one process can be making changes to the database at any moment in time, however.



所以,据我所知,我可以:
1)从多个线程读取数据库(SELECT)
2)从多线程(SELECT)读取数据库并从单线程(CREATE、INSERT、DELETE)写入

但是,我读到了 Write-Ahead Logging这提供了更多的并发性,因为读者不会阻止作者,而作者不会阻止读者。读和写可以同时进行。

最后,当我发现 it 时,我完全糊涂了。 ,当指定时:

Here are other reasons for getting an SQLITE_LOCKED error:

  • Trying to CREATE or DROP a table or index while a SELECT statement is still pending.
  • Trying to write to a table while a SELECT is active on that same table.
  • Trying to do two SELECT on the same table at the same time in a multithread application, if sqlite is not set to do so.
  • fcntl(3,F_SETLK call on DB file fails. This could be caused by an NFS locking issue, for example. One solution for this issue, is to mv the DB away, and copy it back so that it has a new Inode value


所以,我想为自己澄清一下,我什么时候应该避免锁?我可以从两个不同的线程同时读取和写入吗?谢谢。

最佳答案

对于那些与 一起工作的人Android API :

Locking in SQLite is done on the file level which guarantees locking of changes from different threads and connections. Thus multiple threads can read the database however one can only write to it.



更多关于在 SQLite 中的锁定可以阅读 SQLite文档,但我们最感兴趣的是 OS Android 提供的 API。

可以从单个和多个数据库连接使用两个并发线程进行写入。由于只有一个线程可以写入数据库,因此有两种变体:
  • 如果您从一个连接的两个线程写入,则一个线程将
    等待另一个完成写作。
  • 如果您从不同连接的两个线程写入,则会出现错误
    将 - 您的所有数据都不会写入数据库并且
    应用程序将被中断
    SQLiteDatabaseLockedException。很明显,
    应用程序应始终只有一份
    SQLiteOpenHelper(只是一个开放的连接)否则
    SQLiteDatabaseLockedException 可能随时发生。

  • 单个 SQLiteOpenHelper 上的不同连接

    每个人都知道 SQLiteOpenHelper 有 2 个方法提供对数据库的访问 getReadableDatabase() getWritableDatabase() , 分别读取和写入数据。然而,在大多数情况下,存在一种真正的联系。此外,它是同一个对象:

    SQLiteOpenHelper.getReadableDatabase()==SQLiteOpenHelper.getWritableDatabase()



    这意味着读取数据的方法的使用没有区别。然而,还有另一个更重要的未公开问题——在 SQLiteDatabase 类内部有自己的锁——变量 mLock。在对象 SQLiteDatabase 级别锁定写入,并且由于只有一个 SQLiteDatabase 副本用于读取和写入,因此数据读取也会被阻止。在事务中写入大量数据时,它更显眼。

    让我们考虑一个 示例 这种应用程序应该在第一次启动时在后台下载大量数据(大约 7000 行包含 BLOB)并将其保存到数据库中。如果数据保存在事务中,那么保存大约需要。 45 秒,但用户无法使用该应用程序,因为任何阅读查询都被阻止。如果数据被分成小部分保存,那么更新过程会拖延相当长的时间(10-15 分钟),但用户可以不受任何限制和不便地使用该应用程序。 “双刃剑”——快速或方便。

    由于添加了以下方法,Google 已经修复了与 SQLiteDatabase 功能相关的部分问题:

    beginTransactionNonExclusive() – 在“即时模式”下创建交易。

    yieldIfContendedSafely() – 临时占用事务以允许其他线程完成任务。

    isDatabaseIntegrityOk() – 检查数据库完整性

    请在 documentation 中阅读更多详细信息.

    但是,对于旧版本的 Android,此功能也是必需的。

    解决方案

    首先应该关闭锁定并允许在任何情况下读取数据。

    SQLiteDatabase.setLockingEnabled(false);



    使用内部查询锁定取消 – 在 java 类的逻辑级别上(与 SQLite 方面的锁定无关)

    SQLiteDatabase.execSQL(“PRAGMA read_uncommitted = true;”);



    允许从缓存中读取数据。事实上,改变了隔离级别。应该为每个连接重新设置此参数。如果有多个连接,那么它只会影响调用此命令的连接。

    SQLiteDatabase.execSQL(“PRAGMA synchronous=OFF”);



    将写入方法更改为数据库 - 无需“同步”。激活此选项时,如果系统意外故障或电源关闭,数据库可能会损坏。但是,根据 SQLite 文档,如果未激活该选项,某些操作的执行速度会快 50 倍。

    不幸的是,并非所有 PRAGMA Android 支持,例如“ PRAGMA 锁定模式 = 正常 ”和“ PRAGMA journal_mode = OFF ”和其他一些不受支持。在尝试调用 PRAGMA 数据时,应用程序失败。

    在该方法的文档中 setLockingEnabled 据说此方法仅在您确定数据库的所有工作都是从单个线程完成的情况下才推荐使用。我们应该保证一次只持有一笔交易。此外,应使用即时事务而不是默认事务(独占事务)。在旧版本的 Android(低于 API 11)中,没有选项可以通过 java 包装器创建即时事务,但 SQLite 支持此功能。要在立即模式下初始化事务,应直接对数据库执行以下 SQLite 查询,例如通过方法 execSQL:

    SQLiteDatabase.execSQL(“begin immediate transaction”);



    由于事务是由直接查询初始化的,因此它应该以相同的方式完成:

    SQLiteDatabase.execSQL(“commit transaction”);



    然后 TransactionManager 是唯一需要实现的东西,它将启动和完成所需类型的事务。 TransactionManager 的目的是保证所有的更改查询(插入、更新、删除、DDL 查询)都来自同一个线程。

    希望这对 future 的游客有所帮助!!!

    关于sqlite - 如何防止 SQLite 数据库锁定?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17348480/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com