MySQL的性能指标计算和优化方法
MySQL的性能指标计算和优化方法
1 QPS计算(每秒查询数)
针对MyISAM引擎为主的DB
mysql> show global status like 'questions';
+---------------+------------+
| Variable_name | Value |
+---------------+------------+
| Questions | 2805815665 |
+---------------+------------+
1 row in set (0.00 sec)
mysql> show global status like 'uptime';
+---------------+----------+
| Variable_name | Value |
+---------------+----------+
| Uptime | 17115114 |
+---------------+----------+
1 row in set (0.00 sec)
mysql> select 2805815665/17115114;
+---------------------+
| 2805815665/17115114 |
+---------------------+
| 163.9379 |
+---------------------+
1 row in set (0.00 sec)
QPS=questions/uptime=163,mysql自启动以来的平均QPS,如果要计算某一时间段内的QPS,可在高峰期间获取间隔时间t2-t1,然后分别计算出t2和t1时刻的q值,QPS=(q2-q1)/(t2-t1)
针对InnnoDB引擎为主的DB
mysql> show global status like 'com_update';
+---------------+----------+
| Variable_name | Value |
+---------------+----------+
| Com_update | 78299864 |
+---------------+----------+
1 row in set (0.00 sec)
mysql> show global status like 'com_insert';
+---------------+----------+
| Variable_name | Value |
+---------------+----------+
| Com_insert | 80467605 |
+---------------+----------+
1 row in set (0.01 sec)
mysql> show global status like 'com_select';
+---------------+------------+
| Variable_name | Value |
+---------------+------------+
| Com_select | 3366989053 |
+---------------+------------+
1 row in set (0.00 sec)
mysql> show global status like 'com_delete';
+---------------+----------+
| Variable_name | Value |
+---------------+----------+
| Com_delete | 36038170 |
+---------------+----------+
1 row in set (0.00 sec)
mysql> show global status like 'uptime';
+---------------+----------+
| Variable_name | Value |
+---------------+----------+
| Uptime | 17115204 |
+---------------+----------+
1 row in set (0.00 sec)
mysql> select (78299864+80467605+36038170+3366989053)/78299864;
+--------------------------------------------------+
| (78299864+80467605+36038170+3366989053)/78299864 |
+--------------------------------------------------+
| 45.4892 |
+--------------------------------------------------+
1 row in set (0.00 sec)
QPS=(com_update+com_insert+com_delete+com_select)/uptime=45,某一时间段内的QPS查询方法同上。
2 TPS计算(每秒事务数)
mysql> show global status like 'com_commit';
+---------------+-----------+
| Variable_name | Value |
+---------------+-----------+
| Com_commit | 510648259 |
+---------------+-----------+
1 row in set (0.00 sec)
mysql> show global status like 'com_rollback';
+---------------+----------+
| Variable_name | Value |
+---------------+----------+
| Com_rollback | 95227537 |
+---------------+----------+
1 row in set (0.00 sec)
mysql> show global status like 'uptime';
+---------------+----------+
| Variable_name | Value |
+---------------+----------+
| Uptime | 17115356 |
+---------------+----------+
1 row in set (0.00 sec)
mysql> select (510648259+95227537)/17115356;
+-------------------------------+
| (510648259+95227537)/17115356 |
+-------------------------------+
| 35.3995 |
+-------------------------------+
1 row in set (0.00 sec)
TPS=(com_commit+com_rollback)/uptime=35
3 线程连接数和命中率
mysql> show global status like 'threads_%';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Threads_cached | 74 |//代表当前此时此刻线程缓存中有多少空闲线程
| Threads_connected | 106 |//代表当前已建立连接的数量,因为一个连接就需要一个线程,所以也可以看成当前被使用的线程数
| Threads_created | 16760 |//代表从最近一次服务启动,已创建线程的数量
| Threads_running | 6 |//代表当前激活的(非睡眠状态)线程数
+-------------------+-------+
4 rows in set (0.00 sec)
mysql> show global status like 'connections';
+---------------+----------+
| Variable_name | Value |
+---------------+----------+
| Connections | 18352277 |
+---------------+----------+
1 row in set (0.00 sec)
mysql> select 1-16760/18352277;
+------------------+
| 1-16760/18352277 |
+------------------+
| 0.9991 |
+------------------+
1 row in set (0.00 sec)
线程缓存命中率=1-Threads_created/Connections = 99.991%
设置线程缓存个数
mysql> show variables like '%thread_cache_size%';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| thread_cache_size | 128 |
+-------------------+-------+
1 row in set (0.00 sec)
根据Threads_connected可预估thread_cache_size值应该设置多大,一般来说250是一个不错的上限值,如果内存足够大,也可以设置成thread_cache_size值和threads_connected值相同;
或者通过观察threads_created值,如果该值很大或一直在增长,可以适当增加thread_cache_size的值;在休眠状态下每个线程大概占用256KB左右的内存,所以当内存足够时,设置太小也不会节约太多内存,除非该值已经超过几千。
4 表缓存
mysql> show global status like 'open_tables%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables | 1822 |
+---------------+-------+
1 row in set (0.00 sec)
设置打开表的缓存和表定义缓存
mysql> show variables like 'table_open_cache';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| table_open_cache | 3000 |
+------------------+-------+
1 row in set (0.00 sec)
mysql> show variables like 'table_defi%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| table_definition_cache | 1400 |
+------------------------+-------+
1 row in set (0.01 sec)
针对MyISAM:
mysql每打开一个表,都会读入一些数据到table_open_cache 缓存 中,当mysql在这个缓存中找不到相应的信息时,才会去磁盘上直接读取,所以该值要设置得足够大以避免需要重新打开和重新解析表的定义,一般设置为max_connections的10倍,但最好保持在10000以内。
还有种依据就是根据状态open_tables的值进行设置,如果发现open_tables的值每秒变化很大,那么可能需要增大table_open_cache的值。
table_definition_cache 通常简单设置为服务器中存在的表的数量,除非有上万张表。
针对InnoDB:
与MyISAM不同,InnoDB的open table和open file并无直接联系,即打开frm表时其相应的ibd文件可能处于关闭状态;
故InnoDB只会用到table_definiton_cache,不会使用table_open_cache;
其frm文件保存于table_definition_cache中,而idb则由innodb_open_files决定(前提是开启了innodb_file_per_table),最好将innodb_open_files设置得足够大,使得服务器可以保持所有的.ibd文件同时打开。
5 最大连接数
mysql> show global status like 'Max_used_connections';
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| Max_used_connections | 1004 |
+----------------------+-------+
1 row in set (0.00 sec)
设置max_connections大小
mysql> show variables like 'max_connections%';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 2000 |
+-----------------+-------+
1 row in set (0.00 sec)
通常max_connections的大小应该设置为比Max_used_connections状态值大,Max_used_connections状态值反映服务器连接在某个时间段是否有尖峰,如果该值大于max_connections值,代表客户端至少被拒绝了一次,可以简单地设置为符合以下条件:Max_used_connections/max_connections=0.8
6 Innodb 缓存命中率
mysql> show global status like 'innodb_buffer_pool_read%';
+---------------------------------------+---------------+
| Variable_name | Value |
+---------------------------------------+---------------+
| Innodb_buffer_pool_read_ahead_rnd | 0 |
| Innodb_buffer_pool_read_ahead | 730802 |//预读的页数
| Innodb_buffer_pool_read_ahead_evicted | 0 |
| Innodb_buffer_pool_read_requests | 3801141425753 |//从缓冲池中读取的次数
| Innodb_buffer_pool_reads | 2755728 |//表示从物理磁盘读取的页数
+---------------------------------------+---------------+
5 rows in set (0.00 sec)
mysql> select 3801141425753/(3801141425753+730802+2755728);
+----------------------------------------------+
| 3801141425753/(3801141425753+730802+2755728) |
+----------------------------------------------+
| 1.0000 |
+----------------------------------------------+
1 row in set (0.00 sec)
缓冲池命中率 = (Innodb_buffer_pool_read_requests)/(Innodb_buffer_pool_read_requests + Innodb_buffer_pool_read_ahead + Innodb_buffer_pool_reads)=100%
如果该值小于99.9%,建议就应该增大innodb_buffer_pool_size的值了,该值一般设置为内存总大小的75%-85%,或者计算出操作系统所需缓存+mysql每个连接所需的内存(例如排序缓冲和临时表)+MyISAM键缓存,剩下的内存都给innodb_buffer_pool_size,不过也不宜设置太大,会造成内存的频繁交换,预热和关闭时间长等问题。
7 MyISAM Key Buffer命中率和缓冲区使用率
mysql> show global status like 'key_%';
+------------------------+-------------+
| Variable_name | Value |
+------------------------+-------------+
| Key_blocks_not_flushed | 0 |
| Key_blocks_unused | 6692 |
| Key_blocks_used | 6698 |
| Key_read_requests | 51726731594 |
| Key_reads | 5423302 |
| Key_write_requests | 11733536354 |
| Key_writes | 77164219 |
+------------------------+-------------+
7 rows in set (0.00 sec)
mysql> show variables like '%key_cache_block_size%';
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| key_cache_block_size | 1024 |
+----------------------+-------+
1 row in set (0.00 sec)
mysql> show variables like '%key_buffer_size%';
+-----------------+---------+
| Variable_name | Value |
+-----------------+---------+
| key_buffer_size | 8388608 |
+-----------------+---------+
1 row in set (0.00 sec)
缓冲区的使用率=1-(Key_blocks_unused*key_cache_block_size/ key_buffer_size)=18.31%
读命中率=1-Key_reads /Key_read_requests=99.99%
写命中率=1-Key_writes / Key_write_requests =99.34%
可看到缓冲区的使用率并不高,如果很长一段时间后还没有使用完所有的键缓冲,可以考虑把缓冲区调小一点。
键缓存命中率可能意义不大,因为它和应用相关,有些应用在95%的命中率下就工作良好,有些则需要99.99%,所以从经验上看,每秒的缓存未命中次数更重要,假设一个独立磁盘每秒能做100个随机读,那么每秒有5个缓冲未命中可能不会导致I/O繁忙,但每秒80个就可能出现问题。
每秒缓存未命中=Key_reads/uptime=0.33
8 临时表使用情况
mysql> show global status like 'Created_tmp%';
+-------------------------+----------+
| Variable_name | Value |
+-------------------------+----------+
| Created_tmp_disk_tables | 19226325 |
| Created_tmp_files | 117 |
| Created_tmp_tables | 56265812 |
+-------------------------+----------+
3 rows in set (0.00 sec)
mysql> show variables like '%tmp_table_size%';
+----------------+----------+
| Variable_name | Value |
+----------------+----------+
| tmp_table_size | 67108864 |
+----------------+----------+
1 row in set (0.00 sec)
可看到总共创建了56265812 张临时表,其中有19226325 张涉及到了磁盘IO,大概比例占到了0.34,证明数据库应用中排序,join语句涉及的数据量太大,需要优化SQL或者增大tmp_table_size的值,我设的是64M。该比值应该控制在0.2以内。
9 binlog cache使用情况
mysql> show status like 'Binlog_cache%';
+-----------------------+----------+
| Variable_name | Value |
+-----------------------+----------+
| Binlog_cache_disk_use | 645946 |
| Binlog_cache_use | 61175970 |
+-----------------------+----------+
2 rows in set (0.01 sec)
mysql> show variables like 'binlog_cache_size';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| binlog_cache_size | 32768 |
+-------------------+-------+
1 row in set (0.00 sec)
Binlog_cache_disk_use表示因为我们binlog_cache_size设计的内存不足导致缓存二进制日志用到了临时文件的次数
Binlog_cache_use 表示 用binlog_cache_size缓存的次数
当对应的Binlog_cache_disk_use 值比较大的时候 我们可以考虑适当的调高 binlog_cache_size 对应的值
10 Innodb log buffer size的大小设置
mysql> show variables like '%innodb_log_buffer_size%';
+------------------------+---------+
| Variable_name | Value |
+------------------------+---------+
| innodb_log_buffer_size | 8388608 |
+------------------------+---------+
1 row in set (0.00 sec)
mysql> show status like 'innodb_log_waits';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| Innodb_log_waits | 0 |
+------------------+-------+
1 row in set (0.00 sec)
innodb_log_buffer_size设置为8M,应该足够大了;Innodb_log_waits表示因log buffer不足导致等待的次数,如果该值不为0,可以适当增大innodb_log_buffer_size的值。
11 表扫描情况判断
mysql> show global status like 'Handler_read%';
+-----------------------+---------------+
| Variable_name | Value |
+-----------------------+---------------+
| Handler_read_first | 121257954 |
| Handler_read_key | 79172593389 |
| Handler_read_last | 1192685 |
| Handler_read_next | 1397568482926 |
| Handler_read_prev | 48594574644 |
| Handler_read_rnd | 2584877785 |
| Handler_read_rnd_next | 1083694866736 |
+-----------------------+---------------+
7 rows in set (0.00 sec)
Handler_read_first:使用索引扫描的次数,该值大小说不清系统性能是好是坏
Handler_read_key:通过key进行查询的次数,该值越大证明系统性能越好
Handler_read_next:使用索引进行排序的次数
Handler_read_prev:此选项表明在进行索引扫描时,按照索引倒序从数据文件里取数据的次数,一般就是ORDER BY ... DESC
Handler_read_rnd:该值越大证明系统中有大量的没有使用索引进行排序的操作,或者join时没有使用到index
Handler_read_rnd_next:使用数据文件进行扫描的次数,该值越大证明有大量的全表扫描,或者合理地创建索引,没有很好地利用已经建立好的索引
12 Innodb_buffer_pool_wait_free
mysql> show global status like 'Innodb_buffer_pool_wait_free';
+------------------------------+-------+
| Variable_name | Value | |
+------------------------------+-------+
| Innodb_buffer_pool_wait_free | 0 |
+------------------------------+-------+
1 row in set (0.00 sec)
该值不为0表示buffer pool没有空闲的空间了,可能原因是innodb_buffer_pool_size设置太大,可以适当减少该值。
13 join操作信息
mysql> show global status like 'select_full_join';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| Select_full_join | 10403 |
+------------------+-------+
1 row in set (0.00 sec)
该值表示在join操作中没有使用到索引的次数,值很大说明join语句写得很有问题
mysql> show global status like 'select_range';
+---------------+----------+
| Variable_name | Value |
+---------------+----------+
| Select_range | 22450380 |
+---------------+----------+
1 row in set (0.00 sec)
该值表示第一个表使用ranges的join数量,该值很大说明join写得没有问题,通常可查看select_full_join和select_range的比值来判断系统中join语句的性能情况
mysql> show global status like 'Select_range_check';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| Select_range_check | 0 |
+--------------------+-------+
1 row in set (0.00 sec)
如果该值不为0需要检查表的索引是否合理,表示在表n+1中重新评估表n中的每一行的索引是否开销最小所做的联接数,意味着表n+1对该联接而言并没有有用的索引。
mysql> show GLOBAL status like 'select_scan';
+---------------+-----------+
| Variable_name | Value |
+---------------+-----------+
| Select_scan | 116037811 |
+---------------+-----------+
1 row in set (0.00 sec)
select_scan表示扫描第一张表的连接数目,如果第一张表中每行都参与联接,这样的结果并没有问题;如果你并不想要返回所有行但又没有使用到索引来查找到所需要的行,那么计数很大就很糟糕了。
14 慢查询
mysql> show global status like 'Slow_queries';
+---------------+--------+
| Variable_name | Value |
+---------------+--------+
| Slow_queries | 114111 |
+---------------+--------+
1 row in set (0.00 sec)
该值表示mysql启动以来的慢查询个数,即执行时间超过long_query_time的次数,可根据Slow_queries/uptime的比值判断单位时间内的慢查询个数,进而判断系统的性能。
15表锁信息
mysql> show global status like 'table_lock%';
+-----------------------+------------+
| Variable_name | Value |
+-----------------------+------------+
| Table_locks_immediate | 1644917567 |
| Table_locks_waited | 53 |
+-----------------------+------------+
2 rows in set (0.00 sec)
这两个值的比值:Table_locks_waited /Table_locks_immediate 趋向于0,如果值比较大则表示系统的锁阻塞情况比较严重