Sqlite基本使用及性能優(yōu)化

1.寫(xiě)在前面的話

前面寫(xiě)過(guò)一篇關(guān)于Sqlite基本操作的文章,今天我們來(lái)學(xué)習(xí)Android中如何使用Sqlite以及性能優(yōu)化。

2.Android平臺(tái)下數(shù)據(jù)庫(kù)相關(guān)類

SQLiteOpenHelper 抽象類:通過(guò)從此類繼承實(shí)現(xiàn)用戶類,來(lái)提供數(shù)據(jù)庫(kù)打開(kāi)、關(guān)閉等操作函數(shù)。
SQLiteDatabase 數(shù)據(jù)庫(kù)訪問(wèn)類:執(zhí)行對(duì)數(shù)據(jù)庫(kù)的插入記錄、查詢記錄等操作。
SQLiteCursor 查詢結(jié)構(gòu)操作類:用來(lái)訪問(wèn)查詢結(jié)果中的記錄。

3.Android Sqlite的使用

(1)創(chuàng)建數(shù)據(jù)庫(kù)

Android下要使用Sqlite首先要寫(xiě)一個(gè)SQLiteOpenHelper的實(shí)現(xiàn)類,該類的構(gòu)造函數(shù)如下:

private MyDBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
    super(context, name, factory, version);
}

需要傳入的參數(shù)解釋如下:
name:數(shù)據(jù)庫(kù)的名稱,用這個(gè)名稱來(lái)打開(kāi)創(chuàng)建或打開(kāi)相應(yīng)的數(shù)據(jù)庫(kù)。
factory:用來(lái)創(chuàng)建cursor,通常情況下我們傳入null,使用默認(rèn)的就行。
version:數(shù)據(jù)庫(kù)的版本,從1開(kāi)始,可以修改版本號(hào),來(lái)除法數(shù)據(jù)庫(kù)的更新操作。

對(duì)于SQLiteOpenHelper我們般會(huì)設(shè)計(jì)成單例。

private static MyDBHelper myDBHelper;

public static synchronized MyDBHelper getInstance(Context context) {
   if (myDBHelper == null) {
            Context applicationContext = context.getApplicationContext();
            myDBHelper = new MyDBHelper(applicationContext);
        }
        return myDBHelper;
    }
private MyDBHelper(Context context) {
        this(context, DB_NAME, null, VERSION);
    }

當(dāng)我們首次使用MyDBHelper來(lái)獲取數(shù)據(jù)庫(kù)時(shí),即調(diào)用getWritableDatabase或getReadableDatabase方法時(shí),變會(huì)觸發(fā)DBHelper中的onCreate方法,這時(shí)我們可以在數(shù)據(jù)庫(kù)中創(chuàng)建表:

@Override
    public void onCreate(SQLiteDatabase db) {
        StringBuilder sql = new StringBuilder();
        sql.append("create table ");
        sql.append(TAB_PERSON + "(");
        sql.append("id integer,");
        sql.append("name char(8),");
        sql.append("age int");
        sql.append(");");
        db.execSQL(sql.toString());
    }

以上代碼實(shí)際執(zhí)行了一個(gè)sql語(yǔ)句create table person(id integer,name char(10), age int);創(chuàng)建了一張person表。

(2)更新數(shù)據(jù)庫(kù)

通過(guò)修改數(shù)據(jù)庫(kù)的版本我們可以觸發(fā)數(shù)據(jù)庫(kù)的更新。這里我們修改數(shù)據(jù)庫(kù)的version為2,并在更新時(shí)添加一個(gè)新的developer表。和創(chuàng)建一樣,onUpgrade會(huì)在調(diào)用getWritableDatabase或getReadableDatabase時(shí)觸發(fā)。

 @Override
 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        StringBuilder sql = new StringBuilder();
        sql.append("create table ");
        sql.append(TAB_DEVLOPER + "(");
        sql.append("id integer,");
        sql.append("position char(20),");
        sql.append(");");
        db.execSQL(sql.toString());
}

(3)增刪查改

對(duì)于增刪查改,我們可以分別調(diào)用數(shù)據(jù)庫(kù)的insert、delete、query、update方法傳入?yún)?shù)來(lái)進(jìn)行操作,當(dāng)然也可以直接用execSQL方法來(lái)執(zhí)行Sql來(lái)進(jìn)行操作。比如我們要在在person表中插入一條記錄,我們可以在MyDBHelper中創(chuàng)建一個(gè)inser方法,如下:

public boolean insert(int id, String name, int age) {
        ContentValues values = new ContentValues();
        values.put("name", name);
        values.put("age", age);
        long insert = getWritableDatabase().insert(TAB_PERSON, null, values);
        return insert >= 1;
    }

4.Sqlite性能優(yōu)化

(1)編譯SQL語(yǔ)句

Sqlite想要執(zhí)行操作,需要將程序中的sql語(yǔ)句編譯成對(duì)應(yīng)的SQLiteStatement,比如select * from record這一句,被執(zhí)行100次就需要編譯100次。對(duì)于批量處理插入或者更新的操作,我們可以使用顯示編譯來(lái)做到重用SQLiteStatement。

想要做到重用SQLiteStatement也比較簡(jiǎn)單,基本如下

編譯sql語(yǔ)句獲得SQLiteStatement對(duì)象,參數(shù)使用?代替
在循環(huán)中對(duì)SQLiteStatement對(duì)象進(jìn)行具體數(shù)據(jù)綁定,bind方法中的index從1開(kāi)始,不是0

如下向person表中插入100條數(shù)據(jù):

public void insertBatchPreCompile() {
        long start = SystemClock.currentThreadTimeMillis();
        String sql = "insert into " + TAB_PERSON + " values (?,'test','1');";
        SQLiteStatement sqLiteStatement = getReadableDatabase().compileStatement(sql);
        int count = 0;
        while (count < 100) {
            count++;
            sqLiteStatement.clearBindings();
            sqLiteStatement.bindLong(1, count);
            sqLiteStatement.executeInsert();
        }
        Log.e(TAG, "insert recompile use time " + (SystemClock.currentThreadTimeMillis() - start));
    }

(2)顯示使用事務(wù)

在Android中,無(wú)論是使用SQLiteDatabase的insert,delete等方法還是execSQL都開(kāi)啟了事務(wù),來(lái)確保每一次操作都具有原子性,使得結(jié)果要么是操作之后的正確結(jié)果,要么是操作之前的結(jié)果。

然而事務(wù)的實(shí)現(xiàn)是依賴于名為rollback journal文件,借助這個(gè)臨時(shí)文件來(lái)完成原子操作和回滾功能。既然屬于文件,就符合Unix的文件范型(Open-Read/Write- Close),因而對(duì)于批量的修改操作會(huì)出現(xiàn)反復(fù)打開(kāi)文件讀寫(xiě)再關(guān)閉的操作。然而好在,我們可以顯式使用事務(wù),將批量的數(shù)據(jù)庫(kù)更新帶來(lái)的journal文件打開(kāi)關(guān)閉降低到1次。

具體的實(shí)現(xiàn)代碼如下:

public void insertWithTransaction() {
        long start = SystemClock.currentThreadTimeMillis();
        int count = 0;
        try {
            getWritableDatabase().beginTransaction();
            while (count++ < 100) {
                insert(count, "test", 1);
            }
            getWritableDatabase().setTransactionSuccessful();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            getWritableDatabase().endTransaction();
        }
        Log.e(TAG, "insert traceaction use time " + (SystemClock.currentThreadTimeMillis() - start));
    }

使用這兩種方式分別優(yōu)化,對(duì)比效果如下:


這里寫(xiě)圖片描述

從圖中可以看到在插入100條的情況下,使用預(yù)編譯的方式可以稍微提升性能,但是使用事務(wù),能夠使性能提升大概8倍,所以可以看出頻繁的IO操作還是比較耗時(shí)的。同時(shí)使用兩種方式進(jìn)行優(yōu)化,可以提升17倍,優(yōu)化效果非常明顯。

(3)建立索引

a.索引的概念

索引,使用索引可快速訪問(wèn)數(shù)據(jù)庫(kù)表中的特定信息。索引是對(duì)數(shù)據(jù)庫(kù)表中一列或多列的值進(jìn)行排序的一種結(jié)構(gòu)。

在關(guān)系數(shù)據(jù)庫(kù)中,索引是一種與表有關(guān)的數(shù)據(jù)庫(kù)結(jié)構(gòu),它可以使對(duì)應(yīng)于表的SQL語(yǔ)句執(zhí)行得更快。索引的作用相當(dāng)于圖書(shū)的目錄,可以根據(jù)目錄中的頁(yè)碼快速找到所需的內(nèi)容。當(dāng)表中有大量記錄時(shí),若要對(duì)表進(jìn)行查詢,第一種搜索信息方式是全表搜索,是將所有記錄一一取出,和查詢條件進(jìn)行一一對(duì)比,然后返回滿足條件的記錄,這樣做會(huì)消耗大量數(shù)據(jù)庫(kù)系統(tǒng)時(shí)間,并造成大量磁盤(pán)I/O操作;第二種就是在表中建立索引,然后在索引中找到符合查詢條件的索引值,最后通過(guò)保存在索引中的ROWID(相當(dāng)于頁(yè)碼)快速找到表中對(duì)應(yīng)的記錄。
索引是一個(gè)單獨(dú)的、物理的數(shù)據(jù)庫(kù)結(jié)構(gòu),它是某個(gè)表中一列或若干列值的集合和相應(yīng)的指向表中物理標(biāo)識(shí)這些值的數(shù)據(jù)頁(yè)的邏輯指針清單。

索引提供指向存儲(chǔ)在表的指定列中的數(shù)據(jù)值的指針,然后根據(jù)您指定的排序順序?qū)@些指針排序。數(shù)據(jù)庫(kù)使用索引的方式與您使用書(shū)籍中的索引的方式很相似:它搜索索引以找到特定值,然后順指針找到包含該值的行。

b.建立索引

創(chuàng)建索引的基本語(yǔ)法:

CREATE INDEX index_name ON table_name;

創(chuàng)建單列索引

CREATE INDEX index_name ON table_name;
c.索引的利弊

毋庸置疑,索引加速了我們檢索數(shù)據(jù)表的速度。然而正如西方諺語(yǔ) “There are two sides of a coin”,索引亦有缺點(diǎn):

對(duì)于增加,更新和刪除來(lái)說(shuō),使用了索引會(huì)變慢,比如你想要?jiǎng)h除字

  • 列表內(nèi)容典中的一個(gè)字,那么你同時(shí)也需要?jiǎng)h除這個(gè)字在拼音索引和部首索引中的信息。
  • 建立索引會(huì)增加數(shù)據(jù)庫(kù)的大小,比如字典中的拼音索引和部首索引實(shí)際上是會(huì)增加字典的頁(yè)數(shù),讓字典變厚的。
  • 為數(shù)據(jù)量比較小的表建立索引,往往會(huì)事倍功半。

所以使用索引需要考慮實(shí)際情況進(jìn)行利弊權(quán)衡,對(duì)于查詢操作量級(jí)較大,業(yè)務(wù)對(duì)要求查詢要求較高的,還是推薦使用索引的。

(4)查詢數(shù)據(jù)優(yōu)化

按需獲取列信息

db.query(TableDefine.TABLE_RECORD, null, null, null, null, null, null) ;

其中上面方法的第二個(gè)參數(shù)類型為String[],意思是返回結(jié)果參考的colum信息,傳遞null表明需要獲取全部的column數(shù)據(jù)。如果我們不需要所有列的信息,最好指定一下需要的列。

提前獲取索引
例如下面的代碼,我們可以把獲取列索引的代碼cursor.getColumnIndex(TableDefine.COLUMN_INSERT_TIME)放到循環(huán)外,這樣不需要每次獲取。

 private void badQueryWithLoop(SQLiteDatabase db) {
    Cursor cursor = db.query(TableDefine.TABLE_RECORD, new String[]{TableDefine.COLUMN_INSERT_TIME}, null, null, null, null, null) ;
    while (cursor.moveToNext()) {
        long insertTime = cursor.getLong(cursor.getColumnIndex(TableDefine.COLUMN_INSERT_TIME));
    }
}

(5)ContentValues的容量調(diào)整

SQLiteDatabase提供了方便的ContentValues簡(jiǎn)化了我們處理列名與值的映射,ContentValues內(nèi)部采用了 HashMap來(lái)存儲(chǔ)Key-Value數(shù)據(jù),ContentValues的初始容量是8,如果當(dāng)添加的數(shù)據(jù)超過(guò)8之前,則會(huì)進(jìn)行雙倍擴(kuò)容操作,因此建議對(duì)ContentValues填入的內(nèi)容進(jìn)行估量,設(shè)置合理的初始化容量,減少不必要的內(nèi)部擴(kuò)容操作。

(6)及時(shí)關(guān)閉Cursor

(7)耗時(shí)異步化

數(shù)據(jù)庫(kù)的操作,屬于本地IO,通常比較耗時(shí),如果處理不好,很容易導(dǎo)致ANR,因此建議將這些耗時(shí)操作放入異步線程中處理。

本文Demo下載地址SqliteDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容