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

  一、查詢優(yōu)化

  1、wal模式

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

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

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

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

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

 

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

  1、事務(wù)提交

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

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

  3、SQLiteStatement優(yōu)化

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

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

網(wǎng)友評論