網上關於SQL優化的教程不少,可是比較雜亂。近日有空整理了一下,寫出來跟你們分享一下,其中有錯誤和不足的地方,還請你們糾正以及補充php
這篇文章我花費了大量的時間查找資料、修改但願你們閱讀以後,感受好的話推薦給更多的人,讓更多的人看到、糾正以及補充。mysql
要正確的優化SQL,咱們須要快速定位能性的瓶頸點,也就是說快速找到咱們SQL主要的開銷在哪裏?而大多數狀況性能最慢的設備會是瓶頸點,以下載時網絡速度可能會是瓶頸點,本地複製文件時硬盤可能會是瓶頸點,爲何這些通常的工做咱們能快速確認瓶頸點呢,由於咱們對這些慢速設備的性能數據有一些基本的認識,如網絡帶寬是2Mbps,硬盤是每分鐘7200轉等等。所以,爲了快速找到SQL的性能瓶頸點,咱們也須要了解咱們計算機系統的硬件基本性能指標,下圖展現的當前主流計算機性能指標數據sql
從圖上能夠看到基本上每種設備都有兩個指標:數據庫
延時(響應時間):表示硬件的突發處理能力;編程
帶寬(吞吐量):表明硬件持續處理能力。緩存
從上圖能夠看出,計算機系統硬件性能從高到代依次爲:安全
CPU——Cache(L1-L2-L3)——內存——SSD硬盤——網絡——硬盤性能優化
根據數據庫知識,咱們能夠列出每種硬件主要的工做內容:服務器
CPU及內存:緩存數據訪問、比較、排序、事務檢測、SQL解析、函數或邏輯運算;網絡
網絡:結果數據傳輸、SQL請求、遠程數據庫訪問(dblink);
硬盤:數據訪問、數據寫入、日誌記錄、大數據量排序、大表鏈接。
根據當前計算機硬件的基本性能指標及其在數據庫中主要操做內容,能夠整理出以下圖所示的性能基本優化法則:
這個優化法則概括爲5個層次:
一、 減小數據訪問(減小磁盤訪問)
二、 返回更少數據(減小網絡傳輸或磁盤訪問)
三、 減小交互次數(減小網絡傳輸)
四、 減小服務器CPU開銷(減小CPU及內存開銷)
五、 利用更多資源(增長資源)
因爲每一層優化法則都是解決其對應硬件的性能問題,因此帶來的性能提高比例也不同。傳統數據庫系統設計是也是儘量對低速設備提供優化方法,所以針對低速設備問題的可優化手段也更多,優化成本也更低。咱們任何一個SQL的性能優化都應該按這個規則由上到下來診斷問題並提出解決方案,而不該該首先想到的是增長資源解決問題。
如下是每一個優化法則層級對應優化效果及成本經驗參考:
優化法則 |
性能提高效果 |
優化成本 |
減小數據訪問 |
1~1000 |
低 |
返回更少數據 |
1~100 |
低 |
減小交互次數 |
1~20 |
低 |
減小服務器CPU開銷 |
1~5 |
低 |
利用更多資源 |
@~10 |
高 |
接下來,咱們針對5種優化法則列舉經常使用的優化手段
a: 表的設計合理化(符合3NF)
b: 優化SQL語句(索引)
c: 分表技術(水平分割、垂直分割)、分區技術
d: 讀寫[寫: update/delete/add]分離
e: 存儲過程 [模塊化編程,能夠提升速度]
f: 對mysql配置優化 [配置最大併發數, 調整緩存大小 ]
g: mysql服務器硬件升級
h: 定時的去清除不須要的數據,定時進行碎片整理
一、表的設計合理化(符合3NF)
1NF(第一範式)
1NF的限定條件以下:(只要數據庫是關係型數據庫,就自動的知足1NF)
1. 每一個列必須有一個惟一的名稱
2. 行和列的次序可有可無
3. 每一列都必須有單個數據類型
4. 不容許包含相同值的兩行
5. 每一列都必須包含一個單值 (一個列不能保存多個數據值)
6. 列不能包含重複的組
第一範式會存在更新、刪除和插入異常。
2NF(第二範式)
2NF的限定條件以下:(一般咱們設計一個主鍵來實現)
1. 它符合第一範式
2. 全部的非鍵值字段均依賴於全部的鍵值字段
第二範式也會存在更新、刪除和插入異常。
3NF(第三範式)
3NF的限定條件以下:
1. 符合2NF
2. 不包含傳遞相關性,(即,一個非鍵值字段的值依賴於另外一個非鍵值字段的值),不含冗餘數據
反3NF :沒有冗餘的數據庫未必是最好的數據庫,有時爲了提升運行效率,就必須下降範式標準,適當保留冗餘數據。
具體作法:
在概念數據模型設計時遵照第三範式,下降範式標準的工做放到物理數據模型設計時考慮。下降範式就是增長字段,容許冗餘。
二、優化SQL語句
(1)迅速的定位執行速度慢的語句
a 開啓慢查詢
b 設置慢查詢時間
c 啓用慢查詢日誌
d 經過mysqldumoslow工具對慢日誌進行分類彙總
(2)分析SQL語句
a 經過explain分析查詢
b 通profiling能夠獲得更詳細的信息
(3)SQL語句優化
a 建立索引(主鍵索引/惟一索引/全文索引/普通索引)
b 避免Select * (不查詢多餘的列與行)
c Where中少用NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE、OR,它們會忽略索引,引發全表掃描
d 用Where子句替代having子句,having只會在檢索出全部記錄以後纔對結果集進行過濾
e 使用視圖(常常被查詢的列數據,而且這些數據不被常常的修改,刪除)
數據庫索引的原理很是簡單,但在複雜的表中真正能正確使用索引的人不多,即便是專業的DBA也不必定能徹底作到最優。
索引會大大增長表記錄的DML(INSERT,UPDATE,DELETE)開銷,正確的索引可讓性能提高100,1000倍以上,不合理的索引也可能會讓性能降低100倍,所以在一個表中建立什麼樣的索引須要平衡各類業務需求。
若是咱們把一個表的內容認爲是一本字典,那索引就至關於字典的目錄,以下圖所示:
圖中是一個字典按部首+筆劃數的目錄,至關於給字典建了一個按部首+筆劃的組合索引。
一個表中能夠建多個索引,就如一本字典能夠建多個目錄同樣(按拼音、筆劃、部首等等)。
一個索引也能夠由多個字段組成,稱爲組合索引,如上圖就是一個按部首+筆劃的組合目錄。
咱們通常在什麼字段上建索引?
這是一個很是複雜的話題,須要對業務及數據充分分析後再能得出結果。主鍵及外鍵一般都要有索引,其它須要建索引的字段應知足如下條件:
a 字段出如今查詢條件中,而且查詢條件可使用索引;
b 語句執行頻率高,一天會有幾千次以上;
c 經過字段條件可篩選的記錄集很小,那數據篩選比例是多少才適合?
這個沒有固定值,須要根據表數據量來評估,如下是經驗公式,可用於快速評估:
小表(記錄數小於10000行的表):篩選比例<10%;
大表:(篩選返回記錄數)<(表總記錄數*單條記錄長度)/10000/16
單條記錄長度≈字段平均內容長度之和+字段數*2
如何知道SQL是否使用了正確的索引?
簡單SQL能夠根據索引使用語法規則判斷,複雜的SQL很差辦,判斷SQL的響應時間是一種策略,可是這會受到數據量、主機負載及緩存等因素的影響,有時數據全在緩存裏,可能全表訪問的時間比索引訪問時間還少。要準確知道索引是否正確使用,須要到數據庫中查看SQL真實的執行計劃,這個話題比較複雜,詳見SQL執行計劃專題介紹。
索引對DML(INSERT,UPDATE,DELETE)附加的開銷有多少?
這個沒有固定的比例,與每一個表記錄的大小及索引字段大小密切相關,如下是一個普通表測試數據,僅供參考:
索引對於Insert性能下降56%
索引對於Update性能下降47%
索引對於Delete性能下降29%
所以對於寫IO壓力比較大的系統,表的索引須要仔細評估必要性,另外索引也會佔用必定的存儲空間。
切記,性能優化是無止境的,當性能能夠知足需求時便可,不要過分優化。在實際數據庫中咱們不可能把每一個SQL請求的字段都建在索引裏,因此這種只經過索引訪問數據的方法通常只用於核心應用,也就是那種對核心表訪問量最高且查詢字段數據量不多的查詢
三、分表技術(水平分割、垂直分割)、分區技術
爲何要分表和分區?
若是遇到大表的狀況下,SQL語句優化已經沒法繼續優化了,咱們能夠考慮分表和分區,目的就是減小數據庫的負擔,提升數據庫的效率,一般點來說就是提升表 的增刪改查效率。
什麼是分表?
分表是將一個大表按照必定的規則分解成多張具備獨立存儲空間的實體表,咱們能夠稱爲子表,每一個表都對應三個文件,MYD數據文件,.MYI索引文件,.frm表結構文件。這些子表能夠分佈在同一塊磁盤上,也能夠在不一樣的機器上。app讀寫的時候根據事先定義好的規則獲得對應的子表名,而後去操做它。
什麼是分區?
分區和分表類似,都是按照規則分解表。不一樣在於分表將大表分解爲若干個獨立的實體表,而分區是將數據分段劃分在多個位置存放,能夠是同一塊磁盤也能夠在不一樣的機器。分區後,表面上仍是一張表,但數據散列到多個位置了。app讀寫的時候操做的仍是大表名字,db自動去組織分區的數據。
mysql分表和分區有什麼聯繫呢?
(1)都能提升mysql的性能,在高併發狀態下都有一個良好的表現。
(2)分表和分區不矛盾,能夠相互配合的,對於那些大訪問量,而且表數據比較多的表,咱們能夠採起分表和分區結合的方式,訪問量不大,可是表數據不少的表,咱們能夠採起分區的方式等。
(3)分表技術是比較麻煩的,須要手動去建立子表,app服務端讀寫時候須要計算子表名。採用merge好一些,但也要建立子表和配置子表間的union關係。
(4)表分區相對於分表,操做方便,不須要建立子表。
四、讀寫[寫: update/delete/add]分離
大型網站爲了緩解大量的併發訪問,除了在網站實現分佈式負載均衡,遠遠不夠。若是仍是傳統的數據結構,或者只是單單靠一臺服務器扛,如此多的數據庫鏈接操做,數據庫必然會崩潰,數據丟失的話,後果更是不堪設想。這時候,咱們會考慮如何減小數據庫的聯接,一方面採用優秀的代碼框架,進行代碼的優化,採用優秀的數據緩存技術如:memcached,若是資金豐厚的話,必然會想到架設服務器羣,來分擔主數據庫的壓力
所以,通常來講都是經過主從複製(Master-Slave)的方式來同步數據,再經過讀寫分離(MySQL-Proxy,是MySQL官方提供的MySQL中間件服務)來提高數據庫的併發負載能力 這樣的方案來進行部署與實施的
實現方式
第一種:php程序上本身作邏輯判斷,寫php代碼的時候,本身在程序上作邏輯判讀寫匹配。select,insert、update、delete作正則匹配,根據結果選擇寫服務器(主服務器)。若是是select操做則選擇讀服務器(從服務器器) mysql_connect('讀寫的區分')
第二種:MySQL中間件,基本的原理是讓主數據庫處理寫操做(insert、update、delete),而從數據庫處理查詢操做(select)。而數據庫的一致性則經過主從複製來實現。因此說主從複製是讀寫分離的基礎。
下面是一些經常使用的MySQL中間件的背景介紹
五、存儲過程
(1)爲何須要存儲過程
a 數據不安全,網絡傳送SQL代碼,容易被未受權者截獲
b 每次提交SQL代碼都要通過語法編譯後在執行,影響應用程序的運行性能
c 網絡流量大,對於反覆執行的SQL代碼,在網絡上屢次傳送,影響網絡傳輸量
(2)什麼是存儲過程
存儲過程是SQL語句和控制語句的預編譯集合,保存在數據庫中,可有應用程序調用執行,並且容許用戶聲明變量、邏輯控制語句及其餘強大的編程功能。包含邏輯控制語句和數據操做語句,能夠接收參數、輸出參數、返回單個或多個結果值及返回值
(3)使用存儲過程的優勢
a 模塊化程序設計,只需建立一次,之後便可調用該存儲過程任意次
b 執行速度快,效率高
c 減小網絡流量
d 具備良好的安全性
六、對mysql配置優化
下面是一些配置的優化,具體參數的解釋就不寫了,請自行查找資料
七、mysql服務器硬件升級
(1)磁盤
MySQL每秒鐘都在進行大量、複雜的查詢操做,對磁盤的讀寫量可想而知。因此,一般認爲磁盤I/O是制約MySQL性能的最大因素之一
解決方案: 使用RAID-10 、磁盤陣列設備SAN
(2)CPU 對於MySQL應用,推薦使用S.M.P.架構的多路對稱CPU
(3)內存 越大越好
(4)網卡 至少兩個網卡,均爲1GBE。一般我會將這兩個nics綁定在一塊兒以提供冗餘
八、定時的去清除不須要的數據,定時進行碎片整理
什麼是磁盤碎片?
簡單的說,刪除數據必然會在數據文件中形成不連續的空白空間,而當插入數據時,這些空白空間則會被利用起來.因而形成了數據的存儲位置不連續,以及物理存儲順序與理論上的排序順序不一樣,這種是數據碎片.實際上數據碎片分爲兩種,一種是單行數據碎片,另外一種是多行數據碎片.前者的意思就是一行數據,被分紅N個片斷,存儲在N個位置.後者的就是多行數據並未按照邏輯上的順序排列.
當有大量的刪除和插入操做時,必然會產生不少未使用的空白空間,這些空間就是多出來的額外空間.索引也是文件數據,因此也會產生索引碎片,理由同上,大概就是順序紊亂的問題.Engine 不一樣,OPTIMIZE 的操做也不同的,MyISAM 由於索引和數據是分開的,因此 OPTIMIZE 能夠整理數據文件,並重排索引。這樣不但會浪費空間,而且查詢速度也更慢。
解決方案:
(1)查看錶碎片的方法
select ROW_FORMAT,TABLE_ROWS,DATA_LENGTH,INDEX_LENGTH,MAX_DATA_LENGTH,DATA_FREE,ENGINE from TABLES where TABLE_SCHEMA='test_db' and TABLE_NAME='table_name' limit 1;
(2)Innodb存儲引擎清理碎片方法
ALTER TABLE tablename ENGINE=InnoDB
(3)Myisam存儲引擎清理碎片方法
OPTIMIZE TABLE table_name
切記,必定要在夜裏執行,表越大,越耗資源時間,不要頻繁修復,能夠幾個月甚至一年修復一次,若是表頻繁被更改,能夠作個計劃任務,按周/月來整理。