1、優化方式
硬件優化=》系統優化=》mysql配置優化=》SCHEMA優化=》sql優化=》其他解決方案(redis or MongoDB or Cassandra or HBase)
2、mysql配置分析
1)常見瓶頸
90%系統瓶頸都在IO上,所以提高IOPS尤為總要,iowait過高,加內存,減小數據讀取量
如果CPU很高,或者查詢時間很長,90%索引不當
如果系統發生swap,必定是內存分配不當
所以優化,總是會圍繞着提高對內存的使用率+減少IO,比如內存緩存+索引,還有其他方式嗎,NO
2)確認方式
slow log + global status + engine status + processlist + pt工具
3)環境
mysql Ver 14.14 Distrib 5.6.25, for Linux (x86_64) using EditLine wrapper && CentOS release 6.7 (Final)
3、mysql配置優化
1)慢查詢日志
在mysql服務器中,數據表都是保存在磁盤上的(innodb、myisam組織表的形式不同,所以文件結構也就不同)。索引為服務器提供了一種在表中查找特定數據行的方法,而不用搜索整個表。當必須要搜索整個表時,就稱為表掃描。通常來說,您可能只希望獲得表中數據的一個子集,因此全表掃描會浪費大量的磁盤 I/O,因此也就會浪費大量時間。當必須對數據進行連接時,這個問題就更加復雜了,因為必須要對連接兩端的多行數據進行比較。
當然,表掃描並不總是會帶來問題;有時讀取整個表反而會比從中挑選出一部分數據更加有效(服務器進程中查詢優化器用來作出這些決定)。如果索引的使用效率很低,或者根本就不能使用索引,則會減慢查詢速度,而且隨着服務器上的負載和表大小的增加,這個問題會變得更加顯著。執行時間超過給定時間范圍的查詢就稱為慢速查詢。
在my.cnf中開啟慢日志
long_query_time = 2 slow-query-log = on slow-query-log-file = /data/mysql/slow-query.log log-queries-not-using-indexes
查看是否開啟
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語句
mysqldumpslow -s c -t 20 slow-query.log
2)連接數
經常會遇見”MySQL: ERROR 1040: Too manyconnections”的情況,一種是訪問量確實很高,MySQL服務器抗不住,這個時候就要考慮增加從服務器分散讀壓力,另外一種情況是MySQL配置文件中max_connections值過小
mysql> show variables like 'max_connections'; +-----------------+-------+ | Variable_name | Value | +-----------------+-------+ | max_connections | 256 | +-----------------+-------+
這台MySQL服務器最大連接數是256,然后查詢一下服務器響應的最大連接數
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%
還有兩個比較重要參數
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為主要存儲引擎服務器的配置
mysql> show variables like 'key_buffer_size'; +-----------------+------------+ | Variable_name | Value | +-----------------+------------+ | key_buffer_size | 536870912 | +-----------------+------------+
分配了512MB內存給key_buffer_size,我們再看一下key_buffer_size的使用情況
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_*參數
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 字句)之前,都必須先保存到臨時表中;理想情況下,在內存中創建臨時表。但是如果臨時表變得太大,就需要寫入磁盤中
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服務器對臨時表的配置
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情況
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值
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
在接收連接時會根據需要生成線程。在一個連接變化很快的繁忙服務器上,對線程進行緩存便於以后使用可以加快最初的連接
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配置
mysql> show variables like 'thread_cache_size'; +-------------------+-------+ | Variable_name | Value | +-------------------+-------+ | thread_cache_size | 64 | +-------------------+-------+
7)查詢緩存(query cache)
很多 LAMP 應用程序都嚴重依賴於數據庫,但卻會反復執行相同的查詢。每次執行查詢時,數據庫都必須要執行相同的工作 —— 對查詢進行分析,確定如何執行查詢,從磁盤中加載信息,然后將結果返回給客戶機。MySQL 有一個特性稱為查詢緩存,它將(后面會用到的)查詢結果保存在內存中。在很多情況下,這會極大地提高性能。不過,問題是查詢緩存在默認情況下是禁用的
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的配置
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)排序使用情況
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)
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%
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)表鎖情況
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)表掃描情況
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 版本開始,則將默認值調整為256 了,最大設置值為524288,當前版本默認值為528。注意,這里設置的是可以緩存的表定義信息的數目,而不是內存空間的大小
mysql> show global variables like '%definition%'; +------------------------+-------+ | Variable_name | Value | +------------------------+-------+ | table_definition_cache | 528 | +------------------------+-------+ 1 row in set (0.00 sec) mysql> show status like '%definition%'; +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | Open_table_definitions | 70 | | Opened_table_definitions | 0 | +--------------------------+-------+ 2 rows in set (0.02 sec)
4、內存的組成
innodb存儲引擎可以分為三部分:內存、進程、數據文件
innodb的內存的作用大致如下
- 緩存磁盤上的數據,方便快速的讀取;
- 對磁盤文件的數據進行修改之前在這里緩存;
- 應用所作的日志的緩存;
- 內存結構自身的管理結構
1)Innodb buffer pool
緩沖池是最大塊的內存部分,主要用來各種數據的緩沖。innodb將數據文件按頁(16K)讀取到緩沖池,然后按最少使用(LRU)算法來保留緩存數據;數據文件修改時,先修改緩存池中的頁(即臟頁),然后按一定平率將臟頁刷新到文件
mysql> show variables like 'innodb_%_size'; +---------------------------------+------------+ | Variable_name | Value | +---------------------------------+------------+ | innodb_additional_mem_pool_size | 2097152 | | innodb_buffer_pool_size | 2013265920 | | innodb_log_buffer_size | 8388608 | | innodb_log_file_size | 1048576000 | +---------------------------------+------------+ 4 rows in set (0.00 sec)
按照數據頁的類型
1、有索引頁
2、數據頁
4、undo頁
5、插入緩沖
6、自適應哈希索引
7、InnoDB存儲的鎖信息、數據字典信息等
通過show engine innodb status可以查看緩沖池的具體信息
BUFFER POOL AND MEMORY ---------------------- Total memory allocated 2058485760; in additional pool allocated 0 Dictionary memory allocated 819282 Buffer pool size 122879 Free buffers 97899 Database pages 24014 Old database pages 8844 Modified db pages 8 Pending reads 0 Pending writes: LRU 0, flush list 0, single page 0 Pages made young 6, not young 0 0.00 youngs/s, 0.00 non-youngs/s Pages read 1049, created 41540, written 30276412 0.00 reads/s, 0.00 creates/s, 1.55 writes/s Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000 Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s LRU len: 24014, unzip_LRU len: 0
這邊的單位是buffer frame,每個buffer frame為16K,通過計算可以查看buffer pool的使用情況
1、Buffer pool size 122879×16×1024 = 2013249536
2、Free buffers表示當前空閑的緩沖頁
3、Database pages表示已經使用的緩沖頁
4、Modified db pages 表示臟頁的數量
5、Old database pages表示LRU列表中old sublist中的數據塊數量
對上面的innodb buffer pool細看會發現,buffer pool的數據類型又可以分為:page cache、hash index、undo、insert buffer、explicit locks
2)Log Buffer
日志緩沖池(功能跟oracle redo log buffer基本相似),將重做日志信息放入這個緩沖區,然后按一定頻率將其刷新到重做日志文件。該值一般不需要設置很大,因為一般情況下每一秒鍾就會將重做日志緩沖刷新到日志文件,只需要保證每秒產生的事物量在這個緩沖大小之內即可
3)additional buffer pool
在innodb存儲引擎中,對內存的管理是通過一種稱為內存堆的方式進行的。在對一些數據結構本身分配內存時,需要從額外獲得內存池中申請,當該區 域的內存不夠時,Innodb會從緩沖池中申請。但是每個緩沖池中的frame buffer還有對應的緩沖控制對象,這些對象記錄了諸如LRU、鎖、等待等方面的信息,而這個對象的內存需要從額外內存中申請。因此,當你申請了很大的 Innodb緩沖池時,這個值也應該相應增加;
簡單理解為:額外緩沖池用於管理緩沖池的內容的,所以緩沖池越大額外換池也需要越大
4)內存計算
used_Mem = + key_buffer_size + query_cache_size + innodb_buffer_pool_size + innodb_additional_mem_pool_size + innodb_log_buffer_size + max_connections *( + read_buffer_size + read_rnd_buffer_size + sort_buffer_size + join_buffer_size + binlog_cache_size + thread_stack + tmp_table_size + bulk_insert_buffer_size )
sql
SELECT ( @@key_buffer_size + @@query_cache_size + @@tmp_table_size + @@innodb_buffer_pool_size + @@innodb_additional_mem_pool_size + @@innodb_log_buffer_size + @@max_connections * ( @@read_buffer_size + @@read_rnd_buffer_size + @@sort_buffer_size + @@join_buffer_size + @@binlog_cache_size + @@thread_stack + @@bulk_insert_buffer_size ) ) / @giga_bytes AS MAX_MEMORY_GB;
參考文章
http://www.ibm.com/developerworks/cn/linux/l-tune-lamp-3.html#resources
http://database.51cto.com/art/201010/229956.htm
http://news.oneapm.com/php-xhprof-xhgui/
http://mp.weixin.qq.com/s?__biz=MjM5NDE0MjI4MA==&mid=208777870&idx=1&sn=6efddd6283e4deb3fe55a141b0db965c&scene=1&srcid=0910kYIbazQSBqZEivwahGHB&key=dffc561732c2265104613d6540d35b8ad5c92c340ed903cbbd8218ac9ba70f5b9d36aaa09033e2f9cf0e7983792311d4&ascene=1&uin=MjM2NjkwNQ%3D%3D&devicetype=Windows+7&version=61020020&pass_ticket=JuXI39h6lA0f0sHQ0AFMc7g2Z4NJXU5U71301BBDAuw%3D
http://pingzhao1990.blog.163.com/blog/static/113566342201531583628765/