【資料庫】:MySQL資料庫優化
1. MySQL架構
MySQL整體架構圖如下:
2. 查詢執行流程
查詢執行的流程是這樣的:
連線
- 客戶端發起一條Query請求,監聽客戶端的‘連線管理模組’接收請求
- 將請求轉發到‘連線進/執行緒模組’
- 呼叫‘使用者模組’來進行授權檢查
- 通過檢查後,‘連線進/執行緒模組’從‘執行緒連線池’中取出空閒的被快取的連線執行緒和客戶端請求對接,如果失敗則建立一個新的連線請求。
處理
- 先查詢快取,檢查Query語句是否完全匹配,
- 查詢快取失敗則轉交給‘命令解析器’
- 再轉交給對應的模組處理
- 如果是SELECT查詢還會經由‘查詢優化器’做大量的優化,生成執行計劃
- 模組收到請求後,通過‘訪問控制模組’檢查所連線的使用者是否有訪問目標表和目標欄位的許可權
- 有則呼叫‘表管理模組’,先是檢視table cache中是否存在,有則直接對應的表和獲取鎖,否則重新開啟表文件
- 根據表的meta資料,獲取表的儲存引擎型別等資訊,通過介面呼叫對應的儲存引擎處理
- 上述過程中產生資料變化的時候,若開啟日誌功能,則會記錄到相應二進位制日誌檔案中
結果
- Query請求完成後,將結果集返回給‘連線進/執行緒模組’
- 返回的也可以是相應的狀態標識,如成功或失敗等
- ‘連線進/執行緒模組’進行後續的清理工作,並繼續等待請求或斷開與客戶端的連線
3. 什麼是優化
優化主要包含如下幾大要點:
- 合理安排資源、調整系統引數使MySQL執行更快、更節省資源。
- 優化是多方面的,包括查詢、表設計、伺服器等。
- 原則:減少系統瓶頸,減少資源佔用,增加系統的反應速度。
4. 查詢優化
在優化MySQL時,通常需要庫進行分析。常見的分析手段有 慢查詢日誌,EXPLAIN分析查詢,通過定位分析效能的瓶頸,才能更好的優化資料庫系統的效能。
4.1 慢查詢日誌
慢查詢日誌開啟
在配置檔案my.cnf或my.ini中在[mysqld]一行下面加入兩個配置引數:
log-slow-queries=/data/mysqldata/slow-query.log long_query_time=5
注:
- log-slow-queries引數為慢查詢日誌存放的位置,一般這個目錄要有mysql的執行帳號的可寫許可權,一般都將這個目錄設定為mysql的資料存放目錄;
- long_query_time=5中的5表示查詢超過五秒才記錄;
- 還可以在my.cnf或者my.ini中新增log-queries-not-using-indexes引數,表示記錄下沒有使用索引的查詢。
慢查詢分析
我們可以通過開啟log檔案檢視得知哪些SQL執行效率低下,從日誌中,可以發現查詢時間超過5 秒的SQL,而小於5秒的沒有出現在此日誌中。
如果慢查詢日誌中記錄內容很多,可以使用mysqldumpslow工具(MySQL客戶端安裝自帶)來對慢查詢日誌進行分類彙總。mysqldumpslow對日誌檔案進行了分類彙總,顯示彙總後摘要結果。
進入log的存放目錄,執行:
[[email protected]_data]#mysqldumpslow slow-query.log
Reading mysql slow query log from slow-query.log
Count: 2 Time=11.00s (22s) Lock=0.00s (0s) Rows=1.0 (2), root[root]@mysql
select count(N) from t_user;
mysqldumpslow命令:
/path/mysqldumpslow -s c -t 10 /database/mysql/slow-query.log
這會輸出記錄次數最多的10條SQL語句,其中:
- -s, 是表示按照何種方式排序,c、t、l、r分別是按照記錄次數、時間、查詢時間、返回的記錄數來排序,ac、at、al、ar,表示相應的倒敘;
- -t, 是top n的意思,即為返回前面多少條的資料;
- -g, 後邊可以寫一個正則匹配模式,大小寫不敏感的;
例如:
/path/mysqldumpslow -s r -t 10 /database/mysql/slow-log
得到返回記錄集最多的10個查詢。
/path/mysqldumpslow -s t -t 10 -g “left join” /database/mysql/slow-log
得到按照時間排序的前10條裡面含有左連線的查詢語句。
使用mysqldumpslow命令可以非常明確的得到各種我們需要的查詢語句,對MySQL查詢語句的監控、分析、優化是MySQL優化非常重要的一步。開啟慢查詢日誌後,由於日誌記錄操作,在一定程度上會佔用CPU資源影響mysql的效能,但是可以階段性開啟來定位效能瓶頸。
4.2 EXPLAIN
在MySQL中可以使用EXPLAIN檢視SQL執行計劃,用法:EXPLAIN SELECT * FROM products。
5.索引使用
5.1 MySQL索引
B-Tree索引
一般來說,MySQL中的B-Tree索引的物理檔案大多都是以二叉樹的結構來儲存的,也就是所有實際需要的資料都存放於樹的葉子節點,而且到任何一個葉子節點的最短路徑的長度都是完全相同的。
R-Tree索引
RTREE在mysql很少使用,支援該型別的儲存引擎只有MyISAM、BDb、InnoDb、NDb、Archive幾種。相對於BTREE,RTREE的優勢在於範圍查詢。
Hash索引
Hash索引在MySQL中使用的並不是很多,目前主要是Memory儲存引擎使用,而且在Memory儲存引擎中將Hash索引作為預設的索引型別。所謂Hash索引,實際上就是通過一定的Hash演算法,將需要索引的鍵值進行Hash運算,然後將得到的Hash值存入一個Hash表中。然後每次需要檢索的時候,都會將檢索條件進行相同演算法的Hash運算,然後再和Hash表中的Hash值進行比較並得出相應的資訊。
Hash索引特點:
- Hash索引僅僅只能滿足“=”,“IN”和“<=>”查詢,不能使用範圍查詢;
- Hash索引無法被利用來避免資料的排序操作;
- Hash索引不能利用部分索引鍵查詢;
- Hash索引在任何時候都不能避免表掃面;
- Hash索引遇到大量Hash值相等的情況後效能並不一定就會比B-Tree索引高;
Full-text索引
Full-text索引也就是我們常說的全文索引,目前在MySQL中僅有MyISAM儲存引擎支援,而且也並不是所有的資料型別都支援全文索引。目前來說,僅有CHAR,VARCHAR和TEXT這三種資料型別的列可以建Full-text索引。
5.2 建立索引
是否需要建立索引,幾點原則:
- 較頻繁的作為查詢條件的欄位應該建立索引;
- 唯一性太差的欄位不適合單獨建立索引,即使頻繁作為查詢條件;
- 更新非常頻繁的欄位不適合建立索引;
- 不會出現在WHERE子句中的欄位不該建立索引;
索引能夠極大的提高資料檢索效率,也能夠改善排序分組操作的效能,但是我們不能忽略的一個問題就是索引是完全獨立於基礎資料之外的一部分資料,更新資料會帶來的IO量和調整索引所致的計算量的資源消耗。
5.3 使用索引
- 使用聯合索引的查詢:MySQL可以為多個欄位建立索引,一個索引可以包括16個欄位。對於聯合索引,只有查詢條件中使用了這些欄位中第一個欄位時,索引才會生效。
- 使用OR關鍵字的查詢:查詢語句的查詢條件中只有OR關鍵字,且OR前後的兩個條件中的列都是索引時,索引才會生效,否則,索引不生效。
6. 儲存優化
儲存資料時,影響儲存速度的主要是索引、唯一性校驗、一次儲存的資料條數等。
儲存資料的優化,不同的儲存引擎優化手段不一樣,在MySQL中常用的儲存引擎有,MyISAM和InnoDB,兩者的區別。
6.1 儲存引擎介紹
MyISAM儲存引擎
MyISAM儲存引擎是一種非事務性的引擎,提供高速儲存和檢索,以及全文搜尋能力,適合資料倉庫等查詢頻繁的應用。
每一個表都被存放為三個以表名命名的物理檔案。有存放表結構定義資訊的.frm檔案,還有存放了表的資料.MYD檔案和存放索引資料的.MYI檔案。
Innodb 儲存引擎
Innodb 儲存引擎是事務安全的, 因此如果需要一個事務安全的儲存引擎,建議使用它。如果你的資料執行大量的INSERT或UPDATE,出於效能方面的考慮應該使用InnoDB表。
InnoDB 給 MySQL 提供了具有事務(commit)、回滾(rollback)和崩潰修復能力(crash recovery capabilities)的事務安全(transaction-safe (ACID compliant))型表。InnoDB 提供了行鎖(locking on row level),提供與 Oracle 型別一致的不加鎖讀取(non-locking read in SELECTs)。這些特性均提高了多使用者併發操作的效能表現。
在InnoDB表中不需要擴大鎖定(lock escalation),因為 InnoDB 的列鎖定(row level locks)適宜非常小的空間。InnoDB 是 MySQL 上提供外來鍵約束(FOREIGN KEY constraints)的表引擎。
InnoDB 的設計目標是處理大容量資料庫系統,它的 CPU 利用率是其它基於磁碟的關係資料庫引擎所不能比的。在技術上,InnoDB 是一套放在 MySQL 後臺的完整資料庫系統,InnoDB 在主記憶體中建立其專用的緩衝池用於高速緩衝資料和索引。
InnoDB 把資料和索引存放在表空間裡,可能包含多個檔案,這與MyISAM不一樣。InnoDB 表的大小隻受限於作業系統的檔案大小,一般為 2 GB。InnoDB所有的表都儲存在同一個資料檔案 ibdata1 中(也可能是多個檔案,或者是獨立的表空間檔案),相對來說比較不好備份。備份的方案可以是拷貝資料檔案、備份 binlog,或者用 mysqldump。
6.2 MyISAM和Innodb的區別
InnoDB和MyISAM是許多人在使用MySQL時最常用的兩個表型別,這兩個表型別各有優劣,視具體應用而定。基本的差別為:MyISAM型別不支援事務處理等高階處理,而InnoDB型別支援。MyISAM型別的表強調的是效能,其執行數度比InnoDB型別更快,但是不提供事務支援,而InnoDB提供事務支援已經外部鍵等高階資料庫功能。
具體實現的差別:
- MyISAM是非事務安全型的,而InnoDB是事務安全型的。
- MyISAM鎖的粒度是表級,而InnoDB支援行級鎖定。
- MyISAM支援全文型別索引,而InnoDB不支援全文索引。
- MyISAM相對簡單,所以在效率上要優於InnoDB,小型應用可以考慮使用MyISAM。
- MyISAM表是儲存成檔案的形式,在跨平臺的資料轉移中使用MyISAM儲存會省去不少的麻煩。
- InnoDB表比MyISAM表更安全,可以在保證資料不會丟失的情況下,切換非事務表到事務表(alter table tablename type=innodb)。
7.資料庫結構優化
7.1 優化表結構
優化表結構的常用思路:
- 儘量將表字段定義為NOT NULL約束,這時由於在MySQL中含有空值的列很難進行查詢優化,NULL值會使索引以及索引的統計資訊變得很複雜。
- 對於只包含特定型別的欄位,可以使用enum、set 等符合資料型別。
- 數值型欄位的比較比字串的比較效率高得多,欄位型別儘量使用最小、最簡單的資料型別。例如P地址可以使用int型別。
- 儘量使用TINYINT、SMALLINT、MEDIUM_INT作為整數型別而非INT,如果非負則加上UNSIGNED VARCHAR的長度只分配真正需要的空間。
- 儘量使用TIMESTAMP而非DATETIME。
- 單表不要有太多欄位,建議在20以內。
- 合理的加入冗餘欄位可以提高查詢速度。
7.2 表拆分
垂直拆分
垂直拆分按照欄位進行拆分,其實就是把組成一行的多個列分開放到不同的表中,這些表具有不同的結構,拆分後的表具有更少的列。例如使用者表中的一些欄位可能經常訪問,可以把這些欄位放進一張表裡。另外一些不經常使用的資訊就可以放進另外一張表裡。
插入的時候使用事務,也可以保證兩表的資料一致。缺點也很明顯,由於拆分出來的兩張表存在一對一的關係,需要使用冗餘欄位,而且需要join操作,我們在使用的時候可以分別取兩次,這樣的來說既可以避免join操作,又可以提高效率。
水平拆分
水平拆分按照行進行拆分,常見的就是分庫分表。以使用者表為例,可以取使用者ID,然後對ID取10的餘數,將使用者均勻的分配進這 0-9這10個表中。查詢的時候也按照這種規則,又快又方便。
有些表業務關聯比較強,那麼可以使用按時間劃分的。例如每天的資料量很大,需要每天新建一張表。這種業務型別就是需要高速插入,但是對於查詢的效率不太關心。表越大,插入資料所需要索引維護的時間也就越長。
7.3 分割槽
使用分割槽是大資料處理後的產物。比如系統使用者的註冊推廣等等,會產生海量的日誌,當然也可以按照時間水平拆分,建立多張表。但在實際操作中,容易發生忘記切換表導致資料錯誤。
分割槽適用於例如日誌記錄,查詢少。一般用於後臺的資料報表分析。對於這些資料彙總需求,需要很多日誌表去做資料聚合,我們能夠容忍1s到2s的延遲,只要資料準確能夠滿足需求就可以。
MySQL主要支援4種模式的分割槽:range分割槽、list預定義列表分割槽,hash 分割槽,key鍵值分割槽。
7.4 讀寫分離
大型網站會有大量的併發訪問,如果還是傳統的資料結構,或者只是單單靠一臺伺服器扛,如此多的資料庫連線操作,資料庫必然會崩潰,資料丟失的話,後果更是不堪設想。這時候,我們需要考慮如何減少資料庫的聯接。
我們發現一般情況對資料庫而言都是“讀多寫少”,也就說對資料庫讀取資料的壓力比較大,這樣分析可以採用資料庫叢集的方案。其中一個是主庫,負責寫入資料,我們稱為寫庫;其它都是從庫,負責讀取資料,我們稱為讀庫。這樣可以緩解一臺伺服器的訪問壓力。
7.5 資料庫叢集
如果訪問量非常大,雖然使用讀寫分離能夠緩解壓力,但是一旦寫操作一臺伺服器都不能承受了,這個時候我們就需要考慮使用多臺伺服器實現寫操作。
例如可以使用MyCat搭建MySql叢集,對ID求3的餘數,這樣可以把資料分別存放到3臺不同的伺服器上,由MyCat負責維護叢集節點的使用。
8. 硬體優化
是伺服器的硬體效能直接決定著MySQL資料庫的效能,硬體的效能瓶頸,直接決定MySQL資料庫的執行速度和效率。
可以從以下幾個方面考慮:
- 配置較大的記憶體。足夠大的記憶體,是提高MySQL資料庫效能的方法之一。記憶體的IO比硬碟快的多,可以增加系統的緩衝區容量,使資料在記憶體停留的時間更長,以減少磁碟的IO。
- 磁碟I/O相關:
- 使用SSD或者PCIe SSD裝置,至少獲得數百倍甚至萬倍的IOPS提升;
- 購置陣列卡同時配備CACHE及BBU模組,可明顯提升IOPS
- 儘可能選用RAID-10,而非RAID-5
- 使用機械盤的話,儘可能選擇高轉速的,例如選用15000RPM,而不是7200RPM的盤
- 配置CPU相關,在伺服器的BIOS設定中,可調整下面的幾個配置:
- 選擇Performance Per Watt Optimized(DAPC)模式,發揮CPU最大效能;
- 關閉C1E和C States等選項,提升CPU效率;
- Memory Frequency(記憶體頻率)選擇Maximum Performance;
9. MySQL快取
為了提高查詢速度,我們可以通過不同的方式去快取我們的結果從而提高響應效率。當我們的資料庫打開了Query Cache(簡稱QC)功能後,資料庫在執行SELECT語句時,會將其結果放到QC中,當下一次處理同樣的SELECT請求時,資料庫就會從QC取得結果,而不需要去資料表中查詢。如果快取命中率非常高的話,有測試表明在極端情況下可以提高效率238%。
但一個快取機制是否有效,效果如何,卻是一個需要好好思考的問題。Query Cache有如下規則,如果資料表被更改,那麼和這個資料表相關的全部Cache全部都會無效,並刪除之。這裡“資料表更改”包括: INSERT, UPDATE, DELETE, TRUNCATE, ALTER TABLE, DROP TABLE, or DROP DATABASE等。
舉個例子,如果資料表item訪問頻繁,那麼意味著它的很多資料會被QC快取起來,但是每一次item資料表的更新,無論更新是不是影響到了cache 的資料,都會將全部和item表相關的cache清除。如果你的資料表更新頻繁的話,那麼Query Cache將會成為系統的負擔。有實驗表明,糟糕時,QC會降低系統13%的處理能力。
快取又分為全域性快取和區域性快取。這個比較複雜,這裡就不做展開了。
參考