效能調優之MySQL篇三:MySQL配置定位以及優化
1、優化方式
一般的優化方法有:硬體優化,配置優化,sql優化,表結構優化。下面僅僅介紹配置優化,具體優化設定可以參考本人另外一篇部落格,傳送門:https://www.cnblogs.com/langhuagungun/p/9507206.html
2、mysql配置分析
1)常見瓶頸
90%系統瓶頸都在IO上,所以提高IOPS尤為總要,iowait過高,加記憶體,減小資料讀取量
如果CPU很高,或者查詢時間很長,90%索引不當
如果系統發生swap,必定是記憶體分配不當
所以優化,總是會圍繞著提高對記憶體的使用率+減少IO,比如記憶體快取+索引
2)確認方式
slow log + global status + engine status + processlist + pt工具
3、mysql配置優化
1)慢查詢日誌
在mysql伺服器中,資料表都是儲存在磁碟上的(innodb、myisam組織表的形式不同,所以檔案結構也就不同)。索引為伺服器提供了一種在表中查詢特定資料行的方法,而不用搜索整個表。當必須要搜尋整個表時,就稱為表掃描。通常來說,您可能只希望獲得表中資料的一個子集,因此全表掃描會浪費大量的磁碟 I/O,因此也就會浪費大量時間。當必須對資料進行連線時,這個問題就更加複雜了,因為必須要對連線兩端的多行資料進行比較。
當然,表掃描並不總是會帶來問題;有時讀取整個表反而會比從中挑選出一部分資料更加有效(伺服器程序中查詢優化器用來作出這些決定)。如果索引的使用效率很低,或者根本就不能使用索引,則會減慢查詢速度,而且隨著伺服器上的負載和表大小的增加,這個問題會變得更加顯著。執行時間超過給定時間範圍的查詢就稱為慢速查詢
在my.cnf中開啟慢日誌
1 2 3 4 |
long_query_time = 2
slow-query-log =
on
slow-query-log-file = /data/mysql/slow-query.log log-queries-not-
using
-indexes
|
檢視是否開啟
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
mysql> show variables like
'%slow_query%'
;
+---------------------+----------------------------+
| Variable_name | Value |
+---------------------+----------------------------+
| slow_query_log | ON |
| slow_query_log_file | /data/mysql/slow-query.log |
+---------------------+----------------------------+
mysql> show global status like
'%slow%'
;
+---------------------+-------+
| Variable_name | Value |
+---------------------+-------+
| Slow_launch_threads | 0 |
| Slow_queries | 4148 |
+---------------------+-------+
|
開啟慢查詢日誌可能會對系統性能有一點點影響,如果你的MySQL是主-從結構,可以考慮開啟其中一臺從伺服器的慢查詢日誌,這樣既可以監控慢查詢,對系統性能影響又小,另mysql有自帶的命令mysqldumpslow可進行查詢,也可以使用pt工具進行分析(pt-query-digest)
例下列命令可以查出訪問次數最多的20個sql語句
1 |
mysqldumpslow -s c -t 20 slow-query.log
|
2)連線數
經常會遇見”MySQL: ERROR 1040: Too manyconnections”的情況,一種是訪問量確實很高,MySQL伺服器抗不住,這個時候就要考慮增加從伺服器分散讀壓力,另外一種情況是MySQL配置檔案中max_connections值過小
1 2 3 4 5 6 |
mysql> show variables like
'max_connections'
;
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 256 |
+-----------------+-------+
|
這臺MySQL伺服器最大連線數是256,然後查詢一下伺服器響應的最大連線數
1 2 3 4 5 6 |
mysql> show global status like
'Max_used_connections'
;
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| Max_used_connections | 245 |
+----------------------+-------+
|
MySQL伺服器過去的最大連線數是245,沒有達到伺服器連線數上限256,不會出現1040錯誤,最大連線數占上限連線數的85%左右,如果發現比例在10%以下,MySQL伺服器連線數上限設定的過高了
比較理想的設定是:Max_used_connections / max_connections * 100% ≈ 85%
還有兩個比較重要引數
1 2 |
wait_timeout=10
max_connect_errors = 100
|
wait_timeout指的是mysqld 終止所有空閒時間超過 10 秒的連線。在 LAMP 應用程式中,連線資料庫的時間通常就是 Web 伺服器處理請求所花費的時間。有時候,如果負載過重,連線會掛起,並且會佔用連線表空間。如果有多個互動使用者或使用了到資料庫的持久連線,那麼將這個值設低一點並不可取
max_connect_errors 是一個安全的方法。如果一個主機在連線到伺服器時有問題,並重試很多次後放棄,那麼這個主機就會被鎖定,直到 FLUSH HOSTS 之後才能執行。預設情況下,10 次失敗就足以導致鎖定了。將這個值修改為 100 會給伺服器足夠的時間來從問題中恢復。如果重試 100 次都無法建立連線,那麼使用再高的值也不會有太多幫助,可能它根本就無法連線。
3)Key_buffer_size
key_buffer_size是對MyISAM表效能影響最大的一個引數,下面一臺以MyISAM為主要儲存引擎伺服器的配置
1 2 3 4 5 6 |
mysql> show variables like
'key_buffer_size'
;
+-----------------+------------+
| Variable_name | Value |
+-----------------+------------+
| key_buffer_size | 536870912 |
+-----------------+------------+
|
分配了512MB記憶體給key_buffer_size,我們再看一下key_buffer_size的使用情況
1 2 3 4 5 6 7 |
mysql> show global status like
'key_read%'
;
+------------------------+-------------+
| Variable_name | Value |
+------------------------+-------------+
| Key_read_requests | 27813678764 |
| Key_reads | 6798830 |
+------------------------+-------------+
|
Key_reads
代表命中磁碟的請求個數, Key_read_requests
是總數
一共有27813678764個索引讀取請求,有6798830個請求在記憶體中沒有找到直接從硬碟讀取索引
計算索引未命中快取的概率:key_cache_miss_rate = Key_reads / Key_read_requests * 100%
比如上面的資料,key_cache_miss_rate為0.0244%,4000個索引讀取請求才有一個直接讀硬碟,已經很BT 了,key_cache_miss_rate在0.1%以下都很好(每1000個請求有一個直接讀硬碟),如果key_cache_miss_rate在 0.01%以下的話,key_buffer_size分配的過多,可以適當減少,如果每 1,000 個請求中命中磁碟的數目超過 1 個,就應該考慮增大關鍵字緩衝區了。例如,key_buffer = 384M
會將緩衝區設定為 384MB。
MySQL伺服器還提供了key_blocks_*引數
1 2 3 4 5 6 7 |
mysql> show global status like
'key_blocks_u%'
;
+------------------------+-------------+
| Variable_name | Value |
+------------------------+-------------+
| Key_blocks_unused | 0 |
| Key_blocks_used | 413543 |
+------------------------+-------------+
|
Key_blocks_unused 表示未使用的快取簇(blocks)數,Key_blocks_used表示曾經用到的最大的blocks數,比如這臺伺服器,所有的快取都用到了,要麼增加key_buffer_size,要麼就是過渡索引了,把快取佔滿了
比較理想的設定:Key_blocks_used / (Key_blocks_unused + Key_blocks_used) * 100% ≈ 80%
4)臨時表
臨時表可以在更高階的查詢中使用,其中資料在進一步進行處理(例如 GROUP BY 字句)之前,都必須先儲存到臨時表中;理想情況下,在記憶體中建立臨時表。但是如果臨時表變得太大,就需要寫入磁碟中
1 2 3 4 5 6 7 8 |
mysql> show global status like
'created_tmp%'
;
+-------------------------+---------+
| Variable_name | Value |
+-------------------------+---------+
| Created_tmp_disk_tables | 21197 |
| Created_tmp_files | 58 |
| Created_tmp_tables | 1771587 |
+-------------------------+---------+
|
每次建立臨時表,Created_tmp_tables增加,如果是在磁碟上建立臨時表,Created_tmp_disk_tables也增加,Created_tmp_files表示MySQL服務建立的臨時檔案檔案數
比較理想的配置是:Created_tmp_disk_tables / Created_tmp_tables * 100% <= 25%
比如上面的伺服器Created_tmp_disk_tables / Created_tmp_tables * 100% = 1.20%,應該相當好了
我們再看一下MySQL伺服器對臨時表的配置
1 2 3 4 5 6 7 |
mysql> show variables
where
Variable_name
in
(
'tmp_table_size'
,
'max_heap_table_size'
);
+---------------------+-----------+
| Variable_name | Value |
+---------------------+-----------+
| max_heap_table_size | 268435456 |
| tmp_table_size | 536870912 |
+---------------------+-----------+
|
只有256MB以下的臨時表才能全部放記憶體,超過的就會用到硬碟臨時表
每次使用臨時表都會增大 Created_tmp_tables
;基於磁碟的表也會增大 Created_tmp_disk_tables
。對於這個比率,並沒有什麼嚴格的規則,因為這依賴於所涉及的查詢。長時間觀察 Created_tmp_disk_tables
會顯示所建立的磁碟表的比率,您可以確定設定的效率。tmp_table_size
和 max_heap_table_size
都可以控制臨時表的最大大小,因此請確保在 my.cnf 中對這兩個值都進行了設定
5)Open Table情況
1 2 3 4 5 6 |
mysql> show global status like
'open%tables%'
;
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables | 919 |
| Opened_tables | 1951 |
|
Open_tables 表示開啟表的數量,Opened_tables表示開啟過的表數量,如果Opened_tables數量過大,說明配置中 table_cache(5.1.3之後這個值叫做table_open_cache)值可能太小,我們查詢一下伺服器table_cache值
1 2 3 4 5 6 |
mysql> show variables like
'table_cache'
;
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| table_cache | 2048 |
+---------------+-------+
|
比較合適的值為:Open_tables / Opened_tables * 100% >= 85% Open_tables / table_cache * 100% <= 95%
6)執行緒使用情況
與表的快取類似,對於執行緒來說也有一個快取。 mysqld
在接收連線時會根據需要生成執行緒。在一個連線變化很快的繁忙伺服器上,對執行緒進行快取便於以後使用可以加快最初的連線
1 2 3 4 5 6 7 8 9 |
mysql> show global status like
'Thread%'
;
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Threads_cached | 46 |
| Threads_connected | 2 |
| Threads_created | 570 |
| Threads_running | 1 |
+-------------------+-------+
|
如果我們在MySQL伺服器配置檔案中設定了thread_cache_size,當客戶端斷開之後,伺服器處理此客戶的執行緒將會快取起來以響應下一個客戶 而不是銷燬(前提是快取數未達上限)。Threads_created表示建立過的執行緒數,如果發現Threads_created值過大的話,表明 MySQL伺服器一直在建立執行緒,這也是比較耗資源,可以適當增加配置檔案中thread_cache_size值,查詢伺服器 thread_cache_size配置
1 2 3 4 5 6 |
mysql> show variables like
'thread_cache_size'
;
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| thread_cache_size | 64 |
+-------------------+-------+
|
7)查詢快取(query cache)
很多 LAMP 應用程式都嚴重依賴於資料庫,但卻會反覆執行相同的查詢。每次執行查詢時,資料庫都必須要執行相同的工作 —— 對查詢進行分析,確定如何執行查詢,從磁碟中載入資訊,然後將結果返回給客戶機。MySQL 有一個特性稱為查詢快取,它將(後面會用到的)查詢結果儲存在記憶體中。在很多情況下,這會極大地提高效能。不過,問題是查詢快取在預設情況下是禁用的
1 2 3 4 5 6 7 8 9 10 11 12 13 |
mysql> show global status like
'qcache%'
;
+-------------------------+-----------+
| Variable_name | Value |
+-------------------------+-----------+
| Qcache_free_blocks | 22756 |
| Qcache_free_memory | 76764704 |
| Qcache_hits | 213028692 |
| Qcache_inserts | 208894227 |
| Qcache_lowmem_prunes | 4010916 |
| Qcache_not_cached | 13385031 |
| Qcache_queries_in_cache | 43560 |
| Qcache_total_blocks | 111212 |
+-------------------------+-----------+
|
MySQL查詢快取變數解釋:
Qcache_free_blocks:快取中相鄰記憶體塊的個數,數目大說明可能有碎片。FLUSH QUERY CACHE會對快取中的碎片進行整理。
Qcache_free_memory:快取中的空閒記憶體。
Qcache_hits:每次查詢在快取中命中時就增大
Qcache_inserts:每次插入一個查詢時就增大。命中次數除以插入次數就是不中比率。
Qcache_lowmem_prunes: 快取出現記憶體不足並且必須要進行清理以便為更多查詢提供空間的次數。這個數字最好長時間來看;如果這個數字在不斷增長,就表示可能碎片非常嚴重,或者記憶體很少。(上面的 free_blocks和free_memory可以告訴您屬於哪種情況)
Qcache_not_cached:不適合進行快取的查詢的數量,通常是由於這些查詢不是 SELECT 語句或者用了now()之類的函式。
Qcache_queries_in_cache:當前快取的查詢(和響應)的數量。
Qcache_total_blocks:快取中塊的數量。
我們再查詢一下伺服器關於query_cache的配置
1 2 3 4 5 6 7 8 9 10 |
mysql> show variables like
'query_cache%'
;
+------------------------------+-----------+
| Variable_name | Value |
+------------------------------+-----------+
| query_cache_limit | 2097152 |
| query_cache_min_res_unit | 4096 |
| query_cache_size | 203423744 |
| query_cache_type | ON |
| query_cache_wlock_invalidate | OFF |
+------------------------------+-----------+
|
各欄位的解釋:
query_cache_limit:超過此大小的查詢將不快取
query_cache_min_res_unit:快取塊的最小大小
query_cache_size:查詢快取大小
query_cache_type:快取型別,決定快取什麼樣的查詢,示例中表示不快取 select sql_no_cache 查詢
query_cache_wlock_invalidate:當有其他客戶端正在對MyISAM表進行寫操作時,如果查詢在query cache中,是否返回cache結果還是等寫操作完成再讀表獲取結果。
query_cache_min_res_unit的配置是一柄”雙刃劍”,預設是4KB,設定值大對大資料查詢有好處,但如果你的查詢都是小資料查詢,就容易造成記憶體碎片和浪費。
查詢快取碎片率 = Qcache_free_blocks / Qcache_total_blocks * 100%
如果查詢快取碎片率超過20%,可以用FLUSH QUERY CACHE整理快取碎片,或者試試減小query_cache_min_res_unit,如果你的查詢都是小資料量的話。
查詢快取利用率 = (query_cache_size - Qcache_free_memory) / query_cache_size * 100%
查詢快取利用率在25%以下的話說明query_cache_size設定的過大,可適當減小;查詢快取利用率在80%以上而且Qcache_lowmem_prunes > 50的話說明
query_cache_size可能有點小,要不就是碎片太多。
查詢快取命中率 = (Qcache_hits - Qcache_inserts) / Qcache_hits * 100%
示例伺服器 查詢快取碎片率 = 20.46%,查詢快取利用率 = 62.26%,查詢快取命中率 = 1.94%,命中率很差,可能寫操作比較頻繁吧,而且可能有些碎片。
作為一條規則,如果 FLUSH QUERY CACHE
佔用了很長時間,那就說明快取太大了
8)排序使用情況
1 2 3 4 5 6 7 8 9 |
mysql> show global status like
'sort%'
;
+-------------------+------------+
| Variable_name | Value |
+-------------------+------------+
| Sort_merge_passes | 29 |
| Sort_range | 37432840 |
| Sort_rows | 9178691532 |
| Sort_scan | 1860569 |
+-------------------+------------+
|
Sort_merge_passes 包括兩步。MySQL 首先會嘗試在記憶體中做排序,使用的記憶體大小由系統變數Sort_buffer_size 決定,如果它的大小不夠把所有的記錄都讀到記憶體中,MySQL 就會把每次在記憶體中排序的結果存到臨時檔案中,等MySQL 找到所有記錄之後,再把臨時檔案中的記錄做一次排序。這再次排序就會增加 Sort_merge_passes。實際上,MySQL會用另一個臨時檔案來存再次排序的結果,所以通常會看到 Sort_merge_passes增加的數值是建臨時檔案數的兩倍。因為用到了臨時檔案,所以速度可能會比較慢,增加 Sort_buffer_size 會減少Sort_merge_passes 和 建立臨時檔案的次數,但盲目的增加Sort_buffer_size 並不一定能提高速度
9)檔案開啟數(open_files)
1 2 3 4 5 6 7 8 9 10 11 12 |
mysql> show global status like
'open_files'
;
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_files | 1410 |
+---------------+-------+
mysql> show variables like
'open_files_limit'
;
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| open_files_limit | 4590 |
+------------------+-------+
|
比較合適的設定:Open_files / open_files_limit * 100% <= 75%
1 2 3 4 5 6 7 |
mysql> show global status like
'table_locks%'
;
+-----------------------+-----------+
| Variable_name | Value |
+-----------------------+-----------+
| Table_locks_immediate | 490206328 |
| Table_locks_waited | 2084912 |
+-----------------------+-----------+
|
Table_locks_immediate 表示立即釋放表鎖數,Table_locks_waited表示需要等待的表鎖數
如果Table_locks_immediate / Table_locks_waited >5000,最好採用InnoDB引擎,因為InnoDB是行鎖而MyISAM是表鎖,對於高併發寫入的應用InnoDB效果會好些。示例中的服務 器Table_locks_immediate / Table_locks_waited = 235,MyISAM就足夠了
10)表鎖情況
1 2 3 4 5 6 7 |
mysql> show global status like
'table_locks%'
;
+-----------------------+-----------+
| Variable_name | Value |
+-----------------------+-----------+
| Table_locks_immediate | 490206328 |
| Table_locks_waited | 2084912 |
+-----------------------+-----------+
|
Table_locks_immediate 表示立即釋放表鎖數,Table_locks_waited表示需要等待的表鎖數
如果Table_locks_immediate / Table_locks_waited >5000,最好採用InnoDB引擎,因為InnoDB是行鎖而MyISAM是表鎖,對於高併發寫入的應用InnoDB效果會好些。示例中的服務 器Table_locks_immediate / Table_locks_waited = 235,MyISAM就足夠了
11)表掃描情況
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
mysql> show global status like
'handler_read%'
;
+-----------------------+-------------+
| Variable_name | Value |
+-----------------------+-------------+
| Handler_read_first | 5803750 |
| Handler_read_key | 6049319850 |
| Handler_read_next | 94440908210 |
| Handler_read_prev | 34822001724 |
| Handler_read_rnd | 405482605 |
| Handler_read_rnd_next | 18912877839 |
+-----------------------+-------------+
mysql> show global status like
'com_select'
;
+---------------+-----------+
| Variable_name | Value |
+---------------+-----------+
| Com_select | 222693559 |
+---------------+-----------+
|
計算表掃描率:
表掃描率 = Handler_read_rnd_next / Com_select
如果表掃描率超過4000,說明進行了太多表掃描,很有可能索引沒有建好,增加read_buffer_size值會有一些好處,但最好不要超過8MB
12)table_definition_cache
表定義資訊快取是從MySQL5.1.3 版本才開始引入的一個新的快取區,用來存放表定義資訊。當我們的MySQL 中使用了較多的表的時候,此快取無疑會提高對錶定義資訊的訪問效率。MySQL 提供了table_definition_cache 引數給我們設定可以快取的表的數量。在MySQL5.1.25 之前的版本中,預設值為128,從MySQL5.1.25 版本開始