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

1.寫在前面的話

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

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

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

3.Android Sqlite的使用

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

Android下要使用Sqlite首先要寫一個SQLiteOpenHelper的實現(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ù)庫的名稱烈炭,用這個名稱來打開創(chuàng)建或打開相應(yīng)的數(shù)據(jù)庫。
factory:用來創(chuàng)建cursor宝恶,通常情況下我們傳入null符隙,使用默認(rèn)的就行趴捅。
version:數(shù)據(jù)庫的版本,從1開始霹疫,可以修改版本號拱绑,來除法數(shù)據(jù)庫的更新操作。

對于SQLiteOpenHelper我們般會設(shè)計成單例丽蝎。

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來獲取數(shù)據(jù)庫時猎拨,即調(diào)用getWritableDatabase或getReadableDatabase方法時,變會觸發(fā)DBHelper中的onCreate方法,這時我們可以在數(shù)據(jù)庫中創(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());
    }

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

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

通過修改數(shù)據(jù)庫的版本我們可以觸發(fā)數(shù)據(jù)庫的更新红省。這里我們修改數(shù)據(jù)庫的version為2,并在更新時添加一個新的developer表国觉。和創(chuàng)建一樣吧恃,onUpgrade會在調(diào)用getWritableDatabase或getReadableDatabase時觸發(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)增刪查改

對于增刪查改麻诀,我們可以分別調(diào)用數(shù)據(jù)庫的insert痕寓、delete、query针饥、update方法傳入?yún)?shù)來進(jìn)行操作厂抽,當(dāng)然也可以直接用execSQL方法來執(zhí)行Sql來進(jìn)行操作。比如我們要在在person表中插入一條記錄丁眼,我們可以在MyDBHelper中創(chuàng)建一個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語句

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

想要做到重用SQLiteStatement也比較簡單卢厂,基本如下

編譯sql語句獲得SQLiteStatement對象,參數(shù)使用?代替
在循環(huán)中對SQLiteStatement對象進(jìn)行具體數(shù)據(jù)綁定惠啄,bind方法中的index從1開始慎恒,不是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中,無論是使用SQLiteDatabase的insert,delete等方法還是execSQL都開啟了事務(wù)撵渡,來確保每一次操作都具有原子性融柬,使得結(jié)果要么是操作之后的正確結(jié)果,要么是操作之前的結(jié)果趋距。

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

具體的實現(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)化掷空,對比效果如下:


這里寫圖片描述

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

(3)建立索引

a.索引的概念

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

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

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

b.建立索引

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

CREATE INDEX index_name ON table_name;

創(chuàng)建單列索引

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

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

對于增加,更新和刪除來說本今,使用了索引會變慢拆座,比如你想要刪除字

  • 列表內(nèi)容典中的一個字,那么你同時也需要刪除這個字在拼音索引和部首索引中的信息冠息。
  • 建立索引會增加數(shù)據(jù)庫的大小挪凑,比如字典中的拼音索引和部首索引實際上是會增加字典的頁數(shù),讓字典變厚的逛艰。
  • 為數(shù)據(jù)量比較小的表建立索引躏碳,往往會事倍功半。

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

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

按需獲取列信息

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

其中上面方法的第二個參數(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簡化了我們處理列名與值的映射沿猜,ContentValues內(nèi)部采用了 HashMap來存儲Key-Value數(shù)據(jù)枚荣,ContentValues的初始容量是8,如果當(dāng)添加的數(shù)據(jù)超過8之前啼肩,則會進(jìn)行雙倍擴(kuò)容操作橄妆,因此建議對ContentValues填入的內(nèi)容進(jìn)行估量,設(shè)置合理的初始化容量祈坠,減少不必要的內(nèi)部擴(kuò)容操作害碾。

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

(7)耗時異步化

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

本文Demo下載地址SqliteDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末阁猜,一起剝皮案震驚了整個濱河市丸逸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌剃袍,老刑警劉巖黄刚,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異民效,居然都是意外死亡憔维,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門畏邢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來业扒,“玉大人,你說我怎么就攤上這事舒萎⌒琢蓿” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵逆甜,是天一觀的道長。 經(jīng)常有香客問我致板,道長交煞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任斟或,我火速辦了婚禮素征,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘萝挤。我一直安慰自己御毅,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布怜珍。 她就那樣靜靜地躺著端蛆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪酥泛。 梳的紋絲不亂的頭發(fā)上今豆,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機(jī)與錄音柔袁,去河邊找鬼呆躲。 笑死,一個胖子當(dāng)著我的面吹牛捶索,可吹牛的內(nèi)容都是我干的插掂。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼辅甥!你這毒婦竟也來了酝润?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤肆氓,失蹤者是張志新(化名)和其女友劉穎袍祖,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谢揪,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡蕉陋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了拨扶。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凳鬓。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖患民,靈堂內(nèi)的尸體忽然破棺而出缩举,到底是詐尸還是另有隱情,我是刑警寧澤匹颤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布仅孩,位于F島的核電站,受9級特大地震影響印蓖,放射性物質(zhì)發(fā)生泄漏辽慕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一赦肃、第九天 我趴在偏房一處隱蔽的房頂上張望溅蛉。 院中可真熱鬧,春花似錦他宛、人聲如沸船侧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽镜撩。三九已至,卻和暖如春讯检,著一層夾襖步出監(jiān)牢的瞬間琐鲁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工人灼, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留围段,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓投放,卻偏偏與公主長得像奈泪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容