1. 查詢性能優化
1.1 優化數據訪問
- 檢查是否檢索大量超過須要的數據.是否訪問太多行或太多列,增長網絡開銷,消耗cpu和內存資源
- 檢查服務器層是否在分析大量超過須要的數據行
1.2 重構查詢的方式
1.2.1 切分查詢
- 有時對於一個大查詢咱們須要分而治之,切分紅小查詢每次只完成一部分
1.2.2 分解關聯查詢
- 緩存效率更高: 方便緩存單表查詢結果
- 執行單個查詢能夠減小鎖的競爭
- 在應用層作關聯,更容易對數據庫進行拆分,更容易作到高性能和可擴展
- 使用in 代替關聯查詢可能比隨機的關聯要高效
- 能夠減小冗餘記錄的查詢
1.3 查詢執行的基礎
1.3.1 查詢流程
- 先檢查緩存
- sql解析,預處理,優化器生成相應的執行計劃
- 調用存儲引擎的api執行查詢
1.3.2 通訊協議
- 半雙工,任何一時刻要麼是服務器向客戶端發送數據,要麼是客戶端向服務端發送數據
- 客戶端從服務器獲取數據時,實際是MySQL向客戶端推送數據的過程
1.3.3 查詢狀態
- Sleep: 線程正在等待客戶端發送新的請求
- Query: 線程正在執行查詢或者正在將結果發送給客戶端
- Locked: 服務器層,線程正在等待表鎖
- Analyzing and statistics: 線程正在收集存儲引擎統計信息,並生成查詢的執行計劃
- Copying to tmp table: 線程正在執行查詢,而且將結果集複製到一個臨時表中.常見group by或文件排序操做
- Sorting result: 線程正在對結果進行排序
- Sending data: 線程可能在多個狀態之間傳送數據或在生成結果集或向客戶端返回數據
1.3.4 查詢優化
1.3.4.1 語法解析器和預處理
- 經過關鍵字將sql語句進行解析,生成對應的解析樹
- 解析器使用語法規則驗證和解析查詢
- 預處理器進一步檢查解析樹是否合法,驗證權限
1.3.4.2 查詢優化器
- 一條查詢能夠有多種執行方式,優化器找到其中最好的執行計劃,MySQL使用基於成本的優化器
- 優化類型
- 從新定義關聯表的順序
- 外聯結轉化成內鏈接
- 使用等價變換規則
- 優化count, min, max函數
- 預估並轉化爲常數表達式
- 覆蓋索引掃描
- 子查詢優化
- 提早終止查詢,如limit
- 等值傳播
- In優化
1.3.4.3 關聯查詢
- 嵌套循環: 先在一個表中循環取出單條數據,而後再嵌套循環到下一個表中尋找匹配的行,若是最後一個聯表沒法找到更多的行,則返回上一層次關聯表
- UNION查詢和子查詢時都會將臨時結果存放到一個臨時表中
1.3.4.4 執行計劃
- MySQL生成一棵指令樹,經過存儲引擎執行完成並返回結果
1.3.4.5 排序優化
- 排序是一個成本很高的操做
- MySQL排序: 若是數據量小,則在內存中進行; 數據量大則先分塊再排序再合併
- MySQL4.1後使用單次傳輸排序: 先讀取查詢所須要的全部列,再根據給定列排序
1.3.4.6 查詢執行引擎
- 根據執行計劃的指令逐步執行
1.3.4.7 返回結果給客戶端
- 若是查詢能夠緩存,則緩存在這個階段進行
- 返回結果的過程是一個增量逐步返回的過程,一旦開始生成第一條結果時就能夠開始向客戶端返回結果集
1.4 查詢優化器的侷限
- 子查詢相對糟糕(不是絕對),如子查詢用in
- 聯表查詢與子查詢根據場景不一樣有不一樣優點
- MySQL沒法將限制條件下推到子查詢
- 索引合併優化
- MySQL沒法利用多核特性並行執行查詢
- MySQL並不支持哈希關聯, MariaDB已經實現了真正的哈希關聯
- 鬆散索引掃描,沒法按照不連續的方式掃描一個索引
- 最大值最小值函數的優化通常
- 不容許同一張表上同時查詢和更新, 如update set 等於 select 本身.解決方法,能夠經過關聯臨時表
1.5 查詢優化器的提示
- 設置查詢優化器參數,能夠閱讀官方手冊
- 通常除非須要,修改查詢優化器參數會提升維護成本
1.6 優化特定類型的查詢
- 關聯查詢: on的列加索引; 使用group by和order by 只使用一個表的列能夠利用索引
- 優化LIMIT分頁: 儘可能使用覆蓋索引
- 子查詢: 儘可能使用關聯查詢替換
- 靜態查詢分析: Percona Toolkit中的pt-query-advisor能解析查詢日誌,分析查詢模式
- 使用用戶自定義變量: 沒法使用查詢緩存,可能被優化器優化掉
2. MySQL高級特性
2.1 分區表
2.1.1 應用
- 表很是大沒法所有放在內存中,或者只在表的最後部分有熱點數據其餘均是歷史數據
- 分區表的數據更容易維護
- 分區表的數據能夠分佈在不一樣的物理設備上
- 使用分區表避免某些瓶頸,如InnoDB單個索引的互斥訪問
- 備份和恢復獨立分區,對於大數據集效果較好
2.1.2限制
- 一個表最多1024個分區
- 分區表達式必須是整數或返回整數的表達式
- 若是分區字段有主鍵或惟一索引列,那麼全部主鍵列和惟一索引都必須包含進來
- 分區表中沒法使用外鍵約束
2.1.3 原理
- 分區表由多個相關的底層表實現,存儲引擎管理它們跟管理普通表同樣
- select 查詢: 分區層打開並鎖住全部底層表,優化器判斷是否過濾分區,在調用存儲引擎api訪問各個分區數據
- insert: 分區層打開並鎖住全部底層表,肯定分區,寫入
- delete: 分區層打開並鎖住全部底層表,肯定數據所在分區,刪除
- update: 分區層打開並鎖住全部底層表,肯定分區,取出數據,更新,肯定分區,寫入
- 打開並鎖住全部底層表: 若是存儲引擎實現行級鎖如InnoDB,則會在分區層釋放表鎖
2.1.4 分區表類型
- 根據範圍進行分區: 每一個分區儲存落在某個範圍的記錄
- 根據鍵值進行分區,減小InnoDB互斥量競爭
- 使用數學模函數進行分區,而後將數據輪詢放入不一樣的分區,適用於只想保留幾天的數據
2.1.5 使用
- 問題回顧:數據量很大時,除非是索引覆蓋查詢,不然數據庫須要根據索引掃描回表查詢,產生大量的隨機IO,數據庫響應時間很大
- 全量掃描數據不要索引,根據分區定位數據位置
- 索引數據,分離熱點. 將熱點數據單獨放在一個分區
- NULL值會使分區過濾無效: 分區表達式接收NULL值並將其放到第一個分區致使查詢時多查分區.解決方法:建立第一個無用分區存放NULL值數據
- 分區列和索引列不匹配,查詢沒法進行分區過濾
- 選擇分區成本高,插入大量數據時都須要掃描分區定義找到分區
- 打開並鎖住全部底層表的成本可能很高
- 維護分區的成本很高,同alter同樣建立臨時表而後拷貝數據
- 全部分區都必須使用相同的存儲引擎
2.1.6 查詢優化
- 在where條件帶入分區列
- 建立分區時可使用表達式,可是查詢時只能在使用分區列自己進行比較時才能過濾分區,而不能根據表達式的值過濾分區
2.2 視圖
視圖自己是一個虛擬表,不存聽任何數據,不能對視圖建立觸發器算法
2.2.1 算法
- 合併算法: 將存放的視圖sql和用戶發起的查詢sql合併後執行
- 臨時表算法: 由存放的視圖sql先建立臨時表後根據用戶的查詢sql查詢返回
2.2.2 可更新視圖
- 能夠經過更新視圖更新相關表, 全部臨時表算法實現的視圖都沒法更新
2.2.3 視圖對性能的影響
- 通常狀況視圖不能提高性能,在某些狀況下能夠幫助提高性能,須要作比較詳細的測試
- 視圖還能夠實現基於列的權限控制不用真正建立列權限
2.2.4 視圖的限制
- 不保存視圖定義的原始sql語句
- 查看視圖建立的語句,能夠經過使用視圖的.frm文件的最後一行獲取一些信息
2.3 外鍵約束
- InnoDB強制外鍵使用索引
- 查詢須要額外訪問一些表,須要額外的鎖容易致使一些死鎖
- 若是使用外鍵作約束,一般在應用程序裏實現會更好
2.4 內部存儲代碼
2.4.1 優勢
- 離數據最近,節省帶寬和網絡延遲
- 幫助提高安全性,應用程序能夠經過存儲過程訪問那些沒有權限的表
- 服務器端能夠緩存存儲過程的執行計劃
- 維護方便,便於分工
2.4.2 缺點
- 調試困難,難以定位問題
- 存儲代碼效率相對差
- 增長維護複雜性,存儲過程會給數據庫服務器增長額外壓力
- 存在安全隱患,沒有什麼選項能夠控制存儲程序的資源消耗,因此一個小錯誤可能直接把服務器拖死
2.4.3 存儲過程和函數
- 優化器沒法評估存儲函數的執行成本
- 每一個鏈接都有獨立的存儲過程的執行計劃緩存,多個鏈接調用同一個存儲過程會浪費緩存空間反覆緩存一樣的執行計劃
2.4.4 觸發器
- 每一個表的每一個事件只能一個
- MySQL只支持基於行的觸發,若是變動的數據集很是龐大的化效率會很低
- 觸發器的問題很難排查
- 可能致使死鎖和鎖等待
- 實現一些約束,系統維護任務及更新反範式化數據的時候會比較有用
2.4.5 事件
- 相似Linux的定時任務
2.5 遊標
- MySQL在服務器端提供只讀的,單向的遊標
- 一個存儲過程當中能夠有多個遊標,也能夠嵌套
2.6 綁定變量
- 建立一個綁定變量sql時客戶端向服務器發送了一個sql語句原型
- 服務器端解析並存儲這個sql語句的部分執行計劃返回客戶端一個sql語句處理句柄
- 可使用問號做爲sql的佔位,在使用sql接口執行時賦予變量值
2.7 插件
- 存儲過程插件
- 後臺插件: 如Percona Server中包含的Handler-Socket
- INFORMATION_SCHEMA插件
- 全文解析插件: 能夠對文檔進行分詞處理
- 審計插件: 能夠用做記錄事件日誌
- 認證插件: 擴展認證功能
2.8 字符集和校對
- 字符集是指一種從二進制編碼到某類字符符號的映射
- 校對是指一組用於某個字符集的排序規則
2.8.1 建立對象時的默認設置
- 服務器,數據庫,表都有默認的字符集和校對規則,這是一個逐層繼承的默認設置
- 建立數據庫時根據character_set_server設置來設定默認字符集
2.8.2 服務器和客戶端通訊設置
- 服務端老是假設客戶端按照character_set_client設置的字符來傳輸數據和sql語句
- 服務器端收到sql語句後根據character_set_connection轉換成字符串
- 服務器端返回數據時會將其轉換爲character_set_result
2.8.3 選擇字符集和校對規則
- 極簡原則: 先爲服務器選擇合理的字符集在根據實際狀況讓某些列選擇合適的字符集
2.8.4 對查詢的影響
- 不一樣字符集和校對規則之間的轉換會帶來額外的開銷
- 排序查詢要求的字符集與服務器數據的字符集相同時才能利用索引進行排序
- 當兩個字符集不一樣列關聯兩個表時,MySQL會嘗試轉換其中一個列的字符集
2.9 全文索引
- 天然語言的全文索引: 相關度是基於匹配的關鍵詞個數及關鍵詞在文檔中出現的次數,整個索引中出現次數越少的詞語匹配的相關度越高
- 布爾全文索引: 只有MyISAM才能使用
- 平時沒接觸過,有興趣者請自行google
2.10 分佈式XA事務
- 事務協調器保證全部事務參與者完成工做,通知全部事務提交
- 內部XA事務: 存儲引擎提交的同時,須要將提交的信息寫入二進制日誌
- 外部XA事務: XA事務是一種在多個服務器之間同步數據的方法,若是因爲不能使用MySQL自己的複製或者性能並非瓶頸能夠嘗試使用
2.11 查詢緩存
- 查詢緩存系統會跟蹤查詢中涉及的每一個表,若是表發生變化則緩存數據失效
- 緩存存放在一個引用表中,經過一個哈希值引用,哈希值包括查詢自己,查詢數據庫等信息
- 當sql語句和客戶端發送過來的其餘原始信息,任何字符上的不一樣都會致使緩存不命中
- 打開查詢緩存對讀和寫都會帶來額外的消耗
- InnoDB事務修改表時,會將這個表對應的查詢緩存都設置失效
- 查詢緩存被發現是一個影響服務器擴展性的因素
- 若是緩存了大量的查詢結果,那麼失效操做可能會形成系統僵死.由於靠一個全局鎖保護,全部該操做都要等鎖
- 減小碎片, 選擇合適的query_cache_min_res_unit能夠減小內存浪費
- 對於寫密集型的應用,直接禁用更好
- 高併發環境也不適合.只有明確緩存的好處才使用
- 查詢緩存的替代方案: 客戶端緩存