特別說明:程序員
一、 本文只是面對數據庫應用開發的程序員,不適合專業DBA,DBA在數據庫性能優化方面須要瞭解更多的知識;數據庫
二、 本文許多示例及概念是基於Oracle數據庫描述,對於其它關係型數據庫也能夠參考,但許多觀點不適合於KV數據庫或內存數據庫或者是基於SSD技術的數據庫;緩存
三、 本文未深刻數據庫優化中最核心的執行計劃分析技術。性能優化
讀者對像:服務器
開發人員:若是你是作數據庫開發,那本文的內容很是適合,由於本文是從程序員的角度來談數據庫性能優化。網絡
架構師:若是你已是數據庫應用的架構師,那本文的知識你應該清楚90%,不然你多是一個喜歡折騰的架構師。架構
DBA(數據庫管理員):大型數據庫優化的知識很是複雜,本文只是從程序員的角度來談性能優化,DBA除了須要瞭解這些知識外,還須要深刻數據庫的內部體系架構來解決問題。ide
引言函數
在網上有不少文章介紹數據庫優化知識,可是大部份文章只是對某個一個方面進行說明,而對於咱們程序員來講這種介紹並不能很好的掌握優化知識,由於不少介紹只是對一些特定的場景優化的,因此反而有時會產生誤導或讓程序員感受不明白其中的奧妙而對數據庫優化感受很神祕。性能
不少程序員老是問如何學習數據庫優化,有沒有好的教材之類的問題。在書店也看到了許多數據庫優化的專業書籍,可是感受更可能是面向DBA或者是PL/SQL開 發方面的知識,我的感受不太適合普通程序員。而要想作到數據庫優化的高手,不是花幾周,幾個月就能達到的,這並非由於數據庫優化有多高深,而是由於要作 好優化一方面須要有很是好的技術功底,對操做系統、存儲硬件網絡、數據庫原理等方面有比較紮實的基礎知識,另外一方面是須要花大量時間對特定的數據庫進行實 踐測試與總結。
做爲一個程序員,咱們也許不清楚線上正式的服務器硬件配置,咱們不可能像DBA那樣專業的對數據庫進行各類實踐測試與總結,但咱們都應該很是瞭解咱們SQL的業務邏輯,咱們清楚SQL中訪問表及字段的數據狀況,咱們其實只關心咱們的SQL是否能儘快返回結果。那程序員如何利用已知的知識進行數據庫優化?如何能快速定位SQL性能問題並找到正確的優化方向?
面對這些問題,筆者總結了一些面向程序員的基本優化法則,本文將結合實例來坦述數據庫開發的優化知識。
要正確的優化SQL,咱們須要快速定位能性的瓶頸點,也就是說快速找到咱們SQL主要的開銷在哪裏?而大多數狀況性能最慢的設備會是瓶頸點,以下載時網絡速度可能會是瓶頸點,本地複製文件時硬盤可能會是瓶頸點,爲何這些通常的工做咱們能快速確認瓶頸點呢,由於咱們對這些慢速設備的性能數據有一些基本的認識,如網絡帶寬是2Mbps,硬盤是每分鐘7200轉等等。所以,爲了快速找到SQL的性能瓶頸點,咱們也須要了解咱們計算機系統的硬件基本性能指標,下圖展現的當前主流計算機性能指標數據。
從圖上能夠看到基本上每種設備都有兩個指標:
延時(響應時間):表示硬件的突發處理能力;
帶寬(吞吐量):表明硬件持續處理能力。
從上圖能夠看出,計算機系統硬件性能從高到代依次爲:
CPU——Cache(L1-L2-L3)——內存——SSD硬盤——網絡——硬盤
因爲SSD硬盤還處於快速發展階段,因此本文的內容不涉及SSD相關應用系統。
根據數據庫知識,咱們能夠列出每種硬件主要的工做內容:
CPU及內存:緩存數據訪問、比較、排序、事務檢測、SQL解析、函數或邏輯運算;
網絡:結果數據傳輸、SQL請求、遠程數據庫訪問(dblink);
硬盤:數據訪問、數據寫入、日誌記錄、大數據量排序、大表鏈接。
根據當前計算機硬件的基本性能指標及其在數據庫中主要操做內容,能夠整理出以下圖所示的性能基本優化法則:
這個優化法則概括爲5個層次:
一、 減小數據訪問(減小磁盤訪問)
二、 返回更少數據(減小網絡傳輸或磁盤訪問)
三、 減小交互次數(減小網絡傳輸)
四、 減小服務器CPU開銷(減小CPU及內存開銷)
五、 利用更多資源(增長資源)
因爲每一層優化法則都是解決其對應硬件的性能問題,因此帶來的性能提高比例也不同。傳統數據庫系統設計是也是儘量對低速設備提供優化方法,所以針對低速設備問題的可優化手段也更多,優化成本也更低。咱們任何一個SQL的性能優化都應該按這個規則由上到下來診斷問題並提出解決方案,而不該該首先想到的是增長資源解決問題。
如下是每一個優化法則層級對應優化效果及成本經驗參考:
優化法則 |
性能提高效果 |
優化成本 |
減小數據訪問 |
1~1000 |
低 |
返回更少數據 |
1~100 |
低 |
減小交互次數 |
1~20 |
低 |
減小服務器CPU開銷 |
1~5 |
低 |
利用更多資源 |
@~10 |
高 |
接下來,咱們針對5種優化法則列舉經常使用的優化手段並結合實例分析。
數據塊是數據庫中數據在磁盤中存儲的最小單位,也是一次IO訪問的最小單位,一個數據塊一般能夠存儲多條記錄,數據塊大小是DBA在建立數據庫或表空間時指定,可指定爲2K、4K、8K、16K或32K字節。下圖是一個Oracle數據庫典型的物理結構,一個數據庫能夠包括多個數據文件,一個數據文件內又包含多個數據塊;
ROWID是每條記錄在數據庫中的惟一標識,經過ROWID能夠直接定位記錄到對應的文件號及數據塊位置。ROWID內容包括文件號、對像號、數據塊號、記錄槽號,以下圖所示:
數據庫索引的原理很是簡單,但在複雜的表中真正能正確使用索引的人不多,即便是專業的DBA也不必定能徹底作到最優。
索引會大大增長表記錄的DML(INSERT,UPDATE,DELETE)開銷,正確的索引可讓性能提高100,1000倍以上,不合理的索引也可能會讓性能降低100倍,所以在一個表中建立什麼樣的索引須要平衡各類業務需求。
索引常見問題:
索引有哪些種類?
常見的索引有B-TREE索引、位圖索引、全文索引,位圖索引通常用於數據倉庫應用,全文索引因爲使用較少,這裏不深刻介紹。B-TREE索引包括不少擴展類型,如組合索引、反向索引、函數索引等等,如下是B-TREE索引的簡單介紹:
B-TREE索引也稱爲平衡樹索引(Balance Tree),它是一種按字段排好序的樹形目錄結構,主要用於提高查詢性能和惟一約束支持。B-TREE索引的內容包括根節點、分支節點、葉子節點。
葉子節點內容:索引字段內容+表記錄ROWID
根節點,分支節點內容:當一個數據塊中不能放下全部索引字段數據時,就會造成樹形的根節點或分支節點,根節點與分支節點保存了索引樹的順序及各層級間的引用關係。
一個普通的BTREE索引結構示意圖以下所示:
若是咱們把一個表的內容認爲是一本字典,那索引就至關於字典的目錄,以下圖所示:
圖中是一個字典按部首+筆劃數的目錄,至關於給字典建了一個按部首+筆劃的組合索引。
一個表中能夠建多個索引,就如一本字典能夠建多個目錄同樣(按拼音、筆劃、部首等等)。
一個索引也能夠由多個字段組成,稱爲組合索引,如上圖就是一個按部首+筆劃的組合目錄。
SQL什麼條件會使用索引?
當字段上建有索引時,一般如下狀況會使用索引:
INDEX_COLUMN = ?
INDEX_COLUMN > ?
INDEX_COLUMN >= ?
INDEX_COLUMN < ?
INDEX_COLUMN <= ?
INDEX_COLUMN between ? and ?
INDEX_COLUMN in (?,?,...,?)
INDEX_COLUMN like ?||'%'(後導模糊查詢)
T1. INDEX_COLUMN=T2. COLUMN1(兩個表經過索引字段關聯)
SQL什麼條件不會使用索引?
查詢條件 |
不能使用索引緣由 |
INDEX_COLUMN <> ? INDEX_COLUMN not in (?,?,...,?) |
不等於操做不能使用索引 |
function(INDEX_COLUMN) = ? INDEX_COLUMN + 1 = ? INDEX_COLUMN || 'a' = ? |
通過普通運算或函數運算後的索引字段不能使用索引 |
INDEX_COLUMN like '%'||? INDEX_COLUMN like '%'||?||'%' |
含前導模糊查詢的Like語法不能使用索引 |
INDEX_COLUMN is null |
B-TREE索引裏不保存字段爲NULL值記錄,所以IS NULL不能使用索引 |
NUMBER_INDEX_COLUMN='12345' CHAR_INDEX_COLUMN=12345 |
Oracle在作數值比較時須要將兩邊的數據轉換成同一種數據類型,若是兩邊數據類型不一樣時會對字段值隱式轉換,至關於加了一層函數處理,因此不能使用索引。 |
a.INDEX_COLUMN=a.COLUMN_1 |
給索引查詢的值應是已知數據,不能是未知字段值。 |
注: 通過函數運算字段的字段要使用可使用函數索引,這種需求建議與DBA溝通。 有時候咱們會使用多個字段的組合索引,若是查詢條件中第一個字段不能使用索引,那整個查詢也不能使用索引 如:咱們company表建了一個id+name的組合索引,如下SQL是不能使用索引的 Select * from company where name=? Oracle9i後引入了一種index skip scan的索引方式來解決相似的問題,可是經過index skip scan提升性能的條件比較特殊,使用很差反而性能會更差。
|
咱們通常在什麼字段上建索引?
這是一個很是複雜的話題,須要對業務及數據充分分析後再能得出結果。主鍵及外鍵一般都要有索引,其它須要建索引的字段應知足如下條件:
1、字段出如今查詢條件中,而且查詢條件可使用索引;
2、語句執行頻率高,一天會有幾千次以上;
3、經過字段條件可篩選的記錄集很小,那數據篩選比例是多少才適合?
這個沒有固定值,須要根據表數據量來評估,如下是經驗公式,可用於快速評估:
小表(記錄數小於10000行的表):篩選比例<10%;
大表:(篩選返回記錄數)<(表總記錄數*單條記錄長度)/10000/16
單條記錄長度≈字段平均內容長度之和+字段數*2
如下是一些字段是否須要建B-TREE索引的經驗分類:
字段類型 |
常見字段名 |
|
須要建索引的字段 |
主鍵 |
ID,PK |
外鍵 |
PRODUCT_ID,COMPANY_ID,MEMBER_ID,ORDER_ID,TRADE_ID,PAY_ID |
|
有對像或身份標識意義字段 |
HASH_CODE,USERNAME,IDCARD_NO,EMAIL,TEL_NO,IM_NO |
|
索引慎用字段,須要進行數據分佈及使用場景詳細評估 |
日期 |
GMT_CREATE,GMT_MODIFIED |
年月 |
YEAR,MONTH |
|
狀態標誌 |
PRODUCT_STATUS,ORDER_STATUS,IS_DELETE,VIP_FLAG |
|
類型 |
ORDER_TYPE,IMAGE_TYPE,GENDER,CURRENCY_TYPE |
|
區域 |
COUNTRY,PROVINCE,CITY |
|
操做人員 |
CREATOR,AUDITOR |
|
數值 |
LEVEL,AMOUNT,SCORE |
|
長字符 |
ADDRESS,COMPANY_NAME,SUMMARY,SUBJECT |
|
不適合建索引的字段 |
描述備註 |
DESCRIPTION,REMARK,MEMO,DETAIL |
大字段 |
FILE_CONTENT,EMAIL_CONTENT |
如何知道SQL是否使用了正確的索引?
簡單SQL能夠根據索引使用語法規則判斷,複雜的SQL很差辦,判斷SQL的響應時間是一種策略,可是這會受到數據量、主機負載及緩存等因素的影響,有時數據全在緩存裏,可能全表訪問的時間比索引訪問時間還少。要準確知道索引是否正確使用,須要到數據庫中查看SQL真實的執行計劃,這個話題比較複雜,詳見SQL執行計劃專題介紹。
索引對DML(INSERT,UPDATE,DELETE)附加的開銷有多少?
這個沒有固定的比例,與每一個表記錄的大小及索引字段大小密切相關,如下是一個普通表測試數據,僅供參考:
索引對於Insert性能下降56%
索引對於Update性能下降47%
索引對於Delete性能下降29%
所以對於寫IO壓力比較大的系統,表的索引須要仔細評估必要性,另外索引也會佔用必定的存儲空間。