SQLite在Android一般應用中還是比較常用,早期的時候碰到過不少坑,其中最煩的就是多線程并發(fā)讀寫問題,今天正好整理一下,做個筆記,也歡迎指正、討論和補充。

  一、查詢優(yōu)化

  1、wal模式

  開啟wal模式,可以實現(xiàn)并發(fā)讀,且讀寫不阻塞,當然寫與寫之間仍然阻塞,該模式需要android3.0+才支持。

  當開啟了wal模式更新數(shù)據(jù)時,會先將數(shù)據(jù)寫入到*.db-wal文件中,而不是直接修改數(shù)據(jù)庫文件,當執(zhí)行checkpoint時或某個時間點才會將數(shù)據(jù)更新到數(shù)據(jù)庫文件(執(zhí)行endTransaction時會提交checkpoint)。當出現(xiàn)rollback也只是清除wal日志文件,而ROLLBACK JOURNAL模式,也就是關閉wal模式時,當數(shù)據(jù)有更新時,先將需要修改的數(shù)據(jù)備份到journal文件中,然后修改數(shù)據(jù)庫文件,當發(fā)生rollback,從journal日志中取出數(shù)據(jù),并修改數(shù)據(jù)庫文件,然后清除journal日志。 從以上流程來看wal在數(shù)據(jù)更新上I/O量要小,所以寫操作要快。由于在讀取數(shù)據(jù)時也需要讀取wal日志驗證數(shù)據(jù)的正確性,所以讀取數(shù)據(jù)相對要慢,但使用wal還是提高了讀取的并發(fā)性。

  開啟wal模式后,一定要使用beginTransactionNonExclusive來提交事務。db.beginTransaction()相當于execSQL("BEGIN EXCLUSIVE;"),在當前事務沒有結束之前任何其他線程或進程都無法對數(shù)據(jù)庫進行讀寫操作。當開啟wal模式時,使用db.beginTransactionNonExclusive(),相當于execSQL("BEGIN IMMEDIATE;"),只會限制其他線程對數(shù)據(jù)庫的寫操作,不會阻塞讀操作。

  2、建立索引,推薦看這個文章,足夠了解索引的簡單使用和優(yōu)點了http://www.trinea.cn/android/database-performance/,總而言之,索引會增加SQLite體積,且增刪改時也要維護索引,會對增刪改性能存在一定影響,如果數(shù)據(jù)量不大,不建議使用。使用時一定要根據(jù)需求建立合適的索引,勿濫用。

  3、當某張表可預見數(shù)據(jù)量很大時,可以適當?shù)倪M行表的細化、后期可以分表分庫,查詢時也可以使用異步查詢。

 

  二、批量插入優(yōu)化

  1、事務提交

  批量插入,包括更新刪除,一定要加事務,如果不加事務,則默認會為每一次插入開啟一個事務并自動提交,是非常慢的。

  2、開啟wal模式,參見上文中解釋;

  3、SQLiteStatement優(yōu)化

  我們每次執(zhí)行的sql語句最終會轉化為一個SQLiteStatement對象來進行處理,可以預先使用db.compileStatement方法獲取SQLiteStatement對象并重用,而不是讓系統(tǒng)每次insert都構造一個對應的SQLiteStatement對象,這樣能夠提高內存的使用率。

  補充:網(wǎng)上有人解釋“比如insert into xxx,一般情況下執(zhí)行多少次,就要編譯多少次”,關于這點,首先我認為不對,我闡述一下我自己的分析:SQLite想要執(zhí)行操作,需要將程序中的sql語句進行“預編譯”。例如批量插入,我們可以使用“顯式預編譯”來做到重用SQLiteStatement,也就是使用compileStatement方法。其實重