MYSQL執行SQL的流程
SQL的執行過程;mysql
客戶端發送一條查詢給服務器;
服務器經過權限檢查以後,先會檢查查詢緩存,若是命中了緩存,則當即返回存儲在緩存中的結果。不然進入下一階段;
服務器端進行SQL解析、預處理,再由優化器根據該SQL所涉及到的數據表的統計信息進行計算,生成對應的執行計劃;
MySQL根據優化器生成的執行計劃,調用存儲引擎的API來執行查詢;
將結果返回給客戶端。
SQL執行的最大瓶頸在於磁盤的IO,即數據的讀取;不一樣SQL的寫法,會形成不一樣的執行計劃的執行,而不一樣的執行計劃在IO的上面臨徹底不同的數量級,從而形成性能的差距; 因此,咱們說,優化SQL,其實就是讓查詢優化器根據程序猿的計劃選擇匹配的執行計劃,來減小查詢中產生的IO;算法
schema(表結構)對性能的影響
冗餘數據的處理; 適當的數據冗餘能夠提升系統的總體查詢性能(在P2P中,在userinfo對象中有realname和idnumber); 關係數據庫的三範式: 第一範式(1NF)是對關係模式的基本要求,不知足第一範式(1NF)的數據庫就不是關係數據庫,是指數據庫表的每一列都是不可分割的基本數據項,同一列中不能有多個值; 第二範式(2NF)要求數據庫表中的每一個實例或行必須能夠被唯一地區分。 第三範式(3NF)要求一個數據庫表中不包含已在其它表中已包含的非主關鍵字信息。 (不容許有冗餘數據)sql
大表拆小表,有大數據的列單獨拆成小表;數據庫
在一個數據庫中,通常不會設計屬性過多的表;
在一個數據庫中,通常不會有超過500/1000萬數據的表(拆表,按照邏輯拆分,按照業務拆分);
有大數據的列單獨拆成小表(富文本編輯器,CKeditor);
根據需求的展現設置更合理的表結構;緩存
把經常使用屬性分離成小表;服務器
在P2P項目中,咱們把logininfo和userinfo和account表拆成了三張表;
減小查詢經常使用屬性須要查詢的列;
便於經常使用屬性的集中緩存;
####索引和索引的優化: 1,索引的原理:把無序的數據變成有序的查詢;網絡
索引:
索引的物理結構:數據結構
數據庫文件存儲的位置:my.ini配置文件中dataDir對應的數據目錄中;
每個數據庫一個文件夾;
MYISAM引擎:每個表(table_name)--> table_name.MYI:存放的是數據表對應的索引信息和索引內容; table_name.FRM:存放的是數據表的結構信息; table_name.MYD:存放的是數據表的內容;
InnoDB引擎:每個表(table_name)--> table_name.frm:存放的是數據表的結構信息; 數據文件和索引文件都是統一存放在ibdata文件中;
索引文件都是額外存在的,對索引的查詢和維護都是須要消耗IO的;
索引的結構:併發
默認狀況下,一旦建立了一個表,這個表設置了主鍵,那麼MYSQL會自動的爲這個主鍵建立一個unique的索引;
索引類型:
Normal:普通的索引;容許一個索引值後面關聯多個行值;
UNIQUE:惟一索引;容許一個索引值後面只能有一個行值;以前對列添加惟一約束其實就是爲這列添加了一個unique索引;當咱們爲一個表添加一個主鍵的時候,其實就是爲這個表主鍵列(設置了非空約束),併爲主鍵列添加了一個惟一索引;
Fulltext:全文檢索,mysql的全文檢索只能用myisam引擎,而且性能較低,不建議使用;
索引的方法(規定索引的存儲結構): (數據結構,算法基礎)
b-tree:是一顆樹(二叉樹,平衡二叉樹,平衡樹(B-TREE)) 使用平衡樹實現索引,是mysql中使用最多的索引類型;在innodb中,存在兩種索引類型,第一種是主鍵索引(primary key),在索引內容中直接保存數據的地址;第二種是其餘索引,在索引內容中保存的是指向主鍵索引的引用;因此在使用innodb的時候,要儘可能的使用主鍵索引,速度很是快; b-tree中保存的數據都是按照必定順序保存的數據,是能夠容許在範圍以內進行查詢; select * from accountflow where account_id <100;
hash:把索引的值作hash運算,並存放到hash表中,使用較少,通常是memory引擎使用;優勢:由於使用hash表存儲,按照常理,hash的性能比B-TREE效率高不少。 hash索引的缺點: 1,hash索引只能適用於精確的值比較,=,in,或者<>;沒法使用範圍查詢; 2,沒法使用索引排序; 3,組合hash索引沒法使用部分索引; 4,若是大量索引hash值相同,性能較低;
索引的利弊:
索引的好處: 1,提升表數據的檢索效率; 2,若是排序的列是索引列(若是查詢的列==排序的列[而且在這列上作了索引]),大大下降排序成本; 3,在分組操做中若是分組條件是索引列,也會提升效率;
索引的問題: 索引須要額外的維護成本;由於索引文件是單獨存在的文件,對數據的增長,修改,刪除,都會產生額外的對索引文件的操做,這些操做須要消耗額外的IO,會下降增/改/刪的執行效率;
怎麼建立索引?
較頻繁的做爲查詢條件的字段應該建立索引;
惟一性太差的字段不適合單首創建索引,即便頻繁做爲查詢條件; 做爲索引的列,若是不能有效的區分數據,那麼這個列就不適合做爲索引列;好比(性別,狀態很少的狀態列) 舉例:SELECT sum(amount) FROM accountflow WHERE accountType = 0; 假如把accountType做爲索引列,由於accountType只有14種,因此,若是根據accountType來建立索引,最多隻能按照1/14的比例過濾掉數據;可是,若是可能出現,只按照該條件查詢,那咱們就要考慮到其餘的提高性能的方式了; 第一種方案:單首創建一個系統摘要表;在這個裏面有一個列叫作系統總充值金額;每次充值成功,增長這個列的值;之後要查詢系統總充值金額,只須要從這個系統摘要表中查詢;(缺陷:若是充值頻率過快,會致使表的鎖定問題;) 第二種方案:流水一旦發生了,是不會隨着時間改變的;針對這種信息,咱們就可使用增量查詢(結算+增量查詢); 1,建立一張日充值表;記錄每一天的充值總金額(beginDate,endDate,totalAmount),天天使用定時器對當前的充值記錄進行結算;日充值報表裏面記錄只能記錄截止昨天的數據; 2,建立一張月充值表;記錄每個月的充值總金額(beginDate,endDate,totalAmount),每個月最後一天使用定時器對當月的充值記錄進行結算(數據源從日充值報表來); 3,要查詢系統總充值,從月報表中彙總(當前月以前的總充值金額),再從日充值報表中查詢當天以前的日報表數據彙總;再從流水中查詢當前截止查詢時間的流水;使用另一張當天流水錶記錄當天的流水;再把三個數據累加;
更新很是頻繁的字段不適合建立索引;緣由,索引有維護成本;
不會出如今WHERE 子句中的字段不應建立索引;
索引不是越多越好;(只爲必要的列建立索引) 1,無論你有多少個索引,一次查詢至多采用一個索引;(索引和索引之間是獨立的) 2,由於索引和索引之間是獨立的,因此說每個索引都應該是單獨維護的;數據的增/改/刪,會致使全部的索引都要單獨維護;
索引的使用限制:
BLOB 和TEXT 類型的列只能建立前綴索引
MySQL 目前不支持函數索引(在MYSQL中,索引只能是一個列的原始值,不能把列經過計算的值做爲索引); 實例:請查詢1981年入職的員工: SELECT * FROM emp WHERE year(hire_date)='1981'; 問題:查詢的列是在過濾以前通過了函數運算;因此,就算hire_date做爲索引,year(hire_date)也不會使用索引; 解決方案: 1,SELECT * FROM emp WHERE hire_date BETWEEN '1981-01-01' AND '1981-12-31'; 2,在建立一列,這列的值是year(hire_date),而後把這列的值做爲索引; 3. 使用不等於(!= 或者<>)的時候MySQL 沒法使用索引
過濾字段使用了函數運算後(如abs(column)),MySQL 沒法使用索引]
Join 語句中Join 條件字段類型不一致的時候MySQL 沒法使用索引
使用LIKE 操做的時候若是條件以通配符開始( '%abc...')MySQL 沒法使用索引 1,字符串是能夠用來做爲索引的; 2,字符串建立的索引按照字母順序排序; 3,若是使用LIKE,實例:SELECT * FROM userinfo WHERE realName LIKE '吳%';這種狀況是可使用索引的; 可是LIKE '_嘉' 或者LIKE '%嘉'都是不能使用索引的;
使用非等值查詢的時候MySQL 沒法使用Hash 索引
單列索引和複合索引:
由於一個查詢一次至多隻能使用一個索引,因此,若是都使用單值索引(一個列一個索引),在數據量較大的狀況下,不能很好的區分數據;
因此,MYSQL引入了多值索引(複合索引); 複合索引就是由多列的值組成的索引;而且(注意),多列的索引是有順序的!!!!
複合索引的原理:就是相似orderby(orderby後面能夠跟多個排序條件order by hire_date,username desc); 就是在排序和分組(建立倒排表的時候),按照多個列進行排序和合並; SELECT * FROM accountflow WHERE actionTime < 'xxxxx' AND account_id = 5 可使用actionTime+account_id的複合索引; SELECT * FROM accountflow WHERE actionTime < 'xxxxx' 可使用actionTime+account_id的複合索引; SELECT * FROM accountflow WHERE account_id = 5 不可使用actionTime+account_id的複合索引; SELECT * FROM accountflow WHERE account_id = 5 AND actionTime < 'xxxxx' 不可使用actionTime+account_id的複合索引;
複合索引,在查詢的時候,遵照向左原則;只要在查詢的時候,是按照複合索引從左到右的順序依次查詢,無論查詢條件是否徹底知足全部的符合索引的列,均可以使用部分的符合索引;
在實際應用中,基本上都使用複合索引;
查看MYSQL的執行計劃和執行明細狀態(explain+profiling)
Explain:可讓咱們查看MYSQL執行一條SQL所選擇的執行計劃;
Profiling:能夠用來準肯定位一條SQL的性能瓶頸;
EXPLAIN:
使用方式: explain SQL;
返回結果:
ID:執行查詢的序列號;
select_type:使用的查詢類型
DEPENDENT SUBQUERY:子查詢中內層的第一個SELECT,依賴於外部查詢的結果集;
DEPENDENT UNION:子查詢中的UNION,且爲UNION 中從第二個SELECT 開始的後面全部SELECT,一樣依賴於外部查詢的結果集;
PRIMARY:子查詢中的最外層查詢,注意並非主鍵查詢;
SIMPLE:除子查詢或者UNION 以外的其餘查詢;
SUBQUERY:子查詢內層查詢的第一個SELECT,結果不依賴於外部查詢結果集;
UNCACHEABLE SUBQUERY:結果集沒法緩存的子查詢;
UNION:UNION 語句中第二個SELECT 開始的後面全部SELECT,第一個SELECT 爲PRIMARY 8,UNION RESULT:UNION 中的合併結果;
table:此次查詢訪問的數據表;
type:對錶所使用的訪問方式:
all:全表掃描
const:讀常量,且最多隻會有一條記錄匹配,因爲是常量,因此實際上只須要讀一次;
eq_ref:最多隻會有一條匹配結果,通常是經過主鍵或者惟一鍵索引來訪問;
fulltext:全文檢索,針對full text索引列;
index:全索引掃描;
index_merge:查詢中同時使用兩個(或更多)索引,而後對索引結果進行merge 以後再讀取表數據;
index_subquery:子查詢中的返回結果字段組合是一個索引(或索引組合),但不是一個主鍵或者惟一索引;
rang:索引範圍掃描;
ref:Join 語句中被驅動表索引引用查詢;
ref_or_null:與ref 的惟一區別就是在使用索引引用查詢以外再增長一個空值的查詢;
system:系統表,表中只有一行數據;
unique_subquery:子查詢中的返回結果字段組合是主鍵或者惟一約束;
possible_keys:可選的索引;若是沒有使用索引,爲null;
key:最終選擇的索引;
key_len:被選擇的索引長度;
ref:過濾的方式,好比const(常量),column(join),func(某個函數);
rows:查詢優化器經過收集到的統計信息估算出的查詢條數;
Extra:查詢中每一步實現的額外細節信息
Distinct:查找distinct 值,因此當mysql 找到了第一條匹配的結果後,將中止該值的查詢而轉爲後面其餘值的查詢;
Full scan on NULL key:子查詢中的一種優化方式,主要在遇到沒法經過索引訪問null值的使用使用;
Impossible WHERE noticed after reading const tables:MySQL Query Optimizer 經過收集到的統計信息判斷出不可能存在結果;
No tables:Query 語句中使用FROM DUAL 或者不包含任何FROM 子句;
Not exists:在某些左鏈接中MySQL Query Optimizer 所經過改變原有Query 的組成而使用的優化方法,能夠部分減小數據訪問次數;
Select tables optimized away:當咱們使用某些聚合函數來訪問存在索引的某個字段的時候,MySQL Query Optimizer 會經過索引而直接一次定位到所需的數據行完成整個查詢。固然,前提是在Query 中不能有GROUP BY 操做。如使用MIN()或者MAX()的時候;
Using filesort:當咱們的Query 中包含ORDER BY 操做,並且沒法利用索引完成排序操做的時候,MySQL Query Optimizer 不得不選擇相應的排序算法來實現。
Using index:所須要的數據只須要在Index 便可所有得到而不須要再到表中取數據;
Using index for group-by:數據訪問和Using index 同樣,所需數據只須要讀取索引便可,而當Query 中使用了GROUP BY 或者DISTINCT 子句的時候,若是分組字段也在索引中,Extra 中的信息就會是Using index for group-by;
Using temporary:當MySQL 在某些操做中必須使用臨時表的時候,在Extra 信息中就會出現Using temporary 。主要常見於GROUP BY 和ORDER BY 等操做中。
Using where:若是咱們不是讀取表的全部數據,或者不是僅僅經過索引就能夠獲取全部須要的數據,則會出現Using where 信息;
Using where with pushed condition:這是一個僅僅在NDBCluster 存儲引擎中才會出現的信息,並且還須要經過打開Condition Pushdown 優化功能纔可能會被使用。控制參數爲engine_condition_pushdown 。
profiling:
Query Profiler是MYSQL5.1以後提供的一個很方便的用於診斷Query執行的工具,可以準確的獲取一條查詢執行過程當中的CPU,IO等狀況; 1. 開啓profiling:set profiling=1; 2. 執行QUERY,在profiling過程當中全部的query均可以記錄下來; 3. 查看記錄的query:show profiles; 4. 選擇要查看的profile:show profile cpu, block io for query 6編輯器
status是執行SQL的詳細過程; Duration:執行的具體時間; CPU_user:用戶CPU時間; CPU_system:系統CPU時間; Block_ops_in:IO輸入次數; Block_ops_out:IO輸出次數; profiling只對本次會話有效;
JOIN:
1,JOIN的原理: 在mysql中使用Nested Loop Join來實現join; A JOIN B:經過A表的結果集做爲循環基礎,一條一條的經過結果集中的數據做爲過濾條件到下一個表中查詢數據,而後合併結果; 2,JOIN的優化原則: 1,儘量減小Join 語句中的Nested Loop 的循環總次數,用小結果集驅動大結果集; 2,優先優化Nested Loop 的內層循環; 3,保證Join 語句中被驅動表上Join 條件字段已經被索引; 4,擴大join buffer的大小;
SQL優化原則:
[原則一:選擇須要優化的SQL]
選擇須要優化的SQL:不是全部的SQL都須要優化,在優化的過程當中,首選更須要優化的SQL; 怎麼選擇? 優先選擇優化高併發低消耗的SQL;
1小時請求1W次,1次10個IO;
1小時請求10次,1次1W個IO; 考慮:
從單位時間產生的IO總數來講,相同的;
針對一個SQL,若是我能把10個IO變成7個IO,一小時減小3W個IO; 針對第二個SQL,若是能把1W個IO變成7K個IO,一小時減小3W個IO;
從優化難度上講,1W->7K難的多;
從總體性能上來講,第一個SQL的優化可以極大的提高系統總體的性能;第二個SQL慢一點,無非也就是10個鏈接查詢慢一點;
定位性能瓶頸;
SQL運行較慢有兩個影響緣由,IO和CPU,明確性能瓶頸所在;
明確優化目標;
[原則二:從Explain和Profile入手]
任何SQL的優化,都從Explain語句開始;Explain語句可以獲得數據庫執行該SQL選擇的執行計劃;
首先明確須要的執行計劃,再使用Explain檢查;
使用profile明確SQL的問題和優化的結果;
[原則三:永遠用小結果集驅動大的結果集]
[原則四:在索引中完成排序]
[原則五:使用最小Columns]
減小網絡傳輸數據量;
特別是須要使用column排序的時候.爲何?MYSQL排序原理,是把全部的column數據所有取出,在排序緩存區排序,再返回結果;若是column數據量大,排序區容量不夠的時候,就會使用先column排序,再取數據,再返回的屢次請求方式;
[原則六:使用最有效的過濾條件]
過多的WHERE條件不必定可以提升訪問性能;
必定要讓where條件使用本身預期的執行計劃;
[原則七:避免複雜的JOIN和子查詢]
複雜的JOIN和子查詢,須要鎖定過多的資源,MYSQL在大量併發狀況下處理鎖定性能降低較快;
不要過多依賴SQL的功能,把複雜的SQL拆分爲簡單的SQL;
MySQL子查詢性能較低,應儘可能避免使用;