利用MySQL系统数据库做性能负载诊断的方法

时间:2021-12-23 03:08:47

某大师曾说过,像了解自己的老婆 一样了解自己管理的数据库,个人认为包含了两个方面的了解:

1,在稳定性层面来说,更多的是关注高可用、读写分离、负载均衡,灾备管理等等high level层面的措施(就好比要保证生活的稳定性)

2,在实例级别的来说,需要关注内存、io、网络,热点表,热点索引,top sql,死锁,阻塞,历史上执行异常的sql(好比生活品质细节)mysql的performance_data库和sys库提供了非常丰富的系统日志数据,可以帮助我们更好地了解非常细节的,这里简单地列举出来了一些常用的数据。

sys库是以较为可读化的方式封装了performance_data中的某些表,因此这些个数据来源还是performance_data库中的数据。

这里粗略列举出个人常用的一些系统数据,可以在实例级别更加清楚地了解mysql的运行过程中资源分配情况。

status中的信息

mysql的status变量只是给出了一个总的信息,从status变量上无法得知详细资源的消耗,比如io或者内存的热点在哪里,库、表的热点在哪里,如果想要知道具体的明细信息就需要系统库中的数据。

前提要开启performance_schema,因为sys库的视图是基于performance_schema的库的。

利用MySQL系统数据库做性能负载诊断的方法

内存使用:

内存/innodb_buffer_pool使用

概要innodb_buffer_pool的使用情况summary,已知当前实例262144*16/1024 = 4096mb buffer pool,已使用23260*16/1024 363mb

利用MySQL系统数据库做性能负载诊断的方法

innodb_buffer_pool已占用内存的明细信息,可以按照库表的维度来统计

利用MySQL系统数据库做性能负载诊断的方法

  1. set session transaction isolation level read uncommitted ; 
  2. select  
  3.   database_name, 
  4.   sum(compressed_size)/1024/1024 as allocated_memory, 
  5.   sum(data_size)/1024/1024 as data_memory, 
  6.   sum(is_hashed)*16/1024 as is_hashed_memory, 
  7.   sum(is_old)*16/1024 as is_old_memory 
  8. from  
  9.  select  
  10.   case when instr(table_name,'.')>0 then replace(substring(table_name,1,instr(table_name,'.')-1),'`',''
  11.   else  'system_database' end as database_name, 
  12.   case when instr(table_name,'.')>0 then replace(substring(table_name,instr(table_name,'.')+1),'`',''
  13.   else 'system_obj' end as table_name, 
  14.   if(compressed_size = 0, 16384, compressed_size) as compressed_size, 
  15.   data_size, 
  16.   if(is_hashed = 'yes',1,0) is_hashed, 
  17.   if(is_old = 'yes',1,0) is_old 
  18.  from information_schema.innodb_buffer_page 
  19.  where table_name is not null 
  20. ) t 
  21. group by database_name 
  22. order by allocated_memory desc 
  23. limit 10; 

库表的读写统计,逻辑层面的热点数据统计

目标表是performance_schema.table_io_waits_summary_by_table,某些文章上说是逻辑io,其实这里跟逻辑io并无关系,这个表中的字段含义是基于表,读写的到的行数的统计。至于真正的逻辑io层面的统计,笔者目前还有不知道有哪个可用的系统表来查询。这个库可以很清楚地看到这个表中的统计结果是怎么计算出来的。

利用MySQL系统数据库做性能负载诊断的方法

基于表的读写的行的次数统计,这是一个累计值,单纯的看这个值本身,个人觉得意义不大,需要定时收集计算差值,才具备参考意义。

以下按照库级别统计表的读写情况。

利用MySQL系统数据库做性能负载诊断的方法

库表的读写统计,物理io层面的热点数据统计

按照物理io的维度统计热点数据,哪些库表消耗了多少物理io。这里原始系统表中的数据是一个累计统计的值,最极端的情况就是一个表为0行,却存在大量的物理读写io。

  1. set session transaction isolation level read uncommitted ; 
  2. select  
  3.  database_name, 
  4.  ifnull(cast(sum(total_read) as signed),0) as total_read, 
  5.  ifnull(cast(sum(total_written) as signed),0) as total_written, 
  6.  ifnull(cast(sum(total) as signed),0) as total_read_written 
  7. from 
  8.  select  
  9.   substring(replace(file, '@@datadir/'''),1,instr(replace(file, '@@datadir/'''),'/')-1) as database_name, 
  10.   count_read, 
  11.   case  
  12.    when instr(total_read,'kib')>0 then replace(total_read,'kib','')/1024 
  13.    when instr(total_read,'mib')>0 then replace(total_read,'mib','')/1024 
  14.    when instr(total_read,'gib')>0 then replace(total_read,'gib','')*1024 
  15.   end as total_read, 
  16.   case  
  17.    when instr(total_written,'kib')>0 then replace(total_written,'kib','')/1024 
  18.    when instr(total_written,'mib')>0 then replace(total_written,'mib',''
  19.    when instr(total_written,'gib')>0 then replace(total_written,'gib','')*1024 
  20.   end as total_written, 
  21.   case  
  22.    when instr(total,'kib')>0 then replace(total,'kib','')/1024 
  23.    when instr(total,'mib')>0 then replace(total,'mib',''
  24.    when instr(total,'gib')>0 then replace(total,'gib','')*1024 
  25.   end as total 
  26.  from sys.io_global_by_file_by_bytes  
  27.  where file like '%@@datadir%' and instr(replace(file, '@@datadir/'''),'/')>0  
  28. )t 
  29. group by database_name 
  30. order by total_read_written desc; 

ps:个人不太喜欢mysql自定义的format_***函数,这个函数的初衷是好的,把一些数据(时间,存储空间)等格式化成更加可读的模式。但是却不支持单位的参数,更多的时候想以某个固定的单位来显示,比如格式化一个的时间,格式化后根据单位大小可能会显示微妙,或者是毫秒,或者是秒,或者分钟,或者天。比如想把时间统一格式化成秒,对不起,不支持,某些个数据不仅仅是看一眼那么简单,甚至是要读出来存档分析的,因此这里不建议也不会使用那些个format函数

top sql 统计

可以按照执行时间,阻塞时间,返回行数等等维度统计top sql。
另外可以按照时间筛选last_seen,可以统计最近某一段时间出现过的top sql

利用MySQL系统数据库做性能负载诊断的方法

  1. set session transaction isolation level read uncommitted ; 
  2.  
  3. select  
  4.  schema_name, 
  5.  digest_text, 
  6.  count_star, 
  7.  avg_timer_wait/1000000000000 as avg_timer_wait, 
  8.  max_timer_wait/1000000000000 as max_timer_wait, 
  9.  sum_lock_time/count_star/1000000000000 as avg_lock_time , 
  10.  sum_rows_affected/count_star as avg_rows_affected, 
  11.  sum_rows_sent/count_star as avg_rows_sent , 
  12.  sum_rows_examined/count_star as avg_rows_examined, 
  13.  sum_created_tmp_disk_tables/count_star as avg_create_tmp_disk_tables, 
  14.  sum_created_tmp_tables/count_star as avg_create_tmp_tables, 
  15.  sum_select_full_join/count_star as avg_select_full_join, 
  16.  sum_select_full_range_join/count_star as avg_select_full_range_join, 
  17.  sum_select_range/count_star as avg_select_range, 
  18.  sum_select_range_check/count_star as avg_select_range, 
  19.  first_seen, 
  20.  last_seen 
  21. from performance_schema.events_statements_summary_by_digest 
  22. where last_seen>date_add(now(), interval -1 hour) 
  23. order by  
  24. max_timer_wait 
  25. -- avg_timer_wait 
  26. -- sum_rows_affected/count_star  
  27. -- sum_lock_time/count_star 
  28. -- avg_lock_time 
  29. -- avg_rows_sent 
  30. desc 
  31. limit 10; 

需要注意的是,这个统计是按照mysql执行一个事务消耗的资源做统计的,而不是一个语句,笔者一开始懵逼了一阵子,举个简单的例子。

参考如下,这里是循环写个数据的一个存储过程,调用方式就是call create_test_data(n),写入n条测试数据。
比如call create_test_data(1000000)就是写入100w的测试数据,这个执行过程耗费了几分钟的时间,按照笔者的测试实例情况,avg_timer_wait的维度,绝对是一个top sql。

但是在查询的时候,始终没有发现这个存储过程的调用被列为top sql,后面尝试在存储过程内部加了一个事物,然后就顺利地收集到了整个top sql.

因此说performance_schema.events_statements_summary_by_digest里面的统计,是基于事务的,而不是某一个批处理的执行时间的。

  1. create definer=`root`@`%` procedure `create_test_data`( 
  2.  in `loopcnt` int 
  3. language sql 
  4. not deterministic 
  5. contains sql 
  6. sql security definer 
  7. comment '' 
  8. begin 
  9.  -- start transaction;  
  10.   while loopcnt>0 do 
  11.    insert into test_mrr(rand_id,create_date) values (rand()*100000000,now(6)); 
  12.    set loopcnt=loopcnt-1; 
  13.   end while
  14.  -- commit; 
  15. end 

另外一点比较有意思的是,这个系统表是为数不多的支持truncate的,当然它在内部,也是在不断收集的一个过程。

利用MySQL系统数据库做性能负载诊断的方法

执行失败的sql 统计

一直以为系统不会记录执行失败的解析错误的sql,比如想统计因为超时而执行失败的语句,后面才发现,这些信息,mysql会完整地记录下来

利用MySQL系统数据库做性能负载诊断的方法

这里会详细记录执行错误的语句,包括最终执行失败(超时之类的),语法错误,执行过程中产生了警告之类的语句。用sum_errors>0 or sum_warnings>0去performance_schema.events_statements_summary_by_digest筛选一下即可。

  1. set session transaction isolation level read uncommitted ; 
  2.  
  3. select  
  4.  schema_name, 
  5.  digest_text, 
  6.  count_star, 
  7.  first_seen, 
  8.  last_seen 
  9. from performance_schema.events_statements_summary_by_digest 
  10. where sum_errors>0 or sum_warnings>0  
  11. order by last_seen desc; 

index使用情况统计

基于performance_schema.table_io_waits_summary_by_index_usage这个系统表,其统计的维度同样是“按照某个索引查询返回的行数的统计”。

可以按照哪些索引使用最多最少等情况进行统计。

利用MySQL系统数据库做性能负载诊断的方法

不过这个统计有一个给人潜在一个误区:
count_read,count_write,count_fetch,count_insert,count_update,count_delete统计了某个索引上使用到索引的情况下,受影响的行数,sum_timer_wait是累计在该索引上等待的时间。

如果使用到了该索引,但是没有数据受影响(就是没有dml语句的条件没有命中数据),将count_***不会统计进来,但是sum_timer_wait会统计进来
这就存在一个容易受到误导的地方,这个索引明明没有命中过很多次,但是却产生了大量的timer_wait,索引看到类似的信息,也不能贸然删除索引。

 

等待事件统计

mysql数据库中的任何一个动作,都需要等待(一定的时间来完成),一共有超过1000个等待事件,分属不懂的类别,每个版本都不一样,且默认不是所有的等待事件都启用。

利用MySQL系统数据库做性能负载诊断的方法

个人认为等待事件这个东西,仅做参考,不具备问题的诊断性,即便是再优化或者低负载的数据库,累计一段时间,某些事件仍旧会积累大量的等待事件。

这些事件的等待事件,不一定都是负面性的,比如事物的锁等待,是在并发执行过程中必然会生成的,这个等待事件的统计结果,也是累计的,单纯的看一个直接的值,不具备任何参考意义。
除非定期收集,做差值计算,根据实际情况,才具备参考意义。

利用MySQL系统数据库做性能负载诊断的方法

  1. set session transaction isolation level read uncommitted ; 
  2.  
  3. select substring_index(name, '/', 1) as wait_type,count(1)  
  4. from performance_schema.setup_instruments 
  5. group by 1  
  6. order by 2 desc; 
  7.  
  8.  
  9. select 
  10. event_name, 
  11. count_star, 
  12. sum_timer_wait 
  13. from performance_schema.events_waits_summary_global_by_event_name 
  14. where event_name != 'idle' 
  15. order by sum_timer_wait desc 
  16. limit 100; 

最后,需要注意的是,

1,mysql提供的诸多的系统表(视图)中的数据,单纯的看这个值本身,因为它是一个累计值,个人觉得意义不大,尤其是avg_***,需要结合多方面的综合因素,做参考使用。
2,任何系统表的查询,都可能对系统性能的本身造成一定的影响,不要再对系统可能产生较大负面影响的情况下做数据的统计收集。

总结

以上所述是小编给大家介绍的利用mysql系统数据库做性能负载诊断的方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!

如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

原文链接:https://www.cnblogs.com/wy123/archive/2019/09/01/11431227.html