数据库踩坑和调试

数据库

Android数据库设计

  1. 冗余字段设计,表设计要空出2到3个冗余字段
  2. 不要过多的关联查询,外键约束不要多了
  3. 设计要优先考虑易用性 ,容易升级,性能方面主要要通过应用层来掌控
  4. 适当的分库可以有效避免数据库被锁住。(sqlite的文件锁)

database is locked的原因

  • 同时有两个写操作的时候,后执行的只能先等待,如果等待时间超过5秒,就会产生这种错误
  • 一个文件正在写入,重复打开数据库操作也会报错

Android DB操作技巧

  1. 对于经常要使用的DB不需要关闭,一般全局保存一个静态的SQLiteDatabase句柄(Writable的),而且是在Application oncreate 就初始化好.
  2. 由于Application 没有destroy的回调,这里我们一般会做一个ActivityStack,然后在Activity初始化和destory 时候入栈和出栈来控制界面的显示逻辑,当检测到栈为空的时候就可以认为要退出应用了。
  3. 能一次取完数据,不要分次取(虽然可能取得数据量有一定优化,但是分次太耗时,尽量交给上层做)
  4. DataBase is locked,导致数据取不出来。 解决方向有以下几点:
    第1点:在获取databse时候加一个锁
    第2点:分库,对于相互关联若的数据进行分库处理
    第3点:数据库不要关闭,并且尝试分开存writeable 和 readable两个静态的DB

参考文章:
https://segmentfault.com/q/1010000005140824

Android 数据库多线程读写

SQLiteDatabase源码解析

可以知道,insert , update , execSQL 都会调用lock();
query 没有调用lock(),but SQLiteCursor保存了查询条件,但是并没有立即执行查询,而是使用了lazy的策略,在需要时加载部分数据时候依然会SQLiteDatabase.lock()

多线程读写

多线程写

多线程写必须公用一个SqliteHelper,不然会抛出 database is locked的错误

多线程读

多线程读的话可以使用多个SqliteHelper

单写多读

方案: 一个线程写,多个线程同时读,每个线程都用各自SQLiteOpenHelper

问题1:有线程读的时候写数据库会抛出异常 database is locked

原因:
SQLiteOpenHelper.getReadableDatabase() 不见得获得的就是只读SQLiteDatabase

解决方案:
重写getOnlyReadDatabase 来强制只获取只读的Database

问题2:在FIX问题1后在有线程读的时候写数据库仍然会出现database is locked

原因:SQLiteDataBase有个属性 ENABLE_WRITE_AHEAD_LOGGING的属性(默认是关闭的),在关闭时,不允许读,写同时进行(通过锁来保证的);
当打开时,它允许一个写线程与多个读线程同时在一个SQLiteDatabase上起作用。实现原理是写操作其实是在一个单独的文件,不是原数据库文件。所以写在执行时,不会影响读操作,读操作读的是原数据文件,是写操作开始之前的内容。

解决方案:
SQLiteDataBase enableWriteAheadLogging()打开 ENABLE_WRITE_AHEAD_LOGGING属性。

参考文章:
http://www.cnblogs.com/javawebsoa/p/3237018.html
https://blog.csdn.net/qq_25412055/article/details/52414420

数据库调试

第一步通过AS自带的Android monitor 把数据库导出来

see

第二步通过Sqlite professional

查看数据库的结构可以具体查看到每一张表的详细信息
see
也可以通过query工具栏,执行基本的命令行操作。

Sqlite基本命令:http://www.runoob.com/sqlite/sqlite-trigger.html