SQLite在Android一般应用中还是比较常用,早期的时候碰到过不少坑,其中最烦的就是多线程并发读写问题,今天正好整理一下,做个笔记,也欢迎指正、讨论和补充。
一、查询优化
1、wal模式
开启wal模式,可以实现并发读,且读写不阻塞,当然写与写之间仍然阻塞,该模式需要android3.0+才支持。
当开启了wal模式更新数据时,会先将数据写入到*.db-wal文件中,而不是直接修改数据库文件,当执行checkpoint时或某个时间点才会将数据更新到数据库文件(执行endTransaction时会提交checkpoint)。当出现rollback也只是清除wal日志文件,而ROLLBACK JOURNAL模式,也就是关闭wal模式时,当数据有更新时,先将需要修改的数据备份到journal文件中,然后修改数据库文件,当发生rollback,从journal日志中取出数据,并修改数据库文件,然后清除journal日志。 从以上流程来看wal在数据更新上I/O量要小,所以写操作要快。由于在读取数据时也需要读取wal日志验证数据的正确性,所以读取数据相对要慢,但使用wal还是提高了读取的并发性。
开启wal模式后,一定要使用beginTransactionNonExclusive来提交事务。db.beginTransaction()相当于execSQL("BEGIN EXCLUSIVE;"),在当前事务没有结束之前任何其他线程或进程都无法对数据库进行读写操作。当开启wal模式时,使用db.beginTransactionNonExclusive(),相当于execSQL("BEGIN IMMEDIATE;"),只会限制其他线程对数据库的写操作,不会阻塞读操作。
2、建立索引,推荐看这个文章,足够了解索引的简单使用和优点了http://www.trinea.cn/android/database-performance/,总而言之,索引会增加SQLite体积,且增删改时也要维护索引,会对增删改性能存在一定影响,如果数据量不大,不建议使用。使用时一定要根据需求建立合适的索引,勿滥用。
3、当某张表可预见数据量很大时,可以适当的进行表的细化、后期可以分表分库,查询时也可以使用异步查询。
二、批量插入优化
1、事务提交
批量插入,包括更新删除,一定要加事务,如果不加事务,则默认会为每一次插入开启一个事务并自动提交,是非常慢的。
2、开启wal模式,参见上文中解释;
3、SQLiteStatement优化
我们每次执行的sql语句最终会转化为一个SQLiteStatement对象来进行处理,可以预先使用db.compileStatement方法获取SQLiteStatement对象并重用,而不是让系统每次insert都构造一个对应的SQLiteStatement对象,这样能够提高内存的使用率。
补充:网上有人解释“比如insert into xxx,一般情况下执行多少次,就要编译多少次”,关于这点,首先我认为不对,我阐述一下我自己的分析:SQLite想要执行操作,需要将程序中的sql语句进行“预编译”。例如批量插入,我们可以使用“显式预编译”来做到重用SQLiteStatement,也就是使用compileStatement方法。其实重