Android數(shù)據(jù)庫優(yōu)化

本文為性能優(yōu)化的第一篇——數(shù)據(jù)庫性能優(yōu)化磅摹,原理適用于大部分?jǐn)?shù)據(jù)庫包括Sqlite、Mysql霎奢、Oracle偏瓤、Sql server,詳細(xì)介紹了索引(優(yōu)缺點(diǎn)椰憋、分類厅克、場景、規(guī)則)和事務(wù)橙依,最后介紹了部分單獨(dú)針對Sqlite的優(yōu)化证舟。

1硕旗、索引

簡單的說,索引就像書本的目錄女责,目錄可以快速找到所在頁數(shù)漆枚,數(shù)據(jù)庫中索引可以幫助快速找到數(shù)據(jù),而不用全表掃描抵知,合適的索引可以大大提高數(shù)據(jù)庫查詢的效率墙基。

(1). 優(yōu)點(diǎn)

大大加快了數(shù)據(jù)庫檢索的速度,包括對單表查詢刷喜、連表查詢残制、分組查詢、排序查詢掖疮。經(jīng)常是一到兩個(gè)數(shù)量級的性能提升初茶,且隨著數(shù)據(jù)數(shù)量級增長。

(2). 缺點(diǎn)

索引的創(chuàng)建和維護(hù)存在消耗浊闪,索引會(huì)占用物理空間恼布,且隨著數(shù)據(jù)量的增加而增加。

在對數(shù)據(jù)庫進(jìn)行增刪改時(shí)需要維護(hù)索引搁宾,所以會(huì)對增刪改的性能存在影響折汞。

(3). 分類

a. 直接創(chuàng)建索引和間接創(chuàng)建索引

直接創(chuàng)建: 使用sql語句創(chuàng)建,Android中可以在SQLiteOpenHelper的onCreate或是onUpgrade中直接excuSql創(chuàng)建語句盖腿,語句如

CREATEINDEXmycolumn_indexONmytable(myclumn)

間接創(chuàng)建: 定義主鍵約束或者唯一性鍵約束字支,可以間接創(chuàng)建索引,主鍵默認(rèn)為唯一索引奸忽。

b. 普通索引和唯一性索引

普通索引:

CREATEINDEXmycolumn_indexONmytable(myclumn)

唯一性索引:保證在索引列中的全部數(shù)據(jù)是唯一的堕伪,對聚簇索引和非聚簇索引都可以使用,語句為

CREATEUNIQUECOUSTEREDINDEXmyclumn_cindexONmytable(mycolumn)

c. 單個(gè)索引和復(fù)合索引

單個(gè)索引:索引建立語句中僅包含單個(gè)字段栗菜,如上面的普通索引和唯一性索引創(chuàng)建示例欠雌。

復(fù)合索引:又叫組合索引,在索引建立語句中同時(shí)包含多個(gè)字段疙筹,語句如:

CREATEINDEXname_indexONusername(firstname,lastname)

其中firstname為前導(dǎo)列富俄。

d. 聚簇索引和非聚簇索引(聚集索引,群集索引)

聚簇索引:物理索引而咆,與基表的物理順序相同霍比,數(shù)據(jù)值的順序總是按照順序排列,語句為:

CREATECLUSTEREDINDEXmycolumn_cindexONmytable(mycolumn)WITHALLOW_DUP_ROW

其中WITH ALLOW_DUP_ROW表示允許有重復(fù)記錄的聚簇索引

非聚簇索引:

CREATEUNCLUSTEREDINDEXmycolumn_cindexONmytable(mycolumn)

索引默認(rèn)為非聚簇索引

(4). 使用場景

在上面講到了優(yōu)缺點(diǎn)暴备,那么肯定會(huì)對何時(shí)使用索引既有點(diǎn)明白又有點(diǎn)糊涂吧悠瞬,那么下面總結(jié)下:

a. ?當(dāng)某字段數(shù)據(jù)更新頻率較低,查詢頻率較高,經(jīng)常有范圍查詢(>, <, =, >=, <=)或order by浅妆、group by發(fā)生時(shí)建議使用索引望迎。并且選擇度越大,建索引越有優(yōu)勢凌外,這里選擇度指一個(gè)字段中唯一值的數(shù)量/總的數(shù)量辩尊。

b. ?經(jīng)常同時(shí)存取多列,且每列都含有重復(fù)值可考慮建立復(fù)合索引

(5). 索引使用規(guī)則

a. ?對于復(fù)合索引康辑,把使用最頻繁的列做為前導(dǎo)列(索引中第一個(gè)字段)摄欲。如果查詢時(shí)前導(dǎo)列不在查詢條件中則該復(fù)合索引不會(huì)被使用。

如create unique index PK_GRADE_CLASS on student (grade, class)

select * from student where class = 2未使用到索引

select * from dept where grade = 3使用到了索引

b. ?避免對索引列進(jìn)行計(jì)算疮薇,對where子句列的任何計(jì)算如果不能被編譯優(yōu)化胸墙,都會(huì)導(dǎo)致查詢時(shí)索引失效

select * from student where tochar(grade)=’2′

c. ?比較值避免使用NULL

d. ?多表查詢時(shí)要注意是選擇合適的表做為內(nèi)表欠痴。連接條件要充份考慮帶有索引的表溃斋、行數(shù)多的表,內(nèi)外表的選擇可由公式:外層表中的匹配行數(shù)*內(nèi)層表中每一次查找的次數(shù)確定,乘積最小為最佳方案胖齐。實(shí)際多表操作在被實(shí)際執(zhí)行前,查詢優(yōu)化器會(huì)根據(jù)連接條件嗽冒,列出幾組可能的連接方案并從中找出系統(tǒng)開銷最小的最佳方案呀伙。

e. ?查詢列與索引列次序一致

f. ?用多表連接代替EXISTS子句

g. ?把過濾記錄數(shù)最多的條件放在最前面

h. ?善于使用存儲(chǔ)過程,它使sql變得更加靈活和高效(Sqlite不支持存儲(chǔ)過程::>_<::?)

(6)索引檢驗(yàn)

建立了索引添坊,對于某條sql語句是否使用到了索引可以通過執(zhí)行計(jì)劃查看是否用到了索引剿另。

2、使用事務(wù)

使用事務(wù)的兩大好處是原子提交和更優(yōu)性能贬蛙。

(1) 原子提交

原則提交意味著同一事務(wù)內(nèi)的所有修改要么都完成要么都不做雨女,如果某個(gè)修改失敗,會(huì)自動(dòng)回滾使得所有修改不生效阳准。

(2) 更優(yōu)性能

Sqlite默認(rèn)會(huì)為每個(gè)插入氛堕、更新操作創(chuàng)建一個(gè)事務(wù),并且在每次插入野蝇、更新后立即提交讼稚。

這樣如果連續(xù)插入100次數(shù)據(jù)實(shí)際是創(chuàng)建事務(wù)->執(zhí)行語句->提交這個(gè)過程被重復(fù)執(zhí)行了100次。如果我們顯示的創(chuàng)建事務(wù)->執(zhí)行100條語句->提交會(huì)使得這個(gè)創(chuàng)建事務(wù)和提交這個(gè)過程只做一次绕沈,通過這種一次性事務(wù)可以使得性能大幅提升锐想。尤其當(dāng)數(shù)據(jù)庫位于sd卡時(shí),時(shí)間上能節(jié)省兩個(gè)數(shù)量級左右乍狐。

Sqlte顯示使用事務(wù)赠摇,示例代碼如下:

publicvoidinsertWithOneTransaction(){

SQLiteDatabasedb=sqliteOpenHelper.getWritableDatabase();

// Begins a transaction

db.beginTransaction();

try{

// your sqls

for(inti=0;i<100;i++){

db.insert(yourTableName,null,value);

}

// marks the current transaction as successful

db.setTransactionSuccessful();

}catch(Exceptione){

// process it

e.printStackTrace();

}finally{

// end a transaction

db.endTransaction();

}

}

其中sqliteOpenHelper.getWritableDatabase()表示得到寫表權(quán)限。

3、其他針對Sqlite的優(yōu)化

(1) 語句的拼接使用StringBuilder代替String

這個(gè)就不多說了蝉稳,簡單的string相加會(huì)導(dǎo)致創(chuàng)建多個(gè)臨時(shí)對象消耗性能抒蚜。StringBuilder的空間預(yù)分配性能好得多。如果你對字符串的長度有大致了解耘戚,如100字符左右嗡髓,可以直接new StringBuilder(128)指定初始大小,減少空間不夠時(shí)的再次分配收津。

(2) 查詢時(shí)返回更少的結(jié)果集及更少的字段饿这。

查詢時(shí)只取需要的字段和結(jié)果集,更多的結(jié)果集會(huì)消耗更多的時(shí)間及內(nèi)存撞秋,更多的字段會(huì)導(dǎo)致更多的內(nèi)存消耗长捧。

(3) 少用cursor.getColumnIndex

根據(jù)性能調(diào)優(yōu)過程中的觀察cursor.getColumnIndex的時(shí)間消耗跟cursor.getInt相差無幾∥腔撸可以在建表的時(shí)候用static變量記住某列的index串结,直接調(diào)用相應(yīng)index而不是每次查詢。

publicstaticfinalStringHTTP_RESPONSE_TABLE_ID=android.provider.BaseColumns._ID;

publicstaticfinalStringHTTP_RESPONSE_TABLE_RESPONSE="response";

publicListgetData(){

……

cursor.getString(cursor.getColumnIndex(HTTP_RESPONSE_TABLE_RESPONSE));

……

}

優(yōu)化為

publicstaticfinalStringHTTP_RESPONSE_TABLE_ID=android.provider.BaseColumns._ID;

publicstaticfinalStringHTTP_RESPONSE_TABLE_RESPONSE="response";

publicstaticfinalintHTTP_RESPONSE_TABLE_ID_INDEX=0;

publicstaticfinalintHTTP_RESPONSE_TABLE_URL_INDEX=1;

publicListgetData(){

……

cursor.getString(HTTP_RESPONSE_TABLE_RESPONSE_INDEX);

……

}

4舅列、異步線程

Sqlite是常用于嵌入式開發(fā)中的關(guān)系型數(shù)據(jù)庫肌割,完全開源。

與Web常用的數(shù)據(jù)庫Mysql帐要、Oracle db把敞、sql server不同,Sqlite是一個(gè)內(nèi)嵌式的數(shù)據(jù)庫榨惠,數(shù)據(jù)庫服務(wù)器就在你的程序中奋早,無需網(wǎng)絡(luò)配置和管理,數(shù)據(jù)庫服務(wù)器端和客戶端運(yùn)行在同一進(jìn)程內(nèi)赠橙,減少了網(wǎng)絡(luò)訪問的消耗耽装,簡化了數(shù)據(jù)庫管理。不過Sqlite在并發(fā)期揪、數(shù)據(jù)庫大小掉奄、網(wǎng)絡(luò)方面存在局限性,并且為表級鎖横侦,所以也沒必要多線程操作挥萌。

Android中數(shù)據(jù)不多時(shí)表查詢可能耗時(shí)不多,不會(huì)導(dǎo)致anr枉侧,不過大于100ms時(shí)同樣會(huì)讓用戶感覺到延時(shí)和卡頓引瀑,可以放在線程中運(yùn)行,但sqlite在并發(fā)方面存在局限榨馁,多線程控制較麻煩憨栽,這時(shí)候可使用單線程池,在任務(wù)中執(zhí)行db操作,通過handler返回結(jié)果和ui線程交互屑柔,既不會(huì)影響UI線程屡萤,同時(shí)也能防止并發(fā)帶來的異常。

可使用Android提供的AsyncQueryHandler(感謝@內(nèi)網(wǎng)無法登陸賬號 反饋)或類似如下代碼完成:

ExecutorServicesingleThreadExecutor=Executors.newSingleThreadExecutor();

singleThreadExecutor.execute(newRunnable(){

@Override

publicvoidrun(){

// db operetions, u can use handler to send message after

db.insert(yourTableName,null,value);

handler.sendEmptyMessage(xx);

}

});

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末掸宛,一起剝皮案震驚了整個(gè)濱河市死陆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌唧瘾,老刑警劉巖措译,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異饰序,居然都是意外死亡领虹,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門求豫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來塌衰,“玉大人,你說我怎么就攤上這事蝠嘉∽罱” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵是晨,是天一觀的道長肚菠。 經(jīng)常有香客問我舔箭,道長罩缴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任层扶,我火速辦了婚禮箫章,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘镜会。我一直安慰自己檬寂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布戳表。 她就那樣靜靜地躺著桶至,像睡著了一般。 火紅的嫁衣襯著肌膚如雪匾旭。 梳的紋絲不亂的頭發(fā)上镣屹,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天,我揣著相機(jī)與錄音价涝,去河邊找鬼女蜈。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的伪窖。 我是一名探鬼主播逸寓,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼覆山!你這毒婦竟也來了竹伸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤簇宽,失蹤者是張志新(化名)和其女友劉穎佩伤,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晦毙,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡生巡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了见妒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孤荣。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖须揣,靈堂內(nèi)的尸體忽然破棺而出盐股,到底是詐尸還是另有隱情,我是刑警寧澤耻卡,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布疯汁,位于F島的核電站,受9級特大地震影響卵酪,放射性物質(zhì)發(fā)生泄漏幌蚊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一溃卡、第九天 我趴在偏房一處隱蔽的房頂上張望溢豆。 院中可真熱鬧,春花似錦瘸羡、人聲如沸漩仙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽队他。三九已至,卻和暖如春峻村,著一層夾襖步出監(jiān)牢的瞬間麸折,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工雀哨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留磕谅,地道東北人私爷。 一個(gè)月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像膊夹,于是被迫代替她去往敵國和親衬浑。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

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