1、连接数Connections优化
经常会遇见"mysql: error 1040: too many connections
”的情况,一种是访问量确实很高,mysql服务器抗不住,这个时候就要考虑增加从服务器分散读压力,另外一种情况是mysql配置文件中max_connections值过小。
//查看mysql的最大连接数max_connections
mysql> show variables like '%max_connections%';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 2000 |
+-----------------+-------+
这台mysql服务器最大连接数是2000,然后查询一下服务器响应的最大连接数:
mysql> show global status like '%max_used_connections%';
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| Max_used_connections | 430 |
+----------------------+-------+
mysql服务器过去的最大连接数是430,没有达到服务器连接数上限2000,不会出现1040错误,比较理想的设置是
(max_used_connections / max_connections) * 85%
最大连接数占上限连接数的85%左右,如果发现比例在10%以下,mysql服务器连接数上限设置的过高了。
//查看所有连接状态数
show status like '%connect%';
+----------------------+---------+
| Variable_name | Value |
+----------------------+---------+
| Aborted_connects | 875 | //尝试已经失败的MySQL服务器的连接的次数
| Connections | 2144162 | //试图连接MySQL服务器的次数。 不管失败与成功
| Max_used_connections | 43 | //同时使用的连接的最大数目
| Threads_connected | 1 | //当前打开的连接的数量
+----------------------+---------+
2、线程Thread优化
mysql> show global status like '%thread%';
+------------------------------------------+-------+
| Variable_name | Value |
+------------------------------------------+-------+
| Delayed_insert_threads | 0 | //正在使用的延迟插入处理器线程的数量
| Performance_schema_thread_classes_lost | 0 |
| Performance_schema_thread_instances_lost | 0 |
| Slow_launch_threads | 0 |
| Threads_cached | 42 | //代表当前此时此刻线程缓存中有多少空闲线程
| Threads_connected | 1 | //代表当前已建立连接的数量,因为一个连接就需要一个线程,所以也可以看成当前被使用的线程数
| Threads_created | 430 | //已创建线程的数量
| Threads_running | 2 | //代表当前激活的(非睡眠状态)线程数。并不是代表正在使用的线程数,有时候连接已建立,但是连接处于sleep状态,这里相对应的线程也是sleep状态
+------------------------------------------+-------+
如果我们在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 | 50 |
+-------------------+-------+
示例中的服务器还是挺健康的。
3、缓存cache优化
3.1 文件打开数
mysql> show global status like '%open_files%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_files | 59 |
+---------------+-------+
mysql> show variables like ‘open_files_limit‘;
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| open_files_limit | 3600 |
+-----------------------+-------+
比较合适的设置:open_files / open_files_limit * 100% <= 75%
3.2 数据表优化
3.2.1 打开数open_tables
mysql> show global status like 'open%tables%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables | 29 |
| Opened_tables | 7565 |
+---------------+-------+
//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%
3.2.2 临时表tmp_table
mysql> show global status like '%created_tmp%';
+-------------------------+---------+
| Variable_name | Value |
+-------------------------+---------+
| Created_tmp_disk_tables | 19614 |
| Created_tmp_files | 5136 |
| Created_tmp_tables | 1056352 |
+-------------------------+---------+
每次创建临时表,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.80%,应该相当好了。我们再看一下mysql服务器对临时表的配置:
mysql> show variables where variable_name in ('tmp_table_size', 'max_heap_table_size');
+---------------------+----------+
| Variable_name | Value |
+---------------------+----------+
| max_heap_table_size | 16777216 |
| tmp_table_size | 16777216 |
+---------------------+----------+
只有 256mb 以下的临时表才能全部放内存,超过的就会用到硬盘临时表。
3.2.3 表锁情况
mysql> show global status like '%table_locks%';
+-----------------------+---------+
| Variable_name | Value |
+-----------------------+---------+
| Table_locks_immediate | 5149129 | //表示立即释放表锁数
| Table_locks_waited | 3612 | //表示需要等待的表锁数
+-----------------------+---------+
如果 table_locks_immediate / table_locks_waited > 5000,最好采用innodb引擎,因为innodb是行锁,而myisam是表锁,对于高并发写入的应用innodb效果会好些。
示例中的服务器 table_locks_immediate / table_locks_waited = 1425,myisam就足够了。
3.2.4 表扫描情况
mysql> show global status like '%handler_read%';
+-----------------------+-------------+
| Variable_name | Value |
+-----------------------+-------------+
| Handler_read_first | 1090362 |
| Handler_read_key | 15941445 |
| Handler_read_last | 17816 |
| Handler_read_next | 6404117157 |
| Handler_read_prev | 6664042 |
| Handler_read_rnd | 14545191 |
| Handler_read_rnd_next | 50128880878 |
+-----------------------+-------------+
如上所示,mysql中关于read的计数器,有7个。他们的数值对于系统的状况的了解,对于系统的调优都十分重要。我们应该理解他们的含义。
首先7个计数器,我们应该分为两部分:
(1)对索引读的计数器:前面的5个都是对索引读情况的计数器,
Handler_read_first:是指读索引的第一项(的次数)
Handler_read_key:是指读索引的某一项(的次数)
Handler_read_next:是指读索引的下一项(的次数)
Handler_read_last:是指读索引的最后第一项(的次数)
Handler_read_prev:是指读索引的前一项(的次数)
这5个应该有四种组合:
Handler_read_first 和 Handler_read_next 组合应该是索引覆盖扫描
Handler_read_key 基于索引取值
Handler_read_key 和 Handler_read_next 组合应该是索引范围扫描
Handler_read_last 和 Handler_read_prev 组合应该是索引范围扫描(orde by desc)
(2)对数据文件的计数器:后面的2个都是对数据文件读情况的计数器,
这里很重要的一点要理解,索引项之间都是有顺序的,所以才有first, last, next, prev等等,所以前面的5个都是对索引读情况的计数器,而后面的2个是对数据文件的读情况的计数器。
很显然的一点,后面的2个Handler_read_rnd和Handler_read_rnd_next是越低越好,如果很高,应该进行索引相关的调优。而Handler_read_key的数值肯定是越高越好,越高代表使用索引读很高。其他的计数器,要具体情况具体分析。
调出服务器完成的查询请求次数:
mysql> show global status like 'com_select';
+---------------+---------+
| Variable_name | Value |
+---------------+---------+
| Com_select | 4057496 |
+---------------+---------+
计算表扫描率:
表扫描率 = handler_read_rnd_next / com_select
如果表扫描率超过4000,说明进行了太多表扫描,很有可能索引没有建好,增加read_buffer_size值会有一些好处,但最好不要超过8mb。
3.3 key_buffer_size优化
key_buffer_size是对myisam表性能影响最大的一个参数,下面一台以myisam为主要存储引擎服务器的配置:
mysql> show variables like 'key_buffer_size';
+-----------------+----------+
| Variable_name | Value |
+-----------------+----------+
| key_buffer_size | 33554432 |
+-----------------+----------+
分配了32mb内存给key_buffer_size ,我们再看一下key_buffer_size的使用情况:
mysql> show global status like 'key_read%';
+-------------------+-----------+
| Variable_name | Value |
+-------------------+-----------+
| Key_read_requests | 428528048 | //索引读取请求
| Key_reads | 118544 |
+-------------------+-----------+
一共有428528048个索引读取请求,有118544个请求在内存中没有找到直接从硬盘读取索引,计算索引未命中缓存的概率:
key_cache_miss_rate = key_reads / key_read_requests * 100%
比如上面的数据,key_cache_miss_rate为0.027%,key_cache_miss_rate在0.1%以下都很好(每1000个请求有一个直接读硬盘),如果key_cache_miss_rate在0.01%以下的话,key_buffer_size分配的过多,可以适当减少。
【注意】key_read_buffer默认值为8M 。在专有的数据库服务器上,该值可设置为RAM * 1/4。
mysql服务器还提供了key_blocks_*参数:
mysql> show global status like '%key_blocks_u%';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Key_blocks_unused | 26773 | //表示未使用的缓存簇(blocks)数
| Key_blocks_used | 3490 | //表示曾经用到的最大的blocks数
+-------------------+-------+
比较理想的设置:
key_blocks_used / (key_blocks_unused + key_blocks_used) * 100% ≈ 80%
3.4 排序使用情况sort_buffer优化
sort buffer是系统中对数据进行排序的时候用到的Buffer。sort buffer是针对单个线程的,所以当多个线程同时进行排序的时候,系统中就会出现多个sort buffer。我们一般可以通过增大sort buffer的大小来提高order by或者group by的处理性能,但并不是越大越好。系统默认大小时2MB,最大限制和join buffer一样。
mysql> show global status like 'sort%';
+-------------------+----------+
| Variable_name | Value |
+-------------------+----------+
| Sort_merge_passes | 2666 |
| Sort_range | 0 |
| Sort_rows | 14501516 |
| Sort_scan | 487278 |
+-------------------+----------+
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并不一定能提高速度。
3.5 查询缓存优化
mysql> show global status like 'qcache%';
+-------------------------+-------+
| Variable_name | Value |
+-------------------------+-------+
| Qcache_free_blocks | 0 |
| Qcache_free_memory | 0 |
| Qcache_hits | 0 |
| Qcache_inserts | 0 |
| Qcache_lowmem_prunes | 0 |
| Qcache_not_cached | 0 |
| Qcache_queries_in_cache | 0 |
| Qcache_total_blocks | 0 |
+-------------------------+-------+
mysql 查询缓存变量解释:
qcache_free_blocks //缓存中相邻内存块的个数。数目大说明可能有碎片。flush query cache会对缓存中的碎片进行整理,从而得到一个空闲块。
qcache_free_memory //缓存中的空闲内存。
qcache_hits //每次查询在缓存中命中时就增大
qcache_inserts //每次插入一个查询时就增大。命中次数除以插入次数就是命中比率。
qcache_lowmem_prunes //缓存出现内存不足并且必须要进行清理以便为更多查询提供空间的次数。这个数字最好长时间来看;如果这个数字在不断增长,就表示可能碎片非常严重,或者内存很少。
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 | 0 |
| query_cache_type | OFF |
| query_cache_wlock_invalidate | OFF |
+------------------------------+---------+
各字段的解释:
query_cache_limit //超过此大小的查询将不缓存
query_cache_min_res_unit //缓存块的最小大小,它的配置是一柄”双刃剑”,默认是4kb,设置值大对大数据查询有好处,但如果你的查询都是小数据查询,就容易造成内存碎片和浪费
query_cache_size //查询缓存大小
query_cache_type //缓存类型,决定缓存什么样的查询,示例中表示不缓存 select sql_no_cache 查询
query_cache_wlock_invalidate //当有其他客户端正在对myisam表进行写操作时,如果查询在query cache中,是否返回cache结果还是等写操作完成再读表获取结果
(1)查询缓存碎片率 = qcache_free_blocks / qcache_total_blocks * 100%
如果查询缓存碎片率超过20%,可以用flush query cache整理缓存碎片,或者试试减小query_cache_min_res_unit,如果你的查询都是小数据量的话。
(2)查询缓存利用率 = (query_cache_size - qcache_free_memory) / query_cache_size * 100%
查询缓存利用率在25%以下的话说明query_cache_size设置的过大,可适当减小;查询缓存利用率在80%以上而且qcache_lowmem_prunes > 50的话说明query_cache_size可能有点小,要不就是碎片太多。
(3)查询缓存命中率 = (qcache_hits - qcache_inserts) / qcache_hits * 100%