深刻SQLite,一网打尽“危险操做”

简述

Sqlite Database是一个比较稳定轻量的小型数据库,不像是mysql、oracle等数据库同样具备单独的服务进程。基于sqlite的读写都是读写原始的磁盘文件,也就是说sqlite的操做彻底是磁盘的IO操做。SQLite操做存在必定的异常状况,在客户端也难以监控,在用户基数比较大的状况下,每每对极少数的用户的影响也是致命的。了解sqlite便于咱们规范的去使用它。html

一般的异常状况都包含在如下状况:mysql

  • 文件错写
  • 文件锁 bug
  • 文件 sync 失败
  • 设备损坏
  • 内存覆盖
  • 操做系统 bug
  • SQLite bug

官方的文档也有介绍到android

How To Corrupt An SQLite Database File sql

鉴于操做系统和Sqlite bug等不可控方面咱们更多须要作到能准确的监控,如内存不足,相似于微信的作法就是监控到内存不足,弹出提示框告知用户去清理内存。下面就来探讨下因为操做不当引发的异常。数据库

SQLiteDatabaseLockedException

android.database.sqlite.SQLiteDatabaseLockedException DB引擎在执行job的时候发现获取不到数据库的锁就会抛出这个异常。数据库锁用于防止多线程同时写入数据从而致使DB损坏。因此通常发生在多线程的事务操做上。针对同一个事务代码段,若是一个事务还没有结束提交,另一个线程便开始介入执行就会致使冲突抛出此异常。微信

因此出现上述异常,请检查你的代码。建议的操做是:多线程

  1. 只使用一个SQLOpenHelper来访问和操做database,单例你的SQLOpenHelper,避免多个OpenHelper屡次open db并写入数据。
  2. 当再也不须要继续操做db的时候,关闭全部database helper实例
  3. 作事务操做的时候必定要保证事务完成调用了endTransaction() 或者transaction Successful相关方法。下一个事务操做会在entransaction结束后请求锁。

文件锁

系统文件锁问题SQLite依赖于底层的文件系统对文件锁的实现。SQLite 默认锁是协同锁。假设有A、B线程同时访问数据库并写入数据。这个时候来个C线程不是使用SQLite API的方式来操做数据的话(如直接的IO操做),那么数据库锁就会被自动取消,A、B线程在没有锁保护的状态同时操做db就可能致使DB的损坏。oracle

建议:性能

  1. 尽可能使用SQLite API来操做数据库的读写。
  2. 若是有IO线程直接操做数据库,代码逻辑上保证线程间的同步顺序。

文件sync失败

批量写入比较大的数据咱们可能想要更快的速度,网上不少会建议你设置PRAGMA synchronous=OFF, Android对应的设置方式就是:mDatabase.execSQL("PRAGMA synchronous=OFF"); 等同于在说:我期待更快的写入速度,磁盘驱动器为了达到目标,就耍了个小聪明,忽略开始SQLite的同步操做,先通知到系统我写入完了。实际上不必定数据彻底写入了,假如这个时候出现了断电等意外,就会致使真实数据写入失败从而引起DB损坏。优化

建议:

尽可能不要设置PRAGMA synchronous=OFF 数据比较大的写入等操做从sql语句优化和性能的优化入手。

多db操做

不少较为复杂的应用可能一个应用程序不止有一个数据库。这里列举一个场景,应用包含两个DB。 DatabaseA:预置数据的数据库,已经设计好表并提早插入了数据。不须要跟随应用动态的去建立DB。用户第一次安装的时候把准备好的DB经过IO的方式拷贝到应用包路径下面的databases路径提供给应用访问,此DB通常只用读,不多或者彻底不写入数据,是静态的。 DatabaseB: 跟随应用的安装和升级动态的建立和升级,做为应用数据的持久化方式之一。读写操做均比较频繁。

DababaseA会在应用的启动的时候IO迁移拷贝。DatabaseB动态Create后处处可能都有使用到(包括启动)

Danger 1:文件覆盖

DatabaseB的DBOpenHelperB正在读写DatabaseA的表,同时DatabaseA正在被IO线程拷贝写入文件覆盖。DBOpenHelperB创建链接可能依然是旧的数据库,读写天然也是到旧的数据库。部分文件系统甚至引起IO拷贝被中断。

Danger 2:交叉读写

通常有多个DB会对应多个DBOpenHelper进行读写,如过DBOpenHelper作了跨DB的读写,就会致使重复的DB Open或Close,影响到了其余DBOpenHelper的正常读写。直接的致使锁异常或者DB损坏等问题。

多DB建议:

  1. 非SQLite API线程IO数据库请保持IO彻底结束后再创建与数据库的读写链接或读写操做 管理好你的DBHelper
  2. DBHelper避免跨DB的操做,一个DB对应一个DBHelper实例

综上多数状况都是代码上操做不当引起的问题,了解好原理才能对症下药,避开危险区

补充: 减小多进程或多线程操做,尽量单线程写; 减小事务操做,减少事务复杂度。

相关文章
相关标签/搜索