MySQL優化
前言
從程序員角度談數據庫性能的優化
開發人員:如果你是做數據庫開發,那本文的內容非常適合,因爲本文是從程序員的角度來談數據庫性能優化。
架構師:如果你已經是數據庫應用的架構師,那本文的知識你應該清楚90%,否則你可能是一個喜歡折騰的架構師。
DBA(數據庫管理員):大型數據庫優化的知識非常複雜,本文只是從程序員的角度來談性能優化,DBA除了需要了解這些知識外,還需要深入數據庫的內部體系架構來解決問題。
主要是優化數據訪問層:
(1)一方面是側重數據庫原理,例如創建索引等
(2)一方面從SQL語句優化來提高數據查詢效率
一、Rowid和Rownum
Rowid和Rownum對於數據庫開發人員來說基本很少用到,因爲在企業數據庫開發中大多都是進行數據批處理,但是對於其他數據庫人員來說還是會用到的。
rowid和rownum都是虛列,但含義完全不同。rowid是物理地址,用於定位oracle中具體數據的物理存儲位置,而rownum則是sql的輸出結果排序。通俗的講:rowid是相對不變的,rownum會變化,尤其是使用order by的時候。
rowid 用於定位數據表中某條數據的位置,是唯一的、也不會改變
rownum 表示查詢某條記錄在整個結果集中的位置, 同一條記錄查詢條件不同對應的 rownum 是不同的而 rowid 是不會變的
二、MySQL數據庫的優化
數據庫的優化分爲三大類:
CPU及內存:緩存數據訪問、比較、排序、事務檢測、SQL解析、函數或邏輯運算;
網絡:結果數據傳輸、SQL請求、遠程數據庫訪問(dblink);
硬盤:數據訪問、數據寫入、日誌記錄、大數據量排序、大表連接。
影響數據傳輸的因素主要爲延遲和帶寬:效率排序:
cup的處理>網絡傳輸數據>訪問磁盤
顯然,在訪問磁盤時效率最低。
所以,優化數據庫,主要是對數據訪問的優化,即減少訪問的數據。
一、減少數據訪問
創建索引:減少數據的訪問
索引:索引是一種特殊的文件(InnoDB數據表上的索引是表空間的一個組成部分),它們包含着對數據表裏所有記錄的引用指針。更通俗的說,數據庫索引好比是一本書前面的目錄,能加快數據庫的查詢速度。在沒有索引的情況下,數據庫會遍歷全部數據後選擇符合條件的;而有了相應的索引之後,數據庫會直接在索引中查找符合條件的選項。(注:一般數據庫默認都會爲主鍵生成索引)。
PS:索引分爲聚簇索引和非聚簇索引兩種,聚簇索引是按照數據存放的物理位置爲順序的,而非聚簇索引就不一樣了;聚簇索引能提高多行檢索的速度,而非聚簇索引對於單行的檢索很快。
1、索引的分類:
邏輯上:普通索引、唯一索引、單列索引、多列索引、組合索引(最左前綴)
物理上:B-tree索引、Full-text索引(全文索引)、R-tree索引。
一個表中可以建多個索引,就如一本字典可以建多個目錄一樣(按拼音、筆劃、部首等等)。
一個索引也可以由多個字段組成,稱爲組合索引,如上圖就是一個按部首+筆劃的組合目錄。
2、什麼字段上創建索引
(1)語句執行頻率高,一天會有幾千次以上;
(2)作爲查詢條件的字段。
(3)通過字段條件可篩選的記錄集很小。
(4)描述備註、大字段不適合創建索引。
3、不適合創建索引的情況
(1)簡單SQL可以根據索引使用語法規則判斷,複雜的SQL不好辦,判斷SQL的響應時間是一種策略,但是這會受到數據量、主機負載及緩存等因素的影響,有時數據全在緩存裏,可能全表訪問的時間比索引訪問時間還少。要準確知道索引是否正確使用,需要到數據庫中查看SQL真實的執行計劃;
(2)索引也是佔用存儲空間的,此外,索引對DML(insert、update、delete)語句的性能也有影響。
4、不用索引的情況
(1)應儘量避免在 where 子句中使用 != 或 <> 操作符,否則將導致引擎放棄使用索引而進行全表掃描。
(2)應儘量避免在 where 子句中使用 or 來連接條件,如果一個字段有索引,一個字段沒有索引,將導致引擎放棄使用索引而進行全表掃描,如:
select id from twhere num=10 or Name = 'admin'
可以這樣查詢:
select id from twhere num = 10
union all
select id from twhere Name = 'admin'。
5、索引可不能亂建!!!
我記得去年幫同事整理一個大表的數據,從中獲取6000萬的數據,插入到新表中,沒索引時,3小時插入完成,只建了一個索引,9個多小時完成。
6、
SQL語句優化:減少數據的訪問
1、對查詢進行優化,要儘量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。
2、注意SQL語句的優化,減少全表掃描,如:
(1)下面的查詢也將導致全表掃描:
select id from twhere name like ‘%abc%’
若要提高效率,可以考慮全文檢索。
(2)儘量不用*,需要什麼字段,就查詢什麼字段。
3、表字段的選擇
(1)整型>data,time>enum,char,varchar>blob;整數類型的列字段運算快,節省空間。
(2)大的字段浪費內存,影響速度,如:以varchar(10),varchar(300)存儲的內容相同,但在聯查時varchar(300)要花更多內存。
(3)減少null的使用,佔用的磁盤空間大。
(4)減少數據庫中列對null的使用,儘量使用not null。
(5)儘可能的使用 varchar/nvarchar 代替 char/nchar ,因爲首先變長字段存儲空間小,可以節省存儲空間,其次對於查詢來說,在一個相對較小的字段內搜索效率顯然要高些。
4、儘量減少可以避免的日誌
(1)刪除表中所有數據時,用TRUNCATE,DELETE 語句每次刪除一行,並在事務日誌中爲所刪除的每行記錄一項。TRUNCATE TABLE 通過釋放用於存儲表數據的數據頁來刪除數據,並且在事務日誌中只記錄頁釋放,使用的鎖通常較少。
(2)Update 語句,如果只更改1、2個字段,不要Update全部字段,否則頻繁調用會引起明顯的性能消耗,同時帶來大量日誌。
(3)在新建臨時表時,如果一次性插入數據量很大,那麼可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果數據量不大,爲了緩和系統表的資源,應先create table,然後insert。
5、儘量避免大事務操作,提高系統併發能力。
6、儘量避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。
二、減少返回數據
1、分頁
2、只返回需要的字段
三、減少交互次數
[1]批量提交batch
[2]批量返回:設置fetch size屬性,增加服務器向客戶端的每次請求的返回數據量
[3]根據ID查詢時使用in
[4]使用存儲過程
[5]優化業務邏輯
(1)batch(批量)提交數據,減少客戶端與服務端的交互次數。
一般數據庫都會提供批量提交接口,採用batch操作一般不會減少很多數據庫服務器的物理IO,但是會大大減少客戶端與服務端的交互次數,從而減少了多次發起的網絡延時開銷,同時也會降低數據庫的CPU開銷。
(2)使用 In List
即我們會經常根據id進行查詢數據,可以寫一個SQL語句:
for :var in ids[] do begin
select * frommytable where id=:var;
end;
但是可以做一個優化,即用id用INLIST的形式來寫。
select * from mytable where id in(:id1,id2,...,idn);
這樣可以大大減少對數據庫發起SQL的請求數,從而提高性能。但是in中的id數並非是可以無限放的。
【1】數據庫本身有限制;
【2】數據庫的基於成本的優化規則,可能改變SQL執行計劃,由索引變爲全表掃描,佔用內存會變大。
首先大部份數據庫都會有SQL長度和IN裏個數的限制,如ORACLE的IN裏就不允許超過1000個值。
另外當前數據庫一般都是採用基於成本的優化規則,當IN數量達到一定值時有可能改變SQL執行計劃,從索引訪問變成全表訪問,這將使性能急劇變化。隨着SQL中IN的裏面的值個數增加,SQL的執行計劃會更復雜,佔用的內存將會變大,這將會增加服務器CPU及內存成本。評估在IN裏面一次放多少個值還需要考慮應用服務器本地內存的開銷,有併發訪問時要計算本地數據使用週期內的併發上限,否則可能會導致內存溢出。
綜合考慮,一般IN裏面的值個數超過20個以後性能基本沒什麼太大變化,也特別說明不要超過100,超過後可能會引起執行計劃的不穩定性及增加數據庫CPU及內存成本,這個需要專業DBA評估。
(3)設置Fetch Size
【1】由數據庫返回客戶端時,fetch size屬性在數據訪問層框架可以設置
【2】當返回數據量較大時,一般設爲100
【3】fetchsize並不會存在一個最優的固定值,因爲整體性能與記錄集大小及硬件平臺有關
【4】etchsize不能設置太大,如果一次取出的數據大於JVM的內存會導致內存溢出,所以建議不要超過1000,太大了也沒什麼性能提高,反而可能會增加內存溢出的危險。
當我們採用select從數據庫查詢數據時,數據默認並不是一條一條返回給客戶端的,也不是一次全部返回客戶端的,而是根據客戶端fetch_size參數處理,每次只返回fetch_size條記錄,當客戶端遊標遍歷到尾部時再從服務端取數據,直到最後全部傳送完成。所以如果我們要從服務端一次取大量數據時,可以加大fetch_size,這樣可以減少結果數據傳輸的交互次數及服務器數據準備時間,提高性能。
(1)使用存儲過程
(2)優化業務邏輯
(3)使用Resultset遊標處理記錄
四、減少數據庫服務器CPU運算
(1)使用綁定變量
綁定變量是指SQL中對變化的值採用變量參數的形式提交,而不是在SQL中直接拼寫對應的值。
非綁定變量寫法:Select * fromemployee where id=1234567
綁定變量寫法:
Select *from employee where id=?
Preparestatement.setInt(1,1234567)
Java中Preparestatement就是爲處理綁定變量提供的對像,綁定變量有以下優點:
1、防止SQL注入
2、提高SQL可讀性
3、提高SQL解析性能,不使用綁定變更我們一般稱爲硬解析,使用綁定變量我們稱爲軟解析。
(2)合理使用排序
(3)減少比較操作
(4)大量複雜運算在客戶端處理
五、利用更多的資源
(1)客戶端多進程並行訪問
(2)數據庫並行處理