zoukankan      html  css  js  c++  java
  • 11-MySQL DBA笔记-MySQL的监控

    第11章 MySQL的监控
    为什么我们需要监控呢?因为如果没有了监控,那么我们的服务可用性就无从度量,我们也无法及时地发现问题和处理问题。
    一个完善的监控体系,不仅需要进行实时的监控,也需要分析历史的监控数据,以便掌握性能和容量趋势的变化,从而为产品、架构人员提供决策的依据。
    本章将为读者讲述针对MySQL所提供的监控方法,然后,再来探讨下数据库监控的友好呈现,也就是数据的可视化技术。

    11.1 非数据库的监控
    11.1.1 开源监控工具/平台
    一个完整的监控体系,要求能够监控各种非数据库的资源,如操作系统、硬件、网络等。
    目前比较流行的方式是部署一些集中监控工具,当你需要维护越来越多的机器,特别是基于云的部署时,一个集中式的监控产品就会变得很重要了,
    你可以从该监控界面上,查看所有设备的使用情况。
    对于集中式的监控产品,一般需要在被监控的服务器中部署一个代理服务(agent)来收集数据,如Ganglia、Nagio等,或者通过一些系统服务收集信息,比如 snmp。
    广泛使用的一些平台有Zabbix、Nagios、Ganglia、Cacti,读者可以自行阅读相关图书,学习如何使用它们,本书将只关注数据库的性能监控和故障发现。
    有时我们希望能够开发出自己的数据库监控平台,自己编写脚本、工具来收集信息。这样会更有针对性。
    不过笔者建议读者使用市面上流行的监控工具或平台,很多监控平台都有MySQL相关的监控插件,我们需要做的只是少量的二次开发工作。
    完全重新开发一个监控平台的成本往往会比较高,需要综合权衡是否有必要投入人力去实现,有时,在一些开源软件上做二次开发,是更经济的方式。

    11.1.2 编写程序来收集信息
    如果我们想要自己编写脚本收集信息,那么,对于操作系统的信息收集一般有两种方式。
    第一种,使用工具收集。可以使用vmstat、dstat、iostat、sar、netstat这些工具/命令实时观察操作系统。
    第二种,直接使用接口,比如读取/proc/伪文件系统数据这种方式,/proc是许多可视化工具获取信息的来源,一些工具,如ps、top、pmap其实就是读取/proc下的数据。
    使用这种方式有一个风险,那就是它们可能没有工具那么通用,在系统升级后,数据格式可能会有变化,你需要调整处理数据的程序。
    在Linux系统中,/proc是一个伪文件系统(启动时动态生成的文件系统),它是访问内核统计数据的一个接口,
    由于/proc不是一个真正的文件系统,因此它并不占用存储空间,而是占用有限的内存。
    /proc下面有很多以进程id命名的目录,目录下的很多文件记录了进程的使用信息。
    /proc目录下也有一些系统级别的统计信息, 如/proc/meminfo就记录了内存活动和统计信息。
    自行编写程序进行监控需要考虑到如下一些要点:
    1)Linux的I/O是比较难监控的,如果在一台主机之上有多个应用,那么判断I/O负荷重的业务有哪些将会很困难。
    安装iotop之类的工具可以更快地定位到I/O负荷重的进程之上,但iotop之类的工具需要新的内核支持。
    2)由于SSD的大量使用,因此还需要增加对SSD的监控,常用的方式是使用smartctl命令进行监控。
    3)有时我们需要模拟业务访问。
    人的行为是复杂的,复杂的业务系统更是充满了变数,模拟人的行为是一件困难且富有挑战性的事情,
    我们应该模拟尽量真实的访问,这样才能得到真实的反馈,从而衡量服务是否真的健壮、可靠、体验良好。
    4)要注意收集信息的频率,粒度太大了可能不能及时发现问题。
    5)要防止积压收集信息的程序任务。
    6)要确保报警通知到人,还要确保邮件服务、短信等通道的正常。

    11.2 数据库的监控
    11.2.1 数据库服务的基本监控方式
    一般数据库的监控包括探测数据库主库的可用性、复制状态监控、数据库的性能监控、数据库的故障发现等。
    对于数据库主库的监控,可以在主库上创建一张监控表,使用监控程序定期去读写这张表中的数据,以判断数据库是否可以正常提供服务。
    对于数据库从库的监控,由于从库一般都是只读的,因此只需要定期查询从库上监控表的数据即可。
    对于复制状态的监控,由于主库有定期更新的监控表,因此可以认为它也是一张心跳表,表里的数据带有时间戳信息,
    主库监控表(心跳表)每分钟被UPDATE一次,去从库中查询对应的记录,就可以依据记录内的时间戳信息来确认延时了多少。
    这里需要说明的是,MySQL自身的SHOW SLAVE STATUSG显示的延时时间可能是不准确的,所以,推荐使用心跳表的方式。
    需要注意的是,每次信息收集的时间间隔不能太大,否则会难以发现和诊断问题。
    比如磁盘I/O数据每10分钟才去收集一次,数据库性能每隔几分钟才去收集一次,就不是一个好的选择。
    数据库的性能监控主要依靠于收集MySQL的一些状态变量,也就是SHOW GLOBAL STATUS的输出。
    数据库的故障发现涉及的内容包括:分析MySQL的查询响应、错误日志,以及监控是否可以读写数据库。

    11.2.2 应该收集的信息和收集方法
    我们收集的信息主要包括MySQL的运行状态、程序性能日志、慢查询日志、状态变量、数据量、数据占用空间等。
    1.MySQL的参数及运行状态
    以下代码可查看MySQL实例的参数及运行状态。
    SHOW VARIABLES LIKE '%parameter%' ;
    SHOW [FULL] PROCESSLIST ;
    SHOW ENGINE INNODB STATUS G;
    以下是对一个SHOW PROCESSLIST的解析。可以看到不同状态下线程的比例。
    mysql -uroot -p -S /path/to/tmp/3306/mysql.sock -e 'SHOW PROCESSLISTG' | grep State: | sort | uniq -c | sort -rn
    下面来解释一些常用的状态。
    Sleep:线程正在等待来自客户端的新查询。
    Query:线程正在执行查询,或者正在发送结果给客户端。
    Locked:线程正在等待表锁。
    Analyzing和statistics:线程正在获取存储引擎的统计数据和优化查询。
    Copying to tmp table[on disk]:线程正在处理查询,复制数据到临时表中。如果后面有“on disk”字样,则表明MySQL正在将内存临时表转换为磁盘临时表。
    Sorting result:线程正在排序结果集。
    Sending data:这个状态有多种可能,可能是内部各步骤之间传递数据,生成结果集;或者是将结果集返回给客户端。
    大多数状态对应的操作都非常快。如果一个线程停留在一个给定的状态好几秒,那么它可能是有问题的,需要进一步查明。
    下面来查看InnoDB的一些统计数据,命令如下所示。SHOW ENGINE INNODB STATUS G;
    如下命令可查看SQL的执行频率,实时显示当前各种SQL的执行频率等信息,该命令摘录自网上。
    mysqladmin -uroot -p -r -i 2 extended-status |awk -F "|" 'BEGIN { count=0; }
    {
    if($2 ~ /Variable_name/ && ++count%15 == 1)
    {print "----------|---------|--- MySQL Command Status --|----- InnoDB row operation -----|-- Buffer Pool Read --"; print "---Time---|---QPS---|select insert update delete| read inserted updated deleted| logical physical";}
    else if ($2 ~ /Queries/){queries=$3;}
    else if ($2 ~ /Com_select /){com_select=$3;}
    else if ($2 ~ /Com_insert /){com_insert=$3;}
    else if ($2 ~ /Com_update /){com_update=$3;}
    else if ($2 ~ /Com_delete /){com_delete=$3;}
    else if ($2 ~ /InnoDB_rows_read/){innodb_rows_read=$3;}
    else if ($2 ~ /InnoDB_rows_deleted/){innodb_rows_deleted=$3;}
    else if ($2 ~ /InnoDB_rows_inserted/){innodb_rows_inserted=$3;}
    else if ($2 ~ /InnoDB_rows_updated/) {innodb_rows_updated=$3;}
    else if ($2 ~ /InnoDB_buffer_pool_read_requests/){innodb_lor=$3;}
    else if ($2 ~ /InnoDB_buffer_pool_reads/){innodb_phr=$3;}
    else if ($2 ~ /Uptime / && count >= 2){ printf(“ %s |%9d",strftime(“ %H:%M:%S” ),queries);
    printf("|%6d %6d %6d %6d",com_select,com_insert,com_update,com_delete);
    printf("|%8d %7d %7d %7d",innodb_rows_read,innodb_rows_inserted,innodb_rows_updated,innodb_rows_deleted);
    printf("|%10d %11d ",innodb_lor,innodb_phr);}}'
    如果需要做进一步的分析,也可以用tcpdump配合pt-query-digest工具来获取更多的信息,它所生成的报告不仅包括SQL的计数,还包括SQL的耗时及其他成本信息。
    首先,在root用户下执行如下命令。 nohup tcpdump -i eth1 port 3306 -s 65535 -x -nn -q -tttt > db1000_sql_new.log &
    然后在mysql用户下执行如下命令。 ./pt-query-digest --type=tcpdump --watch-server 11.11.11.11:3306 db1000_sql_new.log > app_db.rtf
    对于以上生产报告,可以发送邮件给DBA阅读,或者将其过滤后存放在数据库中。
    SQL的统计最好在应用层收集信息,这是笔者推荐的方式,SQL的很多统计,结合应用才能易于理解,才能更好地评判是否应该进行优化,大致方法如下。
    1)直接记录SQL到日志,统计日志中各种查询的比例。
    2)根据Web服务器日志,例如根据一天中高峰期一个小时的日志,统计涉及某些功能(SQL)页面的日志在总的日志中所占的比例。

    2.性能日志
    这里所说的性能日志,一般是指程序性能日志。
    很多公司并没有考虑性能日志,主要是出于开发的成本考虑。
    但一个良好的、完善的服务程序,应该包含自诊断的信息,以协助诊断问题。
    我们应先查看整个应用的性能表现,从总体上来分析。
    一般来说,程序的性能日志是最容易诊断出性能瓶颈的,性能日志也可以用图形的方式展示出应用的性能变化趋势,方便作为以后扩容的依据。
    例如,对于一个PHP程序,应该收集的信息主要有如下几点。
    合计执行时间(页面执行时间)。 其他各部分时间相加应等于合计执行时间,差别不能过大,否则就要研究是否哪部分操作未做记录。
    每个查询的执行时间。
    打开的连接。
    对外部服务的调用。
    可能消耗资源较大的数据库操作。
    如果性能日志足够详细,那么就可以快速地定位性能的瓶颈所在了,从而判断是否真的是MySQL导致了性能问题,是否访问MySQL耗费了绝大部分的页面时间。
    进行压力测试的时候,可以定位伸缩性存在问题的环节。
    关于性能日志更详细的信息请参考4.4节。

    3.慢查询日志
    除了SHOW GLOBAL STATUS和SHOW PROCESSLIST之外,还可以检查慢查询日志,一般推荐优先采用前面两种方式检查系统,慢查询的检查又耗时又复杂。
    MySQL提供了两类日志:通用日志(generallog)和慢查询日志(slowlog)。
    通用日志记录了接收的所有查询。这个日志有助于判断读写的比例,看MySQL的主要工作是什么?
    但打开通用日志需要注意日志的空间消耗,可能还需要考虑轮询切割日志。
    一般情况下没必要启用通用日志。这里仅分析慢查询日志。
    慢查询日志记录了慢查询情况,我们可以把捕捉到的日志二次处理后发送给研发人员进行优化,MySQL 5.1及以上版本可以动态启用慢查询日志,MySQL 5.0则需要重启后才能生效。
    需要设置的参数如下。
    MySQL 5.0需要设置log_slow_queries和slow_launch_time。
    MySQL 5.1需要设置slow_query_log和long_query_time,这里不需要再使用slow_launch_time这个参数了,因为这个参数不能设置到毫秒级。
    MySQL 5.1.21后可以进行毫秒级的慢查询记录,例如,设置long_query_time=0.01。
    有一个参数log_queries_not_using_indexes,也可以协助分析,不过小表无须建立索引速度也很快,这样的情况下,使用该参数可能会导致产生大量的日志记录。
    因此建议忽略这个参数,不予设置。
    有一些补丁或MySQL分支,如Percona Server,可以显示出更翔实的慢查询信息,这样就有助于我们探查到底是什么原因导致的查询慢,
    因为官方版本中慢查询日志默认的输出信息都比较粗略,并没有告诉我们查询为什么会变慢。
    某个SQL出现在慢查询日志里,并不意味着这就是一个质量差的SQL,也并不表示现在或未来这个查询很慢,也许你手动执行它,会非常之快。
    有诸多因素会影响到SQL的响应:如锁表、数据或索引初次使用时未被缓存、磁盘I/O紧张、内存泄露等。
    现实中,如果一个查询平时运行得很快,但在发现性能问题时被记入慢查询日志,可能是因为其他查询占用了大量的系统资源,被阻塞而导致的。
    对于慢查询日志的分析可以使用MySQL自带的mysqldump slow来实现,还有一些比mysqldumpslow更强大的分析工具,如pt-query-digest。
    对于慢查询日志,可以关注执行时间过长的查询,或者执行次数过多的查询,或者结果集过大的查询。
    通过如下命令,可以看到每秒的慢查询统计,以方便绘图。
    当检查到有突变时,往往这个时候会有异常发生,可以更进一步到具体的慢查询日志中去查找可能的原因。
    awk '/^# Time:/{print $3, $4, c;c=0}/^# User/{c++}' slowquery.log > /tmp/aaa.log

    4.状态变量
    如SHOW GLOBAL STATUS、SHOW SESSION STATUS和SHOW PROFILE
    查看全局状态变量的命令如下。 SHOW GLOBAL STATUS LIKE '%parameter%';
    查看吞吐率时,可多次运行下面的命令,检查增量。 SHOW GLOBAL STATUS LIKE ‘ %question%’ ;SHOW GLOBAL STATUS LIKE ‘ Com_%’ ;
    也可以使用如下命令检查多个系统状态变量的变化。
    SHOW GLOBAL STATUS WHERE Variable_name LIKE 'Com_select' OR Variable_name LIKE 'Com_insert' OR variable_name LIKE 'com_update' OR variable_name LIKE 'com_delete' OR variable_name LIKE '%Qcache_hits';
    使用mysqladmin命令可监视状态变量的变化,注意如下命令中添加了参数-r。
    mysqladmin -uroot -p extended-status -r -i 10 |egrep "Com_select|Com_insert|Com_delete|Com_update|Qcache_hits|Handler_write|Handler_read”
    mysqladmin命令的另一个示例如下。
    mysqladmin -uroot -p -i1 | awk ' /Queries/{q=$4-qp;qp=$4} /Threads_connected/{tc=$4} /Threads_running/{printf "%5d %5d %5d ", q,tc, $4}'
    也可以使用监控工具的一些插件来监控状态变量的变化,如使用Cacti的MySQL插件。
    Cacti有丰富的模板支持,可以近乎实时地监察MySQL的运行状态。
    它主要也是用于获取SHOW GLOBAL STATUS的信息。
    关于查询读写比率的计算,可以大致采用如下的公式进行计算。 (SELECT + Qcache_hits ) / (INSERT + UPDATE + REPLCACE + DELETE)
    相应的状态变量可以查询以“com_”或以“handler_”为前缀的一些变量。
    在SHOW GLOBAL STATUS中,我们需要关注的主要有以下几个计数器:handler、temporary files、command、wait。
    有时我们需要单独分析一些查询的成本,需要先手动清除状态变量(运行命令FLUSH STATUS),然后再运行查询,最后重新运行SHOW SESSION STATUS, 从此来查看查询所耗费的成本。
    SHOW SESSION STATUS,顾名思义,显示的是当前会话的状态变量,它不受其他进程的影响。
    以下示例显示了执行一个SQL后会话的Select%状态的变化,为了节省空间,这里没有列出所有状态的值。
    flush status;
    mysql> SELECT SQL_NO_CACHE ... from ...
    mysql> show session status like 'Select%';
    | Variable_name | Value |
    +---------------------+---------+
    Select_full_join | 0 |
    Select_full_range_join |
    | Select_range | 0 |
    | Select_range_check | 0 |
    | Select_scan | 2 |
    +---------------------+---------+
    Select_full_join:全表扫描连接的次数,如果该值比较高,那么可能是没有正确地创建索引。
    Select_full_range_join:在引用的表中使用范围查找的连接数量。
    Select_scan:执行了全表扫描的数量。
    如下命令将检查存储引擎操作。
    mysql> SHOW SESSION STATUS LIKE 'Handler%';
    +------------------------+---------+
    | Variable_name | Value |
    +------------------------+---------+
    | Handler_commit | 0 |
    | Handler_delete | 0 |
    | Handler_discover | 0 |
    | Handler_prepare | 0 |
    | Handler_read_first | 1 |
    | Handler_read_key | 5665 |
    | Handler_read_next | 5662
    | Handler_read_prev | 0 | DESC。
    | Handler_read_rnd | 200 |
    | Handler_read_rnd_next | 207 |
    | Handler_rollback | 0 |
    | Handler_savepoint | 0 |
    | Handler_savepoint_rollback | 0 |
    | Handler_update | 5262 |
    | Handler_write | 219 |
    Handler_read_first:索引中第一条被读的次数。如果较高,则代表服务器正在执行大量全索引扫描。
    Handler_read_key:根据键读取一行记录的请求数。如果该值较高,则说明查询和表的索引是正确的。
    Handler_read_next:按照键顺序读下一行的请求数。如果你使用范围约束或执行索引扫描来查询索引列,那么该值会增加。
    Handler_read_prev:按照键顺序读取前一行的请求数。
    Handler_read_rnd:根据固定位置读取一行的请求数。如果你正执行大量的查询并且需要对结果进行排序,那么该值会较高。
    如果使用了大量需要MySQL扫描整个表的查询语句,或者连接没有正确地使用键,那么该值也会较高。
    Handler_read_rnd_next:在数据文件中读取下一行的请求数。如果你正在进行大量的表扫描,那么该值会较高。
    通常情况下,该值高说明你的表索引不正确,或者写入的查询没有利用索引。
    Handler_update:在表内插入一行的请求数。以上示例中Handler_update计数器的值比较高,
    是因为MySQL的GROUP BY、ORDER BY操作会先把表写入一个临时表,扫描后进行排序,然后进行输出。
    Sort_merge_passes:排序算法已经执行了合并的数量。如果这个变量值较大,则应该考虑增加sort_buffer_size系统变量的值。
    如下命令将检查sort相关的统计。
    mysql> SHOW SESSION STATUS LIKE'Sort%';
    +---------------------+---------+
    | Variable_name | Value |
    +---------------------+---------+
    | Sort_merge_passes | 0 |
    | Sort_range | 0 |
    | Sort_rows | 200 |
    | Sort_scan | 1 |
    +---------------------+---------+
    Sort_rows:已经排序的行数。
    Sort_scan:通过扫描表完成排序的数量。
    如下命令将查看临时表的创建情况。
    mysql> SHOW SESSION STATUS LIKE 'Created%';
    +---------------------+---------+
    | Variable_name | Value |
    +---------------------+---------+
    | Created_tmp_disk_tables | 0 |
    | Created_tmp_files | 0 |
    | Created_tmp_tables | 5 |
    Created_tmp_disk_tables:如果持续增加,那么可能是有性能问题。
    以上输出可能仍然会受到内部操作的影响,建议多运行几次查询,从而得到一个比较可靠的增量。
    笔者将在后续章节里更详细地解释一些状态变量的含义。
    另外还有一个简单易用的方法,使用SHOW PROFILE。该功能默认是关闭的,但是会话级别可以开启这个功能。
    开启它可以让MySQL收集在执行语句的时候所使用的资源和耗时。
    下面的示例将使用SET profiling=1开启这个功能。
    oot@localhost test>SHOW VARIABLES LIKE '%profil%';
    +---------------------+---------+
    | Variable_name | Value |
    +---------------------+---------+
    | profiling | OFF |
    | profiling_history_size | 15 |
    root@localhost test>SET profiling = 1;
    root@localhost test>SELECT COUNT(*) FROM testad; --1
    root@localhost test>SHOW PROFILES G;
    *************************** 1. row ***************************
    Query_ID: 1
    Duration: 0.00015100
    Query: select count(*) from testad 1 row in set (0.00 sec)
    ERROR: No query specified
    root@localhost test>SHOW PROFILES;
    +--------------+-------------+---------+
    | Query_ID | Duration | Query |
    +--------------+-------------+---------+
    | 1 | 0.00015100 | select count(*) from testad |
    | 2 | 0.00017100 | select count(*) from testac |
    如果SHOW PROFILE后不加参数,则显示最近的查询统计。Status栏位与SHOW FULL PROCESSLIST的Status栏位相同。
    root@localhost test>SHOW PROFILE;
    +---------------------------------+-----------+
    Status | Duration |
    +---------------------------------+-----------+
    | starting | 0.000031 |
    | checking query cache for query | 0.000035 |
    | Opening tables | 0.000011 |
    | System lock | 0.000004 |
    | Table lock | 0.000019 |
    | init | 0.000010 |
    optimizing | 0.000006 |
    | executing | 0.000009 |
    | end | 0.000003 |
    | query end | 0.000002 |
    | freeing items | 0.000011 |
    | storing result in query cache | 0.000006 |
    | logging slow query | 0.000002 |
    | cleaning up | 0.000002 |
    mysql> SHOW PROFILE CPU FOR QUERY 1;
    如上介绍了SHOW SESSION STATUS及SHOW PROFILE命令,笔者很少使用它们,一般来说,使用SHOW GLOBAL STATUS命令检查状态变量即可。

    5.MySQL实例的数据增长
    我们需要获取MySQL实例的数据增长情况,以便提前进行扩容,
    MySQL的information_schema库记录了各个库、表的数据量大小,可以据此统计实例的数据增长情况,以及各个库,甚至各个表的数据增长情况,
    研发人员通过判断表的数据量增长趋势及数据库的操作频率,大致判断应用的数据库流量的特点,从而更有针对性地进行数据库的应用优化。
    需要注意的一点是,SHOW TABLE STATUS命令可以查看表的很多信息,但InnoDB引擎表的统计信息可能不是很准确,尤其是在表特别大的时候。
    数据表的大小可根据information_schema.tables表中Data_length和Index_length列的和大致统计。
    当我们使用共享表空间的时候,有时希望能够合理分配每个数据文件的大小,还可能需要知道数据文件的空闲空间还有多少。
    这时,可以启动Tablespace Monitor,通过日志输出收集表空间的信息,通过计算使用的块和空闲的块来判断表空间的空闲空间还有多少,以及数据增长的趋势。
    但此种方式不易操作,分析也较复杂,所以更合适的办法是简单查询MySQL自带的统计表,据此进行估算。
    在实际生产环境中,我们可以定期查询INFORMATION_SCHEMA信息数据库,把收集的数据库大小插入监控数据库,在收集的信息的基础上进行空间趋势分析。
    下面的查询将检查数据库argls下面的所有基础表的信息。
    SELECT TABLE_SCHEMA,TABLE_NAME,TABLE_TYPE,ENGINE,TABLE_ROWS ,DATA_LENGTH,INDEX_LENGTH,DATA_FREE
    FROM tables WHERE TABLE_SCHEMA='argls' AND TABLE_TYPE='BASE TABLE';
    下面的查询将统计数据库argls的大小。
    SELECT SUM(DATA_LENGTH),SUM(INDEX_LENGTH),SUM(DATA_FREE)
    FROM tables WHERE TABLE_SCHEMA='argls' AND TABLE_TYPE='BASE TABLE';
    关于DATA_FREE列,没有太大的参考意义此处不做讲解。

    11.2.3 MySQL需要关注的参数及状态变量
    以下的一些状态变量,是监控系统需要着重关注的,由于篇幅所限,这里并没有列出所有值得关注的状态变量。
    (1)open_files_limit
    操作系统允许mysqld打开的文件数量。这个值可以设置得比较大,比如50000,最好在系统初始化安装时就设置了一个较大的值。
    可修改文件/etc/security/limits.conf 来实现,命令如下。
    vi /etc/security/limits.conf
    * - nofile 50000
    (2)max_connect_errors
    此值应设置得比较大,如大于5000,以避免因为连接出错而超过出错阈值,导致MySQL阻止该主机连接。
    如被阻塞,则须手动执行flush-hosts进行复位。
    (3)max_connections
    允许并行的客户端连接数目。默认值100太小,一般会不够用。
    生产环境中建议设置为2000~5000。
    注意,对于32位的MySQL由于有内存限制,连接数不能过大(建议小于800),否则可能会由于连接过多,造成MySQL实例崩溃。
    (4)max_used_connections
    MySQL Server启动后曾经到达的最大连接数。
    如果该值达到max_connections,那么某个时刻存在突然的高峰连接时,可能会有性能问题。
    (5)threads_connected
    当前打开的连接数量。这个值不能超过设置的max_connections*80%。
    需要注意及时调整max_connections的值。一旦连接数超过了max_connections,就会出现客户端连接不上的错误。
    (6)aborted_connects
    试图连接到MySQL服务器而失败的连接数。
    正常情况下,该值不会持续增加,出现连接失败的原因主要有如下几点。
    客户端程序在退出之前未调用mysql_close()。
    客户端的空闲时间超过了wait_timeout或interactive_timeout秒,未向服务器发出任何请求。
    客户端在数据传输中途突然结束。
    (7)Aborted_clients
    由于客户端没有正确关闭连接导致客户端终止而中断的连接数。
    出现下述情况时,服务器将增加“Aborted_clients”(放弃客户端)的状态变量。
    客户端不具有连接至数据库的权限。
    客户端采用了不正确的密码。
    连接信息包含不正确的信息。
    获取连接信息包的时间超过了connect_timeout秒。
    我们可以使用如下的命令发现异常。 mysqladmin -uroot -p -S /path/to/tmp//3306/mysql.sock ext | grep Abort
    也可以使用tcpdump来判断是什么原因导致了异常。 tcpdump -s 1500 -w tcp.out port 3306 strings tcpdump.out
    (8)thread_cache_size
    服务器应缓存多少线程以便重新使用?当客户端断开连接时,如果线程少于thread_cache_size,则客户端的线程将被放入缓存。
    如果有新连接请求分配线程则可以从缓存中重新利用线程,只有当缓存空了时才会创建新线程。
    如果新连接很多,则可以增加该变量以提高性能。
    如果是大量并发的短连接,则可能会因为 thread_cache_size不够而导致性能问题。
    生产环境中一般将其设置为100~200。
    由于线程可以缓存,所以线程持有的内存不会被轻易释放。
    (9)Threads_created
    创建用来处理连接的线程数。应该监视Threads_created的增量,如果较多,则需要增加thread_cache_size的值。
    以上对thread_cache_size的设置在高并发的时候会很有效。
    高并发时大量并发短连接对CPU的冲击不容忽视。
    (10)threads_running
    指同时运行的线程数目。这个值一般不会大于逻辑CPU的个数,如果经常有过多的线程同时运行,那么可能就意味着有性能问题。
    这个指标很重要,往往表明了一个系统的繁忙程度,它在系统爆发性能问题之前,会有一个上升的趋势,此时收集的性能信息,将有助于我们诊断复杂的性能问题。
    (11)slow_launch_threads
    如果这个值比较大,则意味着创建线程太慢了,可能是系统出现了性能问题,存在资源瓶颈,从而导致操作系统没有安排足够的CPU时间给新创建的线程。
    (12)query_cache_size
    为缓存查询结果分配的内存大小。一般设置为256MB。注意不要设置得太大。
    可监控查询缓存命中率:Qcache_hits/(Qcache_hits+Com_select)。
    更改这个值,会清空所有的缓存结果集,对于非常繁忙的系统,可能会很耗时,导致服务停顿,因为MySQL在删除所有的缓存查询时是逐个进行的。
    (13)Qcache_lowmem_prunes
    该变量记录了由于查询缓存出现内存不足,而需要从缓存中删除的查询数量,可通过监控Qcache_lowmem_prunes的增量,来衡量是否需要增大query_cache_size。
    Qcache_lowmem_prunes状态变量提供的信息能够帮助你调整查询缓存的大小。
    它可计算为了缓存新的查询而从查询缓存区中移出到自由内存中的查询数目。
    查询缓存区使用最近最少使用(LRU)策略来确定哪些查询需要从缓存区中移出。
    (14)InnoDB_buffer_pool_wait_free
    一般情况下,是通过后台向InnoDB缓冲池中写入数据的。
    但是,如果需要读或创建页,并且没有干净的页可用,那么它还需要先等待页面清空。
    如果已经适当设置了缓冲池的大小,那么该值应该会很小。
    (15)Slow_queries
    查询时间超过long_query_time秒的查询个数。
    应该监控此变量的增量变化,一般1秒内不要超过5~10个,否则可能是有性能问题。
    (16)Select_full_join
    没有使用索引的连接数量。如果该值较大,则应该仔细检查一下表的索引。
    (17)Created_tmp_tables
    创建内存临时表的数量,如果Created_tmp_disk_tables比较大,则应该考虑增加tmp_table_size的大小。
    注意:应该将tmp_table_size和max_heap_table_size简单调整到大小一样。32MB一般足够了。
    对这两个参数的控制通常基于内存引擎的临时表可以增长的阈值,若超过了这个阈值,就会转化成On-disk MyISAM表。
    (18)Created_tmp_disk_tables
    服务器执行语句时在硬盘上自动创建的临时表的数量。
    (19)Bytes_received和Bytes_sent
    可以用来监控MySQL的流量。
    (20)key_buffer_size
    MyISAM索引缓冲,实际用到多少就分配多少。不一定需要分配很大的空间,可参考实际观察到的值,不要大于实际值。
    如下命令可用于评估索引空间的大小。
    SELECT SUM(INDEX_LENGTH) FROM INFORMATION_SCHEMA.TABLES WHERE ENGINE='MYISAM';
    或者使用操作系统下的命令du进行统计。 $ du -sch `find /path/to/mysql/data/directory/ -name "*.MYI"`
    如下公式将计算访问Key的命中率:100-((Key_blocks_unused*key_cache_block_size)*100/key_buffer_size),
    但是,该值没有什么实际意义,相对而言,key_reads更有实际意义,因此更值得关注,如下:
    $ mysqladmin extended-status -r -i 10 | grep Key_reads
    不要把key_buffer_size设置为0,至少也应设置为一个较小的值,比如32MB或64MB,因为MySQL的一些内部操作需要用到MyISAM引擎,如临时表。
    (21)Open_tables 当前打开的表的数量。
    (22)Opened_tables 已经打开的表的数量。
    查看Open_tables及Opened_tables的增量时,如果Opened_tables的增量比较大,那么可能table_open_cache(或者table_cache)不够用了。
    如果Open_tables对比 table_cache_size并不大,但Opened_tables还在持续增长,那么也可能是显式临时表被不断打开而导致的。
    (23)table_open_cache(table_cache 5.1.3之前的参数名)
    默认的设置太小了,生产环境中应该将其设置得足够大,数千到一万是比较合理的值。
    检查Opened_tables status变量,如果该值比较大,而我们不经常运行FLUSH TABLES命令,那么应该增加table_open_cache的变量值。
    (24)table_definition_cache
    一般可以将其设置为足够高的值来缓存表定义,比如4096,这并不会耗费什么资源。默认的256太小了。
    其他一些反应数据库访问请求、读写数据量的状态变量,这里将不再赘述。

    11.3 数据库监控的实现
    11.3.1 Nagios
    使用Nagios对数据库进行监控的思路与之前讲述的心跳表大同小异,这里将简要介绍下其实现思路。
    1)监控主库的可用性。
    可以创建一个监控表监控主库的可用性,监控表有时间戳字段。
    Nagios每分钟更新监控表的数据,以测试主库可用性,如果连续多次失败,就判断为失败,并发送短信邮件报警。
    2)监控复制。
    定期检测,比如每隔5分钟,就读取从库的监控表最近的时间记录,判断滞后多少秒。
    监控账号可以限制资源使用,示例如下。
    GRANT SELECT,UPDATE ON db_name.* TO monitor_user@'10.%' IDENTIFIED BY 'xxxxxxxxxxxxX' WITH MAX_CONNECTIONS_PER_HOUR 360 MAX_USER_CONNECTIONS 3 MAX_QUERIES_PER_HOUR 720 MAX_UPDATES_PER_HOUR 360 ;

    11.3.2 swatch
    可以使用swatch监控服务器日志和数据库日志,它可以实时监控日志,从而节省很多编写监控脚本的时间。
    下面简单介绍下swatch的安装和使用方式。
    swatch需要用到Perl 5.10,如果你的系统是Perl5.8,那么建议将其升级到5.10。
    Perl升级到5.10的步骤如下。
    perl -v
    cd
    mkdir pkgs
    cd pkgs
    wget http://www.cpan.org/src/perl-5.10.1.tar.gz
    tar zxvf perl-5.10.1.tar.gz
    cd perl-5.10.1
    ./Configure -des
    make
    make install c
    d /usr/bin;mv perl perl.bak;ln -s /usr/local/bin/perl
    通过以下命令安装swatch所需要的模块。
    cpan> install Date::Calc Date::Format Date::Manip File::Tail
    swatch的安装方法如下。 下载swatch-3.2.3.tar.gz到本机。
    tar zxvf swatch-3.2.3.tar.gz
    cd swatch-3.2.3
    perl Makefile.PL
    make
    make install
    cpan > install Proc::ProcessTable
    这个步骤如果没有安装成功,则maketest会失败,也可以手动执行编译安装,示例如下。
    cd /root/.cpan/build/Proc-ProcessTable-0.45-OJ6Aeg
    perl Makefile.PL
    make
    make install
    我们可以让swatch随系统启动,或者在root下添加守护,如下。
    crontab -l
    ## monitor log files using Swatch
    */2 * * * * /root/crontab/sw.sh > /dev/null 2>&1
    cat sw.sh
    #!/bin/bash
    #source /root/.bash_profile
    #For monitoring log files ,looking for trouble.
    #20090311
    source $HOME/.bash_profile
    export PATH=/usr/local/bin:$PATH
    host_name=`hostname`
    exist_count=`ps -ef |grep "swatch" |grep -v grep |wc -l`
    echo "$exist_count"
    if [ "$exist_count" -eq 0 ]; then
    echo "starting swatch"
    #/usr/bin/swatch --config-file=/etc/swatch.conf --tail-file=/var/log/messages &
    swatch --config-file=/etc/swatch.conf
    --tail-prog=/usr/bin/tail
    --tail-args '--follow=name --lines=1'
    --tail-file="/var/log/messages /usr/local/mysql/data/`hostname`.err"
    --daemon
    echo "started swatch"
    fi
    swatch并不知道如何处理异常日志,所有的规则都是在日常维护工作中不断积累下来的,以下提供的是笔者曾经使用过的一份配置文件。
    swatch.conf
    ######################### A simple example Start ########################
    # watchfor /authentication failure|other message you want to be alerted/ #可使用正则表达式捕捉日志内的警告错误信息
    # threshold track_by="foo",type=limit,count=2,seconds=300 #若在 300s之内捕捉到了信息,则执行动作 ,但最多只能执行 2次,会忽略 300s内的相同信息
    # threshold track_by="foo",type=threshold,count=2,seconds=300 #若在 300s之内捕捉到 2次信息,则执行动作 ,然后重新计时
    # mail addresses=username1@ooea.com:abcd@ooea.com,subject="SSH: Invalid User ",when=1-6:8-17 #执行动作,发送邮件 ,可使用 when选项指定某个时间段才可执行动作, when=day_of_week:hour_of_day.
    # exec "command" #执行动作,执行命令, The command may contain variables which are substituted with fields from the matched line.
    # perlcode [depth] arbitrary_Perl_code :可嵌入 perl代码。
    ######################### A simple example End ###########################
    # This is Swatch configureration file. Usage: swatch -c=/etc/swatch.conf -t=/var/log/messages
    # Added by garychen on 20070507
    perlcode my $hostname=`hostname`;
    watchfor /kernel BUG/
    threshold type=limit,count=1,seconds=300
    mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname Error "
    ###exec ""
    watchfor /ERROR/
    threshold type=limit,count=1,seconds=300
    mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname Error "
    ###exec ""
    watchfor /InnoDB: Warning/
    threshold type=limit,count=1,seconds=300
    mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname MySQL Error "
    ###exec ""
    watchfor /ORA-/
    threshold type=limit,count=1,seconds=300
    mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname Oracle Error "
    ###exec ""
    #watchfor /EXT3-fs error/
    watchfor /error/
    threshold type=limit,count=1,seconds=300
    mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname system Error "
    ###exec ""
    watchfor /Can't connect to localhost/
    threshold type=limit,count=1,seconds=900
    mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname Memcached Error "
    #exec ""
    #watchfor /(.*PHP Warning.*)/
    # threshold type=threshold,count=10,seconds=900
    #mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname php Error "
    # exec "echo $1 >> /root/crontab/log/error_swatch.log"
    watchfor /[alert]/
    threshold type=limit,count=1,seconds=300
    mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname Nginx Alert"
    #exec ""
    #watchfor /(.*[error].*)/
    # threshold type=threshold,count=10,seconds=900
    #mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname Nginx Error"
    # exec "echo $1 >> /root/crontab/log/error_swatch.log"
    watchfor /ip_conntrack: table full/
    threshold type=limit,count=1,seconds=60
    mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname System Error”
    #exec "echo $1 >> /root/crontab/log/error_swatch.log"
    #exec "/root/crontab/modify_sysctl.sh"
    watchfor /ALERT/
    threshold type=limit,count=1,seconds=300
    mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname System Error"
    #
    watchfor /worker process d* exited on signal 9/
    threshold type=limit,count=1,seconds=60
    mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname Nginx Error
    " #exec ""
    #
    watchfor /messages suppressed/
    threshold type=limit,count=1,seconds=300
    mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname Nginx Error "
    #exec ""
    watchfor /mysql_error()/
    threshold type=limit,count=1,seconds=300
    mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname MySQL Error "
    #exec ""
    watchfor /Failed reading log event/
    threshold type=limit,count=1,seconds=300
    mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname MySQL Error "
    #exec "
    watchfor /segfault at/
    threshold type=limit,count=1,seconds=300
    mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname TT Error "
    #exec ""
    watchfor /Out of memory/
    threshold type=limit,count=1,seconds=300
    mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname System Error " #
    exec ""
    watchfor /detected inconsistency/
    threshold type=limit,count=1,seconds=300
    mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname System Error "
    #exec ""
    watchfor /response failed/
    threshold type=limit,count=1,seconds=300
    mail addresses=username1@ooea.com:username2@ooea.com,subject="$hostname System Error "
    #exec ""

    11.3.3 Cacti
    Cacti等其他开源监控工具一般都提供了MySQL插件,可以通过添加插件方便地对MySQL进行监控和性能信息收集。
    Cacti的插件可以参考如下链接: http://www.percona.com/software/percona-monitoring-plugins
    下面对Cacti输出的一些图形做一些简单的说明。
    图11-1 InnoDB事务计数图。
    一个活动的事务是指这个事务当前的状态是打开的,还没有关闭,也就是说,在BEGIN...COMMIT之间;
    一个正在运行的查询也是一个活动的事务(MySQL默认配置为事务自动提交,所以每个查询都被当作一个单独的事务);
    一个锁定的事务是指处于LOCK WAIT状态的事务,通常是在等待一个行锁,但也可能是在等待表锁。
    图11-2 InnoDB缓冲池页变动信息
    图11-2给出了InnoDB缓冲池中页面的创建、读取和写入的频率,可以作为InnoDB吞吐率的一个指标。如果发现图中有突变,那么应该警惕。
    图11-3 InnoDB缓冲池中内存的使用情况图
    图11-3显示了InnoDB缓冲池的一些基本信息,各项的含义如下。
    Pool Size:InnoDB Buffer Pool的大小。
    Database Pages:已经使用的页。
    Free Pages:自由空间。
    Modified Pages:脏数据所占用的空间。
    图11-4所示的是InnoDBCheckpoint Age信息情况。
    图11-4所展示的InnoDBCheckpoint Age,等同于还没有应用检查点操作的数据字节数,如果这个时刻实例发生崩溃,那么恢复时就需要应用图中所示的这么多日志量。
    如果这个值接近日志文件的合计大小,那么可能你还需要增大日志文件。
    图11-5是InnoDBI/O信息图。
    图11-5展示了InnoDB I/O的统计情况,包括文件读写、日志写和Fsync()调用。
    Fsync是一项成本昂贵的操作,如果参数innodb_flush_log_at_trx_commi的值设置为1, 那么在图11-5中可能会看到很高的File Fsyncs值。
    图11-6是InnoDB I/O挂起信息图。
    图11-6中应该没有挂起的I/O操作或挂起操作的值很小。如果在图中看到大量的Pending操作,那么我们可能需要更大的缓冲池,或者更快的存储。
    图11-7是InnoDB查询修改记录的操作图。
    图11-7中显示了InnoDB每秒执行SELECT、INSERT、UPDATE、DELETE操作的行数。从图中可以看到凌晨0点有一个高峰。
    图11-8是InnoDB事务图。
    图11-8中的参数及其说明如下。
    InnoDBTransactions:创建的事务。
    Current Transactions:当前事务,不管处于何种状态,包括active、lock wait、notstarted等状态。
    History List:未被清理的事务的列表长度,表征了最旧的事务,这些事务的存在可能是因为清理的速度跟不上事务的创建频率,也可能是因为有长时间运行的查询事务,为了维护一致性读而不能清理旧的行记录版本。
    Read Views:多少事务有一致性快照。
    图11-9是InnoDB连接图。
    应关注图11-9中的Aborted Clients和Connections,Aborted Clients可能意味着连接超时退出或网络问题、账号验证错误、程序异常中断等情况的发生。
    图11-10是MySQL句柄计数器信息图。
    各种句柄的计数,图11-10中的Handeler Read Next有时会很大,表示可能有索引扫描。
    Handler Read Next指按照键顺序读取下一行的请求数。
    如果使用范围约束或执行索引扫描来查询索引列,那么该值会增加。
    图11-11是一个网络传输流量统计图。
    图11-12是MySQL连接状态图。
    图11-12所示的是MySQL连接各种状态的一个统计。
    在大部分情况下,你应该能看到很少的State Sending Data,对于图形突变,则需要谨慎探究是何种原因所导致的。
    图11-13是不同类型的SELECT查询图。
    图11-13显示的是不同类型的SELECT查询,一般情况下Select Full Join必须等于0,需要注意曲线的变化,如果有突变,则需要探明原因。
    图11-14是MySQL表锁信息图。
    对于InnoDB来说,一般不用关注MySQL表锁信息图,如果有较高的Table Locks Waited,那么可能是由MyISAM表引起的。
    图11-15是MySQL临时对象图。
    对于图11-15,需要关注下Created Tmp Disk Tables,该值等于0为佳,如果比较高,比如大于5,则可能有性能问题。

    11.3.4 如何打造一个强大的监控系统
    官方的企业版监视器,基本涵盖了监控的各种要素,我们可以仿照它开发自己的监控系统。
    我们的监控系统可能需要满足如下需求。
    (1)能实现实时查看和历史查看
    我们需要满足实时查看性能数据及其可用性的需求,历史数据也要能够存储下来,保留一定的周期,以便在需要对其进行分析的时候,随时能够查询。
    (2)采用分布式的架构
    仅仅在远程通过命令行对MySQL的状态进行监控可能还不够,可以看到开源工具的一些插件,选择了集中式的监控方式,即从一台监控机器上探测所有被监控的主机,
    这种方式有一些弊端,事实上,主动和被动方式都采用会更灵活、更强大,也能监控到更多的信息。
    推荐的方式是部署一个分布式的Web应用程序,它由 一个集中式的服务管理端和在每台被监控的MySQL服务器上安装的一个轻量级服务代理端组成。
    (3)可提供预警及建议功能
    监控系统应能持续监控性能和可用性,在性能趋势偏离基准水平时发出报警,同时还能提供建议的配置和参数设定以改善性能。
    (4)丰富的图表
    我们需要能够方便地查看所有的MySQL服务器,能够批量进行配置管理,可直观地查看一台服务器、自定义的组或所有服务器。
    一组丰富的实时图形和历史图形可帮助我们深入了解详细的服务器统计信息,可帮助我们全面深入地了解数据库性能、可用性、关键活动等信息。
    (5)可视化查询分析
    可监视实时查询性能,查看执行统计信息,筛选和定位导致性能下降的SQL代码。
    MySQL在5.6版之后加强了Performance Schema的功能,所以我们可结合使用 Performance Schema和MySQL Server 5.6直接从MySQL服务器收集数据,而无需额外的软件或配置。
    (6)发现并修复占用大量资源的查询
    开发人员和DBA可通过相关的图形针对当时执行的查询比较执行参数,如服务器负载、线程统计信息或内存使用情况等。
    只需选中图形上的一个时间片就能找到最占用资源的查询,并找到可能导致更大性能问题的根源。
    (7)InnoDB监视
    监视影响MySQL性能的主要InnoDB指标。
    接收有关索引使用效率低下、锁定问题及InnoDB缓冲池使用情况等的警报,获取根据当前性能和趋势分析改进InnoDB 配置的提示和技巧。
    (8)复制监控
    可配合心跳表实现复制监控,在生产运维中,推荐采用人工输入主从复制拓扑架构信息的方式,并结合监控自动发现进行监控。
    因为如果只是自动发现,而我们对复制架构进行了误操作,那么我们自己就不能发现。
    我们需要了解所有MySQL主服务器和从服务器的性能、可用性和运行状况。
    特别是对于读写分离的架构, 从库的可用性和复制延时,也显得尤其重要。
    (9)磁盘监视
    趋势分析和预测可以帮助管理员预测未来的容量需求。
    可以根据用户定义的阈值(例如“如果磁盘空间12个月后将用尽请通知我”)向操作人员发出预警。
    (10)操作系统监视
    直观地实时监视操作系统级别的性能指标,包括平均负载、CPU使用情况、内存使用情况、Swap使用情况、文件系统使用情况及磁盘I/O等。
    按照需求分析,我们可以确定自己应该收集哪些信息,该如何设计库表结构,并验证表结构是否能够满足我们的业务需求。
    如果有大量的数据库服务器需要监控,那么还需要处理好实时收集数据和历史数据查询之间的资源争用问题,归档表、按时间分表、分区表都是可以考虑使用的技术。
    一旦我们实现了数据收集,就可以在这些标准化的数据之上进行预警、分析、统计等功能的开发,
    在后面更高级的阶段,还可以提供更丰富的咨询和建议功能,使监控平台变得更加智能。

    11.4 数据库监控的可视化
    现实工作中,我们不会经常去查看图形,特别是在有了很多图形的时候,更多的情况下,我们会接到报警,这个时候,才会去看图形,
    从图形上看到负载情况、资源使用情况有了变化,然后再去确定当时发生了什么?有没有做什么变更,从而快速定位问题的所在。
    我们在监控数据、性能数据时往往有数据可视化的需求。
    可通过图形数据看到某种周期性的变动,比如每小时的波动,可能是有某些定时任务;每天的波动, 可能和用户集中在某些时间段上网有关;
    每周的波动,可能是工作日访问请求大,非工作日访问请求少;季度的波动,可能是每个季度要生成一些报表。
    监控展示数据所使用的图形一般是二维形式的,有折线图、散点图、热图、条形图及饼图等,三维图形在一些领域也有应用。
    以下将介绍几种常见的图形。
    11.4.1 折线图
    折线图(linechart)是用线段将各数据点连接起来的图形,它以折线的方式显示数据的变化趋势。
    折线图的特点是可以反映事物在一段时间内的趋势,它可以显示随时间(根据常用比例设置)而变化的连续数据,因此非常适合显示在相等时间间隔下数据变化的趋势。
    另外,在折线图中,数据是递增还是递减、增减的速率、增减的规律(周期性、螺旋性等)、峰值等特征都可以清晰地反映出来。
    所以,折线图常用来分析数据随时间的变化趋势,也可用来分析多组数据随时间变化的相互作用和相互影响。
    折线图是生产环境监控系统中最常使用的图形,图11-16所示的就是一个折线图的案例。
    从图11-16中,我们可以看到,在22:06分,查询达到峰值,而在凌晨时间段,查询量就很小。

    11.4.2 散点图
    散点图又名散布图(scatter plot)。它是表示两个变量之间关系的图,又称相关图,是以一个变量为横坐标,另一变量为纵坐标,利用散点(坐标点)的分布形态反映变量统计关系的一种图形。
    散布图用于分析两测定值之间的相关关系,它的优点是能通过直观醒目的图形方式反映变量间关系,以便决定用何种数学表达方式来模拟变量之间的关系。
    散点图不仅可传递变量间关系类型的信息,也能反映变量间关系的明确程度。
    通过作散点图对数据的相关性进行直观地观察,不但可以得到定性的结论,而且还可以剔除异常数据,从而提高用计算法估算的准确性。
    图11-17所示的就是一个散点图。
    图11-17是一个Web访问日志的散点图,可以看到在下午15点左右有一个响应时间变差的时间窗口。
    一般情况下,页面的访问响应可分为两类,一类是页面本身的响应就比较慢,它会一直表现得很慢,而另一类是页面在高峰时间段才会响应变慢,这往往意味着系统碰到了某些资源瓶颈。
    散点图存在两个不足之外,一是如果点非常密集,那么点会重叠,相互之间很难区分;二是我们可能需要收集、存储和处理大量的数据。
    对于大量数据,绘图的成本会较高,对比可以采用对日志进行取样的方式,仅针对取样点数据绘图,绘制的图仍然能够反映实际的响应时间分布,
    比如,如下的awk脚本,就可以采样1/3的数据。 cat access.log | awk '// { n++; if ((n % 3) == 0) { print $0 } }' > access.log_sampled.txt
    或者采用其他扩展性更好的绘图方式,比如热图。

    11.4.3 热图
    热图(heat map)的原理是把坐标点分组,每个分组的区域都称为bucket,它的颜色取决于在这个bucket里元素的数量,
    通过颜色变化的方式来表示坐标点的密集程度变化,解决了散点图坐标点过于密集所带来的问题,我们可用热图来分析Web服务器响应、磁盘访问延迟等指标。
    图11-18所示的就是一个磁盘I/O响应时间的热图。
    可以看到大部分的响应时间都在2000us以下,响应时间主要集中在两个区间,即深红色的两条颜色带。
    通过使用热图,我们可以描绘大量数据,并且使用渐变的 色带来直观地展示数据的疏密程度或频率高低。
    现实中,热图应用在很多领域中,比如记录用户在Web页面内鼠标的点击位置,记录足球运动员的跑位情况等。
    热图的不足之处是,它并不像折线图、条形图、饼图这样知名,很多人不知道有这种表示数据的方法,用户还需要了解其是如何展示数据的。
    其他图形就不一一举例了。读者如果有兴趣,可阅读数据可视化的相关书籍。

    小结:
    本章讲述了MySQL应该监控记录哪些信息及如何记录。
    我们需要具备一个意识,即应该持续监控一切事情,包括网络、系统、服务、用户行为等,基于翔实的数据,我们才能持续优化架构、辅助决策。
    强大的监控系统不仅仅是为DBA服务的,管理好自己的数据,友好地展现给研发、测试、运营、架构等各个团队,也为他们提供服务才是监控的意义所在。

  • 相关阅读:
    springboot(十二)-分布式锁(redis)
    springboot(十一)-为什么要用springboot
    HashMap和HashTable区别
    ArrayList和LinkedList区别
    springcloud(三)-Eureka
    springboot(十)-监控应用
    springboot(九)-log配置
    springcloud(二)-最简单的实战
    rest版的webservice
    sqlite与android交互 (封装)
  • 原文地址:https://www.cnblogs.com/BradMiller/p/12036129.html
Copyright © 2011-2022 走看看