MySQL性能优化之常用优化

前言

考虑到服务器硬件配置的不同,具体应用的差别,需要根据自己服务器的”状态”的情况进行配置优化

基本命令:

mysql> show global status; -- 可以列出MySQL服务器运行各种状态值,另外,查询MySQL服务器配置信息语句:
mysql> show variables;      -- 显示系统变量的名称和值

1. 慢查询优化

命令:
mysql> show variables like '%slow%';
  +------------------+-------+ 
  | Variable_name | Value    | 
  +------------------+-------+ 
  | log_slow_queries | ON    | 
  | slow_launch_time | 2     | 
  +------------------+-------+ 
mysql> show global status like '%slow%';  
  +---------------------+-------+ 
  | Variable_name       | Value | 
  +---------------------+-------+ 
  | Slow_launch_threads | 0     | 
  | Slow_queries        | 4148  | 
  +---------------------+-------+
现象:

执行时间(slow_launch_time)超过N秒的即为慢查询,系统显示(Slow_queries)有M个慢查询

优化策略:

配置中打开了记录慢查询,执行时间超过2秒的即为慢查询,系统显示有4148个慢查询,你可以分析慢查询日志,找出有问题的SQL语句,慢查询时间不宜设置过长,否则意义不大,最好在5秒以内,如果你需要微秒级别的慢查询

2. 连接数

经常会遇见”MySQL: ERROR 1040: Too many connections”的情况,一种是访问量确实很高,MySQL服务器抗不住,这个时候就要考虑增加从服务器分散读压力,另外一种情况是MySQL配置文件中max_connections值过

命令:
mysql> show variables like 'max_connections';   -- 系统配置的最大连接数
  +-----------------+-------+ 
  | Variable_name   | Value | 
  +-----------------+-------+ 
  | max_connections | 256   | 
  +-----------------+-------+  
mysql> show global status like ‘Max_used_connections’;  -- 服务器达到的连接数上限
优化策略:
Max_used_connections / max_connections * 100% ≈ 85%

最大连接数占上限连接数的85%左右,如果发现比例在10%以下,MySQL服务器连接数上限设置的过高了。

3. Key_buffer_size

key_buffer_size是对MyISAM表性能影响最大的一个参数

命令:
mysql> show variables like 'key_buffer_size';   -- 系统配置的索引缓冲区大小
+-----------------+------------+ 
| Variable_name   | Value      | 
+-----------------+------------+ 
| key_buffer_size | 536870912  | 
+-----------------+------------+  

mysql> show global status like 'key_read%';     -- 索引缓冲区实际使用情况
+------------------------+-------------+ 
| Variable_name          | Value       | 
+------------------------+-------------+ 
| Key_read_requests    | 27813678764 | 
| Key_reads              | 6798830     | 
+------------------------+-------------+ 
数据 :

一共有27813678764个索引读取请求,有6798830个请求在内存中没有找到直接从硬盘读取索引,
计算索引未命中缓存的概率:

 key_cache_miss_rate = Key_reads / Key_read_requests * 100% 
优化策略:

key_cache_miss_rate在0.1%以下都很好(每1000个请求有一个直接读硬盘),如果key_cache_miss_rate在0.01%以下的话,key_buffer_size分配的过多,可以适当减少。

附:

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. 临时表问题

命令:
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% 
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以下的临时表才能全部放内存,超过的就会用到硬盘临时表。

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或者table_open_cache值:

mysql> show variables like 'table_cache';   -- 系统配置用于限制缓存表的最大数目
+---------------+-------+ 
| Variable_name | Value | 
+---------------+-------+ 
| table_cache   | 2048  | 
+---------------+-------+

table_cache用于限制缓存表的最大数目:如果当前已经缓存的表未达到table_cache,则会将新表添加进来;若已经达到此值,MySQL将根据缓存表的最后查询时间、查询率等规则释放之前的缓存。

优化策略:
Open_tables / Opened_tables * 100% >= 85%
Open_tables / table_cache * 100% <= 95%

6. 进程使用情况

Mysql支持单线程和多线程两种连接线程模式,如果单线程,则在同一时刻,只能有一个connection连接到Mysql,其他的连接会被挂起。如果是多线程,则同一时刻可以支持多个connection同时连接到服务器。mysql使用哪种线程模式通过下面变量控制。

mysql> show variables like '%thread_handling%';  -- 系统配置的线程模式
+-----------------+---------------------------+
| Variable_name   | Value                     |
+-----------------+---------------------------+
| thread_handling | one-thread-per-connection |
+-----------------+---------------------------+

客户端发起连接到mysql server,mysql server监听进程,监听到新的请求,然后mysql为其分配一个新的 thread,去处理此请求。从建立连接之开始,CPU要给它划分一定的thread stack,然后进行用户身份认证,建立上下文信息,最后请求完成,关闭连接,同时释放资源,在高并发的情况下,将给系统带来巨大的压力,不能保证性能。所以,mysql通过线程缓存来是实现线程重用,减小这部分的消耗;一个connection断开,并不销毁承载其的线程,而是将此线程放入线程缓冲区,并处于挂起状态,当下一个新的Connection到来时,首先去线程缓冲区去查找是否有空闲的线程,如果有,则使用之,如果没有则新建线程。
mysql通过 thread_cache_size这参数来设置可以重用线程的个数,他的大小可以通过状态变量Threads_cached和Threads_created来设置

命令:
mysql> show global status like 'Thread%'; 
+-------------------+-------+ 
| Variable_name     | Value | 
+-------------------+-------+ 
| Threads_cached    | 46    | 
| Threads_connected | 2     | 
| Threads_created   | 570   | 
| Threads_running   | 1     | 
+-------------------+-------+  
  • Threads_cached — 已经被线程缓存池缓存的线程个数
  • Threads_created — 表示创建过的线程数
  • Threads_connected — 当前打开的连接的数量(处于连接状态),等于show processlist
  • Threads_running — 处于激活状态的线程的个数,这个一般都是远小于Threads_connected的。

如果我们在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)

命令:
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:缓存中块的数量。
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        |
+------------------------------+------------+ 

MySQL查询缓存变量解释:

  • 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%,命中率很差,可能写操作比较频繁吧,而且可能有些碎片。