1. 程式人生 > >MySQLDBA修煉之道讀書筆記

MySQLDBA修煉之道讀書筆記

以下的命令將檢視慢查詢是否啟用了,以及慢查詢的日誌路徑。

mysql> show variables like’%query_log%’;

-------------show variables like’%query_log%’

-------------±--------------------±----------------------------------------+

| Variable_name | Value | ±--------------------±----------------------------------------+

| slow_query_log | ON |

| slow_query_log_file |/path/to/log3304/slowquery.log | ±--------------------±----------------------------------------+ 2rows in set (0.00sec)

由於連線的成本比較高,因此對於高併發的應用,應該儘量減少有連線的查詢,連線的表的個數不能太多,連線的表建議 控制在4個以內。網際網路應用比較常見的一種情況是,在資料量比較小的時候,連線的開銷不大,這個時候一般不會有效能問

題,但當資料量變大之後,連線的低效率問題就暴露出來了,成為整個系統的瓶頸所在。所以對於資料庫應用的設計,最好在 早期就確定未來可能會影響效能的一些查詢,進行反正規化設計減少連線的表,或者考慮在應用層進行連線。 優化連線的一些要點如下。

1)ON、USING子句中的列確認有索引。如果優化器選擇了連線的順序為B、A,那麼我們只需要在A表的列上建立索引即 可。例如,對於查詢“SELECT B.*,A.*FROM B JOIN A ON B.col1=A.col2;”語句MySQL會全表掃描B表,對B表的每一行記錄探測 A表的記錄(利用A表col2列上的索引)。 2)最好是能轉化為INNER JOIN,LEFT JOIN的成本比INNER JOIN高很多。 3)使用EXPLAIN檢查連線,留意EXPLAIN輸出的rows列,如果rows列太高,比如幾千,上萬,那麼就需要考慮是否索引 不佳或連線表的順序不當。 4)反正規化設計,這樣可以減少連線表的個數,加快存取資料的速度。

5)考慮在應用層實現連線。

對於一些複雜的連線查詢,更值得推薦的做法是將它分解為幾個簡單的查詢,可以先執行查詢以獲得一個較小的結果集, 然後再遍歷此結果集,最後根據一定的條件去獲取完整的資料,這樣做往往是更高效的,因為我們把資料分離了,更不容易發

生變化,更方便快取資料,資料也可以按照設計的需要從快取或資料庫中進行獲取。

GROUP BY、DISTINCT、ORDER BY這幾類子句比較類似,GROUP BY預設也是要進行ORDER BY排序的,筆者在本書中 把它們歸為一類,優化的思路也是類似的。可以考慮的優化方式如下。 ·儘量對較少的行進行排序。 ·如果連線了多張表,ORDER BY的列應該屬於連線順序的第一張表。 ·利用索引排序,如果不能利用索引排序,那麼EXPLAIN查詢語句將會看到有filesort。 ·GROUP BY、ORDER BY語句參考的列應該儘量在一個表中,如果不在同一個表中,那麼可以考慮冗餘一些列,或者合併 表。 ·需要保證索引列和ORDER BY的列相同,且各列均按相同的方向進行排序。 ·增加sort_buffer_size。 sort_buffer_size是為每個排序執行緒分配的緩衝區的大小。增加該值可以加快ORDER BY或GROUP BY操作。但是,這是為每 個客戶端分配的緩衝區,因此不要將全域性變數設定為較大的值,因為每個需要排序的連線都會分配sort_buffer_size大小的記憶體。 ·增加read_rnd_buffer_size。 當按照排序後的順序讀取行時,通過該緩衝區讀取行,從而避免搜尋硬碟。將該變數設定為較大的值可以大大改進ORDER BY的效能。但是,這是為每個客戶端分配的緩衝區,因此你不應將全域性變數設定為較大的值。相反,只用為需要執行大查詢 的客戶端更改會話變數即可。 ·改變tmpdir變數指向基於記憶體的檔案系統或其他更快的磁碟。 如果MySQL伺服器正作為複製從伺服器被使用,那麼不應將“–tmpdir”設定為指向基於記憶體的檔案系統的目錄,或者當服務 器主機重啟時將要被清空的目錄。因為,對於複製從伺服器,需要在機器重啟時仍然保留一些臨時檔案,以便能夠複製臨時表 或執行LOAD DATA INFILE操作。如果在伺服器重啟時丟失了臨時檔案目錄下的檔案,那麼複製將會失敗。 ·指定ORDER BY NULL。 預設情況下,MySQL將排序所有GROUP BY的查詢,如果想要避免排序結果所產生的消耗,可以指定ORDER BY NULL。 例如: SELECT count(*) cnt, cluster_id FROM stat GROUP BY cluster_id ORDER BY NULL LIMIT 10; ·優化GROUP BY WITH ROLLUP。 GROUP BY WITH ROLLUP可以方便地獲得整體分組的聚合資訊(super aggregation),但如果存在效能問題,可以考慮在應 用層實現這個功能,這樣往往會更高效,伸縮性也更佳。 ·使用非GROUP BY的列來代替GROUP BY的列。 比如,原來是“GROUP BY xx_name,yy_name”,如果GROUP BY xx_id可以得到一樣的結果,那麼使用GROUP BY xx_id也是可 行的。

Web應用經常需要對查詢的結果進行分頁,分頁演算法經常需要用到“LIMIT offset,row_count ORDER BY col_id”之類的語句。 一旦offset的值很大,效率就會很差,因為MySQL必須檢索大量的記錄(offset+row_count),然後丟棄大部分記錄。

可供考慮的優化辦法有如下4點。 1)限制頁數,只顯示前幾頁,超過了一定的頁數後,直接顯示“更多(more)”,一般來說,對於N頁之後的結果,使用者一 般不會關心。 2)要避免設定offset值,也就是避免丟棄記錄。

例如以下的例子,按照id排序(id列上有索引),通過增加一個定位的列“id>990”,可以避免設定offset的值

查詢第1條到第10條的資料的sql是:select * from table limit 0,10; ->對應我們的需求就是查詢第一頁的資料:select * from table limit (1-1)*10,10;

查詢第10條到第20條的資料的sql是:select * from table limit 10,20; ->對應我們的需求就是查詢第二頁的資料:select * from table limit (2-1)*10,10;

查詢第20條到第30條的資料的sql是:select * from table limit 20,30; ->對應我們的需求就是查詢第三頁的資料:select * from table limit (3-1)*10,10;

MySQL的引數及執行狀態 以下程式碼可檢視MySQL例項的引數及執行狀態。 SHOW VARIABLES LIKE ‘%parameter%’ ;

SHOW FULL PROCESSLIST ;

SHOW INNODB STATUS \G;

下面來解釋一些常用的狀態。 ·Sleep:執行緒正在等待來自客戶端的新查詢。 ·Query:執行緒正在執行查詢,或者正在傳送結果給客戶端。 ·Locked:執行緒正在等待表鎖。 ·Analyzing和statistics:執行緒正在獲取儲存引擎的統計資料和優化查詢。 ·Copying to tmp table[on disk]:執行緒正在處理查詢,複製資料到臨時表中。如果後面有“on disk”字樣,則表明MySQL正在將記憶體臨時錶轉換為磁碟臨時表。 ·Sorting result:執行緒正在排序結果集。 ·Sending data:這個狀態有多種可能,可能是內部各步驟之間傳遞資料,生成結果集;或者是將結果集返回給客戶端。

MySQL需要關注的引數及狀態變數 以下的一些狀態變數,是監控系統需要著重關注的,由於篇幅所限,這裡並沒有列出所有值得關注的狀態變數。 (1)open_files_limit 作業系統允許mysqld開啟的檔案數量。這個值可以設定得比較大,比如50000,最好在系統初始化安裝時就設定了一個較大的值。可修改檔案/etc/security/limits.conf 來實現,命令如下。 vi /etc/security/limits.conf * - nofile 50000 (2)max_connect_errors 此值應設定得比較大,如大於5000,以避免因為連接出錯而超過出錯閾值,導致MySQL阻止該主機連線。如被阻塞,則須手動執行flush-hosts進行復位。 (3)max_connections 允許並行的客戶端連線數目。預設值100太小,一般會不夠用。 生產環境中建議設定為2000~5000。注意,對於32位的MySQL由於有記憶體限制,連線數不能過大(建議小於800),否則可能會由於連線過多,造成MySQL例項 崩潰。 (4)max_used_connections MySQL Server啟動後曾經到達的最大連線數。如果該值達到max_connections,那麼某個時刻存在突然的高峰連線時,可能會有效能問題。 (5)threads_connected 當前開啟的連線數量。這個值不能超過設定的max_connections*80%。需要注意及時調整max_connections的值。一旦連線數超過了max_connections,就會出現客戶端 連線不上的錯誤。 (6)aborted_connects 試圖連線到MySQL伺服器而失敗的連線數。正常情況下,該值不會持續增加,出現連線失敗的原因主要有如下幾點。 ·客戶端程式在退出之前未呼叫mysql_close()。 ·客戶端的空閒時間超過了wait_timeout或interactive_timeout秒,未向伺服器發出任何請求。 ·客戶端在資料傳輸中途突然結束。 (7)Aborted_clients 由於客戶端沒有正確關閉連線導致客戶端終止而中斷的連線數。 出現下述情況時,伺服器將增加“Aborted_clients”(放棄客戶端)的狀態變數。 ·客戶端不具有連線至資料庫的許可權。 ·客戶端採用了不正確的密碼。 ·連線資訊包含不正確的資訊。 ·獲取連線資訊包的時間超過了connect_timeout秒。 我們可以使用如下的命令發現異常。 mysqladmin -uroot -p -S /path/to/tmp//3306/mysql.sock ext | grep Abort 也可以使用tcpdump來判斷是什麼原因導致了異常。 tcpdump -s 1500 -w tcp.out port 3306 strings tcpdump.out (8)thread_cache_size 伺服器應快取多少執行緒以便重新使用?當客戶端斷開連線時,如果執行緒少於thread_cache_size,則客戶端的執行緒將被放入快取。如果有新連線請求分配執行緒則可 以從快取中重新利用執行緒,只有當快取空了時才會建立新執行緒。如果新連線很多,則可以增加該變數以提高效能。如果是大量併發的短連線,則可能會因為 thread_cache_size不夠而導致效能問題。生產環境中一般將其設定為100~200。 由於執行緒可以快取,所以執行緒持有的記憶體不會被輕易釋放。 (9)Threads_created 建立用來處理連線的執行緒數。應該監視Threads_created的增量,如果較多,則需要增加thread_cache_size的值。 以上對thread_cache_size的設定在高併發的時候會很有效。高併發時大量併發短連線對CPU的衝擊不容忽視。 (10)threads_running 指同時執行的執行緒數目。這個值一般不會大於邏輯CPU的個數,如果經常有過多的執行緒同時執行,那麼可能就意味著有效能問題。這個指標很重要,往往表明 了一個系統的繁忙程度,它在系統爆發性能問題之前,會有一個上升的趨勢,此時收集的效能資訊,將有助於我們診斷複雜的效能問題。 (11)slow_launch_threads

如果這個值比較大,則意味著建立執行緒太慢了,可能是系統出現了效能問題,存在資源瓶頸,從而導致作業系統沒有安排足夠的CPU時間給新建立的執行緒。

批量KILL連線 有時生產環境突然出現效能惡化,登入MySQL,執行SHOW PROCESSLIST命令,發現有大量查詢正在執行,這時你打算手動KILL掉應用程式中過來的執行時 間超過200s的所有的資料庫連線。 mysql> SELECT CONCAT(‘KILL ‘,id,’;’) FROM information_schema.processlist WHERE user<>‘root’ AND Command=‘Query’ AND db=‘db_name’ AND time > 200 INTO OUTFILE ‘/tmp/a.txt’; mysql> SOURCE /tmp/a.txt; 你可以新增更多的篩選條件。 如下是一個KILL掉被阻塞的連線的例子,這是一個臨時的解決方案,徹底解決問題需要儘快找到導致阻塞的原因。 for id in mysqladmin processlist|grep -i locked|awk '{print $1}' do mysqladmin kill ${id} done 14.1.13 記錄執行時間長的查詢 如下命令將記錄執行時間超過120s的查詢。

mysql -uroot -p -e “show full processlist” |grep “Query” |grep “select” |egrep -v “root|Sleep|Locked|INSERT|DELETE|UPDATE” | gawk ‘{if(strtonum($6)>120){print $0;}}’ | grep db_name > /tmp/long_running_process.lst

資訊包過大錯誤 通訊資訊包是傳送至MySQL伺服器的單個SQL語句,或者傳送至客戶端的單一行。在MySQL 5.1伺服器和客戶端之間最大能傳送的資訊包為1GB。 當MySQL客戶端或mysqld伺服器收到大於max_allowed_packet位元組的資訊包時,將發出“(ER_NET_PACKET_TOO_LARGE)”,錯誤,並關閉連線,錯誤如下。 Error: 1153 SQLSTATE: 08S01 (ER_NET_PACKET_TOO_LARGE) Message: Got a packet bigger than ‘ max_allowed_packet’ bytes有一些客戶端還會在包過大時,提示“Lost connection to MySQL server during query”的錯誤。 客戶端和伺服器均有自己的max_allowed_packet變數,因此,如你打算處理大的資訊包,則必須增加客戶端和伺服器上的該變數。 如果你正在使用mysql客戶端程式,這時要想將max_allowed_packet變數設定為較大的值32M,可用下述方式進行修改。 mysql> set global max_allowed_packet=3210241024;