性能優化之數據庫優化

本文性能優化之數據庫優化,原理適用於大部分數據庫包括Sqlite、Mysql、Oracle、Sql server,詳細介紹了索引(優缺點、分類、場景、規則)和事務,最後介紹了部分單獨針對Sqlite的優化。html

一、索引java

簡單的說,索引就像書本的目錄,目錄能夠快速找到所在頁數,數據庫中索引能夠幫助快速找到數據,而不用全表掃描,合適的索引能夠大大提升數據庫查詢的效率。android

(1). 優勢sql

大大加快了數據庫檢索的速度,包括對單表查詢、連表查詢、分組查詢、排序查詢。常常是一到兩個數量級的性能提高,且隨着數據數量級增加。數據庫

(2). 缺點安全

索引的建立和維護存在消耗,索引會佔用物理空間,且隨着數據量的增長而增長。
在對數據庫進行增刪改時須要維護索引,因此會對增刪改的性能存在影響。性能優化

(3). 分類服務器

a. 直接建立索引和間接建立索引網絡

直接建立: 使用sql語句建立,Android中能夠在SQLiteOpenHelper的onCreate或是onUpgrade中直接excuSql建立語句,語句如多線程


1

CREATE INDEX mycolumn_index ON mytable (myclumn)

間接建立: 定義主鍵約束或者惟一性鍵約束,能夠間接建立索引,主鍵默認爲惟一索引。

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

普通索引:

 

1

CREATE INDEX mycolumn_index ON mytable (myclumn)

惟一性索引:保證在索引列中的所有數據是惟一的,對聚簇索引和非聚簇索引均可以使用,語句爲

 

1

CREATE UNIQUE COUSTERED INDEX myclumn_cindex ON mytable(mycolumn)

c. 單個索引和複合索引

單個索引:索引創建語句中僅包含單個字段,如上面的普通索引和惟一性索引建立示例。
複合索引:又叫組合索引,在索引創建語句中同時包含多個字段,語句如:

1

CREATE INDEX name_index ON username(firstname, lastname)

其中firstname爲前導列。

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

聚簇索引:物理索引,與基表的物理順序相同,數據值的順序老是按照順序排列,語句爲:

 

1

CREATE CLUSTERED INDEX mycolumn_cindex ON mytable(mycolumn) WITH ALLOW_DUP_ROW

其中WITH ALLOW_DUP_ROW表示容許有重複記錄的聚簇索引

非聚簇索引:

 

1

CREATE UNCLUSTERED INDEX mycolumn_cindex ON mytable(mycolumn)

索引默認爲非聚簇索引

(4). 使用場景

在上面講到了優缺點,那麼確定會對什麼時候使用索引既有點明白又有點糊塗吧,那麼下面總結下:

a.  當某字段數據更新頻率較低,查詢頻率較高,常常有範圍查詢(>, <, =, >=, <=)或order by、group by發生時建議使用索引。而且選擇度越大,建索引越有優點,這裏選擇度指一個字段中惟一值的數量/總的數量。

b.  常常同時存取多列,且每列都含有重複值可考慮創建複合索引

(5). 索引使用規則

a.  對於複合索引,把使用最頻繁的列作爲前導列(索引中第一個字段)。若是查詢時前導列不在查詢條件中則該複合索引不會被使用。

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

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

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

b.  避免對索引列進行計算,對where子句列的任何計算若是不能被編譯優化,都會致使查詢時索引失效

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

c.  比較值避免使用NULL

d.  多表查詢時要注意是選擇合適的表作爲內表。鏈接條件要充份考慮帶有索引的表、行數多的表,內外表的選擇可由公式:外層表中的匹配行數*內層表中每一次查找的次數肯定,乘積最小爲最佳方案。實際多表操做在被實際執行前,查詢優化器會根據鏈接條件,列出幾組可能的鏈接方案並從中找出系統開銷最小的最佳方案。

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

f.  用多表鏈接代替EXISTS子句

g.  把過濾記錄數最多的條件放在最前面

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

(6)索引檢驗

創建了索引,對於某條sql語句是否使用到了索引能夠經過執行計劃查看是否用到了索引。

二、使用事務

使用事務的兩大好處是原子提交和更優性能。

(1) 原子提交

原則提交意味着同一事務內的全部修改要麼都完成要麼都不作,若是某個修改失敗,會自動回滾使得全部修改不生效。

(2) 更優性能

Sqlite默認會爲每一個插入、更新操做建立一個事務,而且在每次插入、更新後當即提交。

這樣若是連續插入100次數據實際是建立事務->執行語句->提交這個過程被重複執行了100次。若是咱們顯示的建立事務->執行100條語句->提交會使得這個建立事務和提交這個過程只作一次,經過這種一次性事務可使得性能大幅提高。尤爲當數據庫位於sd卡時,時間上能節省兩個數量級左右。

Sqlte顯示使用事務,示例代碼以下:

public void insertWithOneTransaction() {
     SQLiteDatabase db = sqliteOpenHelper.getWritableDatabase();
     // Begins a transaction
     db.beginTransaction();
     try {
         // your sqls
         for (int i = 0; i < 100; i++) {
              db.insert(yourTableName, null, value);
         }
 
         // marks the current transaction as successful
         db.setTransactionSuccessful();
     } catch (Exception e) {
         // process it
         e.printStackTrace();
     } finally {
         // end a transaction
         db.endTransaction();
     }
}

 

其中sqliteOpenHelper.getWritableDatabase()表示獲得寫表權限。

三、其餘針對Sqlite的優化

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

這個就很少說了,簡單的string相加會致使建立多個臨時對象消耗性能。StringBuilder的空間預分配性能好得多。若是你對字符串的長度有大體瞭解,如100字符左右,能夠直接new StringBuilder(128)指定初始大小,減小空間不夠時的再次分配。

(2) 查詢時返回更少的結果集及更少的字段。

查詢時只取須要的字段和結果集,更多的結果集會消耗更多的時間及內存,更多的字段會致使更多的內存消耗。

(3) 少用cursor.getColumnIndex

根據性能調優過程當中的觀察cursor.getColumnIndex的時間消耗跟cursor.getInt相差無幾。能夠在建表的時候用static變量記住某列的index,直接調用相應index而不是每次查詢

public static final String HTTP_RESPONSE_TABLE_ID = android.provider.BaseColumns._ID;
public static final String HTTP_RESPONSE_TABLE_RESPONSE = "response";
public List<Object> getData() {
      ……
      cursor.getString(cursor.getColumnIndex(HTTP_RESPONSE_TABLE_RESPONSE));
      ……
}

 

優化爲

public static final String HTTP_RESPONSE_TABLE_ID = android.provider.BaseColumns._ID;
public static final String HTTP_RESPONSE_TABLE_RESPONSE = "response";
public static final int HTTP_RESPONSE_TABLE_ID_INDEX = 0;
public static final int HTTP_RESPONSE_TABLE_URL_INDEX = 1;
public List<Object> getData() {
     ……
     cursor.getString(HTTP_RESPONSE_TABLE_RESPONSE_INDEX);
     ……
}

 

四、異步線程

Sqlite是經常使用於嵌入式開發中的關係型數據庫,徹底開源。

與Web經常使用的數據庫Mysql、Oracle db、sql server不一樣,Sqlite是一個內嵌式的數據庫,數據庫服務器就在你的程序中,無需網絡配置和管理,數據庫服務器端和客戶端運行在同一進程內,減小了網絡訪問的消耗,簡化了數據庫管理。不過Sqlite在併發、數據庫大小、網絡方面存在侷限性,而且爲表級鎖,因此也不必多線程操做。

Android中數據很少時表查詢可能耗時很少,不會致使anr,不過大於100ms時一樣會讓用戶感受到延時和卡頓,能夠放在線程中運行,但sqlite在併發方面存在侷限,多線程控制較麻煩,這時候可以使用單線程池,在任務中執行db操做,經過handler返回結果和ui線程交互,既不會影響UI線程,同時也能防止併發帶來的異常。關於性能的優化,還要知道怎麼樣去採起措施保護源代碼,這點可諮詢移動信息安全智能服務提供商——愛加密,加密技術!

可以使用Android提供的AsyncQueryHandler或相似以下代碼完成:

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor.execute(new Runnable() {

     @Override
     public void run() {
         // db operetions, u can use handler to send message after
         db.insert(yourTableName, null, value);
         handler.sendEmptyMessage(xx);
     }
});
相關文章
相關標籤/搜索