1. 程式人生 > 實用技巧 >MySQL高負載優化

MySQL高負載優化

佔用CPU過高,可以做如下考慮:

1)一般來講,排除高併發的因素,還是要找到導致你CPU過高的哪幾條在執行的SQL,show processlist語句,查詢負荷最重的SQL語句,優化該SQL,比如適當建立某欄位的索引;

2)開啟慢查詢日誌,將那些執行時間過長且佔用資源過多的SQL拿來進行explain分析,導致CPU過高,多數是GroupBy、OrderBy排序問題所導致,然後慢慢進行優化改進。比如優化insert語句、優化group by語句、優化order by語句、優化join語句等等;

3)考慮定時優化檔案及索引;

4)定期分析表,使用optimize table;

5)優化資料庫物件;

6)考慮是否是鎖問題;

7)調整一些MySQL Server引數,比如key_buffer_size、table_cache、innodb_buffer_pool_size、innodb_log_file_size等等;

8)如果資料量過大,可以考慮使用MySQL叢集或者搭建高可用環境。

9)可能由於記憶體latch(洩露)導致資料庫CPU高

10)在多使用者高併發的情況下,任何系統都會hold不住的,所以,使用快取是必須的,使用memcached或者redis快取都可以;

11)看看tmp_table_size大小是否偏小,如果允許,適當的增大一點;

12)如果max_heap_table_size配置的過小,增大一點;

13)mysql的sql語句睡眠連線超時時間設定問題(wait_timeout)

14)使用show processlist檢視mysql連線數,看看是否超過了mysql設定的連線數

配置優化

1使用 InnoDB 儲存引擎

如果你還在使用 MyISAM 儲存引擎,那麼是時候轉換到 InnoDB 了。有很多的理由都表明 InnoDB 比 MyISAM 更有優勢,如果你關注效能,那麼,我們來看一下它們是如何利用實體記憶體的:

  1. MyISAM:僅在記憶體中儲存索引。
  2. InnoDB:在記憶體中儲存索引和資料。

結論:儲存在記憶體的內容訪問速度要比磁碟上的更快。

下面是如何在你的表上去轉換儲存引擎的命令:

ALTER TABLE table_name ENGINE=InnoDB;

注意:你已經建立了所有合適的索引,對嗎?為了更好的效能,建立索引永遠是第一優先考慮的事情。

2 InnoDB 使用所有的記憶體

你可以在 my.cnf 檔案中編輯你的 MySQL 配置。使用 innodb_buffer_pool_size 引數去配置在你的伺服器上允許 InnoDB 使用實體記憶體數量。

對此(假設你的伺服器僅僅執行 MySQL),公認的“經驗法則”是設定為你的伺服器實體記憶體的 80%。在保證作業系統不使用交換分割槽而正常執行所需要的足夠記憶體之後 ,儘可能多地為 MySQL 分配實體記憶體。

因此,如果你的伺服器實體記憶體是 32 GB,可以將那個引數設定為多達 25 GB。

innodb_buffer_pool_size = 25600M 

*注意:(1)如果你的伺服器記憶體較小並且小於 1 GB。為了適用本文的方法,你應該去升級你的伺服器。 (2) 如果你的伺服器記憶體特別大,比如,它有 200 GB,那麼,根據一般常識,你也沒有必要為作業系統保留多達 40 GB 的記憶體。 *

3 InnoDB 多工執行

如果伺服器上的引數innodb_buffer_pool_size的配置是大於 1 GB,將根據引數 innodb_buffer_pool_instances 的設定, 將 InnoDB 的緩衝池劃分為多個。

擁有多於一個的緩衝池的好處有:

在多執行緒同時訪問緩衝池時可能會遇到瓶頸。你可以通過啟用多緩衝池來最小化這種爭用情況:

對於緩衝池數量的官方建議是:

為了實現最佳的效果,要綜合考慮 innodb_buffer_pool_instancesinnodb_buffer_pool_size 的設定,以確保每個例項至少有不小於 1 GB 的緩衝池。

因此,在我們的示例中,將引數 innodb_buffer_pool_size 設定為 25 GB 的擁有 32 GB 實體記憶體的伺服器上。一個合適的設定為 25600M / 24 = 1.06 GB

innodb_buffer_pool_instances = 24

連線超時

mysql> show variables like 'wait_timeout'; 睡眠連線超時秒數
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout  | 120   |
+---------------+-------+
1 row in set (0.23 sec)

連線數

mysql> show variables like '%max_connections%'; mysql的最大連線數
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 6000  |
+-----------------+-------+
1 row in set (0.25 sec)
mysql> show global status like 'Max_used_connections'; 伺服器響應的最大連線數3
+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| Max_used_connections | 45    |
+----------------------+-------+
1 row in set (0.24 sec)

mysql伺服器最大連線數值的設定範圍比較理想的是:伺服器響應的最大連線數值佔伺服器上限連線數值的比例值在10%以上,如果在10%以下,說明mysql伺服器最大連線上限值設定過高.

Max_used_connections / max_connections * 100% = 45/6000 *100% =0.0075

增加max_connections引數的值,不會佔用太多系統資源。系統資源(CPU、記憶體)的佔用主要取決於查詢的密度、效率等

臨時表

mysql>  show global status like 'created_tmp%';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 21755 |
| Created_tmp_files       | 94    |
| Created_tmp_tables      | 56250 |
+-------------------------+-------+
3 rows in set (0.09 sec)

每次建立臨時表時,Created_tmp_table都會增加,如果磁碟上建立臨時表,Created_tmp_disk_tables也會增加。Created_tmp_files表示MySQL服務建立的臨時檔案數,比較理想的配置是:

Created_tmp_disk_tables / Created_tmp_files *100% <= 25%

伺服器Created_tmp_disk_tables / Created_tmp_files *100% =23%

開啟表的情況

Open_tables表示開啟表的數量,Opened_tables表示開啟過的表數量,我們可以用如下命令檢視其具體情況:

mysql> show global status like 'open%tables%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables   | 1994  |
| Opened_tables | 5132  |
+---------------+-------+
2 rows in set (0.23 sec)
如果Opened_tables數量過大,說明配置中table_open_cache的值可能太小
mysql> show variables like 'table_open_cache';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| table_open_cache | 2000  |
+------------------+-------+
1 row in set (0.12 sec)

比較合適的值為:

open_tables / opened_tables* 100% > = 85%

1994 / 5132 *100% =38%

open_tables / table_open_cache* 100% < = 95%

1994 / 2000 *100% =99.7%

程序使用情況

MySQL伺服器的配置檔案中設定了thread_cache_size,當客戶端斷開時,伺服器處理此客戶請求的執行緒將會快取起來以響應一下客戶而不是銷燬(前提是快取數未達上線)Thread_created表示建立過的執行緒數,我們可以用如下命令檢視:

mysql>  show global status like 'thread%';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Threads_cached    | 28    |
| Threads_connected | 17    |
| Threads_created   | 45    |
| Threads_running   | 3     |
+-------------------+-------+
4 rows in set (0.28 sec)
Threads_created的值過大的話,表明MySQL伺服器一直在建立執行緒,這也是比較耗費資源的,可以適當增大配置檔案中thread_cache_size的值。查詢伺服器thread_cache_size配置如下:
mysql> show variables like 'thread_cache_size';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| thread_cache_size | 68    |
+-------------------+-------+
1 row in set (0.31 sec)

查詢快取

query_cache_size是設定MySQL的Query Cache大小,query_cache_type是設定使用查詢快取的型別,我們可以用如下命令檢視其具體情況:

mysql> show global status like 'qcache%';
+-------------------------+---------+
| Variable_name           | Value   |
+-------------------------+---------+
| Qcache_free_blocks      | 1       |
| Qcache_free_memory      | 1031832 |
| Qcache_hits             | 0       |
| Qcache_inserts          | 0       |
| Qcache_lowmem_prunes    | 0       |
| Qcache_not_cached       | 2181771 |
| Qcache_queries_in_cache | 0       |
| Qcache_total_blocks     | 1       |
+-------------------------+---------+
8 rows in set (0.29 sec)

MySQL查詢快取變數的相關解釋如下:

Qcache_free_blocks:快取中相領記憶體快的個數。數目大說明可能有碎片。Flush query cache會對快取中的碎片進行整理,從而得到一個空間塊。

Qcache_free_memory:快取中的空閒空間。

Qcache_hits:多少次命中。通過這個引數可以檢視到Query Cache的基本效果。

Qcache_inserts:插入次數,沒插入一次查詢時就增加1。命中次數除以插入次數就是命中比率。

Qcache_lowmem_prunes:多少條Query因為記憶體不足而被清楚出Query Cache。通過Qcache_lowmem_prunes和Query_free_memory相互結合,能夠更清楚地瞭解到系統中Query Cache的記憶體大小是否真的足夠,是否非常頻繁地出現因為記憶體不足而有Query被換出的情況。

Qcache_not_cached:不適合進行快取的查詢數量,通常是由於這些查詢不是select語句或用了now()之類的函式。

Qcache_queries_in_cache:當前快取的查詢和響應數量。

Qcache_total_blocks:快取中塊的數量。

我們在查詢一下伺服器上關於query_cache的配置:

mysql> show variables like 'query_cache%';
+------------------------------+---------+
| Variable_name                | Value   |
+------------------------------+---------+
| query_cache_limit            | 1048576 |
| query_cache_min_res_unit     | 4096    |
| query_cache_size             | 1048576 |
| query_cache_type             | OFF     |
| query_cache_wlock_invalidate | OFF     |
+------------------------------+---------+
5 rows in set (0.12 sec)

query_cache_limit:超過此大小的查詢將不快取。

query_cache_min_res_unit:快取塊的最小值。

query_cache_size:查詢快取大小。

query_cache_type:快取型別,決定快取什麼樣的查詢,示例中表示不快取select sql_no_cache查詢。

query_cache_wlock_invalidat:表示當有其他客戶端正在對MyISAM表進行寫操作,讀請求是要等WRITE LOCK釋放資源後再查詢還是允許直接從Query Cache中讀取結果,預設為OFF(可以直接從Query Cache中取得結果。)

query_cache_min_res_unit的配置是一柄雙刃劍,預設是4KB,設定值大對大資料查詢有好處,但如果你的查詢都是小資料查詢,就容易造成記憶體碎片和浪費。

查詢快取碎片率= Qcache_free_blocks /Qcache_total_blocks *100%

如果查詢碎片率超過20%,可以用flushquery cache整理快取碎片,或者試試減少query_cache_min_res_unit,如果你查詢都是小資料庫的話。

查詢快取利用率= (Qcache_free_size – Qcache_free_memory)/query_cache_size * 100%

查詢快取利用率在25%一下的話說明query_cache_size設定得過大,可適當減少;查詢快取利用率在80%以上而且Qcache_lowmem_prunes > 50的話則說明query_cache_size可能有點小,不然就是碎片太多。

查詢命中率=(Qcache_hits - Qcache_insert)/Qcache)hits * 100%

示例伺服器中的查詢快取碎片率等於20%左右,查詢快取利用率在50%,查詢命中率在2%,說明命中率很差,可能寫操作比較頻繁,而且可能有些碎片。

排序使用情況

它表示系統中對資料進行排序時所用的Buffer,我們可以用如下命令檢視:

mysql> show global status like 'sort%';
+-------------------+---------+
| Variable_name     | Value   |
+-------------------+---------+
| Sort_merge_passes | 37      |
| Sort_range        | 110404  |
| Sort_rows         | 1122189 |
| Sort_scan         | 795334  |
+-------------------+---------+
4 rows in set (0.26 sec)

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並不一定能提高速度。

開啟檔案數(open_files)

我們現在處理MySQL故障時,發現當Open_files大於open_files_limit值時,MySQL資料庫就會發生卡住的現象,導致Nginx伺服器打不開相應頁面。這個問題大家在工作中應注意,我們可以用如下命令檢視其具體情況:

mysql> show global status like 'open_files';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_files    | 18    |
+---------------+-------+
1 row in set (0.30 sec)

比較合適的設定是:Open_files / Open_files_limit * 100% < = 75%