7 mysql_ - Munin plugin to display misc MySQL server status
 
   9 =head1 APPLICABLE SYSTEMS
 
  11 Any MySQL platform, tested by the authors on:
 
  13 * MySQL 5.5.32, 5.5.37
 
  17 * MariaDB-5.5.39(galera).
 
  20 * MariaDB-10 Query Response Time: https://mariadb.com/kb/en/mariadb/query_response_time-plugin/
 
  22 Information Schema tables:
 
  23 * User statistics - MariaDB-5.2+, OurDelta, Percona Server - https://mariadb.com/kb/en/mariadb/user-statistics
 
  27 This script is used to generate data for several graphs. To generate
 
  28 data for one specific graph, you need to create a symbolic link with a
 
  29 name like mysql_<GRAPH> to this script.
 
  31 If you need to run against multiple MySQL instances on the same host,
 
  32 create your symlinks with names like mysql<N>_<GRAPH> where N is any
 
  33 non-negative integer. You must also set the env.cachenamespace variable
 
  34 to a unique value for each group of symlinks.
 
  36 To get a list of symlinks that can be created, run:
 
  40 In addition you might need to specify connection parameters in the
 
  41 plugin configuration to override the defaults. These are the defaults:
 
  44     env.mysqlconnection DBI:mysql:information_schema
 
  50     env.mysqlconnection DBI:mysql:information_schema;host=127.0.0.1;port=3306
 
  52     env.mysqlpassword geheim
 
  53     env.cachenamespace munin_mysql_pri
 
  55     env.mysqlconnection DBI:mysql:information_schema;host=127.0.0.1;port=13306
 
  57     env.mysqlpassword ryuWyawEv
 
  58     env.cachenamespace munin_mysql_alt
 
  62     env.mysqlconnection DBI:mysql:information_schema;mysql_read_default_file=/etc/munin/.my-10.cnf
 
  63     env.cachenamespace munin_mysql_10
 
  64     # here the [client] section of /etc/munin/.my-10.cnf is read. socket= can
 
  67 Creating a munin user:
 
  69   CREATE USER 'munin'@'localhost' IDENTIFIED BY 'ryuWyawEv';
 
  71 or with a unix_socket plugin (INSTALL PLUGIN unix_socket SONAME 'auth_socket')
 
  73   CREATE USER 'munin'@'localhost' IDENTIFIED WITH unix_socket;
 
  75 Note: requires 'user munin' in the configuration.
 
  77 The minimum required priviledges of the munin database user is:
 
  79   GRANT PROCESS, REPLICATION CLIENT ON *.* TO 'munin'@'localhost';
 
  82 Warning and critical values can be set via the environment in the usual way.
 
  86     env.slave_io_running_warning 0.5
 
  87     env.slave_sql_running_warning 0.5
 
  88     env.seconds_behind_master_warning 300
 
  89     env.seconds_behind_master_critical 600
 
  97 The plugin uses shared memory to cache the statistics gathered from
 
  98 MySQL. This ensures minimal inpact on the MySQL server.
 
 104 =head1 INTERPRETATION
 
 108 The statistics from innodb are mainly collected from the command
 
 110   SHOW ENGINE INNODB STATUS
 
 112 A nice walk through is found at
 
 113 L<http://www.mysqlperformanceblog.com/2006/07/17/show-innodb-status-walk-through/>
 
 116 L<http://blog.jcole.us/2014/04/16/the-basics-of-the-innodb-undo-logging-and-history-system/>
 
 120 FIX point to relevant sections in the MySQL manual and other www
 
 121 resources for each graph
 
 125 =item mysql_replication
 
 127 slave_io_running and slave_sql_running both translate the "Yes" values to 0 and
 
 128 anything else to 1 for their respective fields in the "SHOW SLAVE STATUS" output.
 
 129 This can be used to warn on slave failure if the warning and critical values
 
 130 are set as seen in a previous section.
 
 132 =item wsrep_cluster_status
 
 134 "Primary" is translated 0 and "non-Primary" to 1.
 
 140 Copyright (C) 2008,2009 Kjell-Magne Ã˜ierud, 2014 Open Query
 
 142 This program is free software; you can redistribute it and/or modify
 
 143 it under the terms of the GNU General Public License as published by
 
 144 the Free Software Foundation; version 2 dated June, 1991.
 
 146 This program is distributed in the hope that it will be useful, but
 
 147 WITHOUT ANY WARRANTY; without even the implied warranty of
 
 148 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
 149 General Public License for more details.
 
 151 You should have received a copy of the GNU General Public License along
 
 152 with this program; if not, write to the Free Software Foundation, Inc.,
 
 153 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 157 git-master + a few munin modifications
 
 159 This plugin was downloaded from L<http://github.com/kjellm/munin-mysql/>
 
 161 =head1 MAGICK MARKERS
 
 164   #%# capabilities=suggest autoconf
 
 174 use Math::BigInt; # Used to append "=> lib 'GMP'" here, but GMP caused
 
 175                   # segfault on some occasions. Removed as I don't
 
 176                   # think the tiny performance boost is worth the
 
 178 use Storable qw(nfreeze thaw);
 
 185     eval 'require Cache::SharedMemoryCache';
 
 186     $has_cache = $@ ? 0 : 1;
 
 190 #---------------------------------------------------------------------
 
 192 #---------------------------------------------------------------------
 
 195     'dsn'        => $ENV{'mysqlconnection'} || 'DBI:mysql:information_schema',
 
 196     'user'       => $ENV{'mysqluser'}       || 'root',
 
 197     'password'   => $ENV{'mysqlpassword'}   || '',
 
 198     'cache_namespace' => $ENV{'cachenamespace'} || 'munin_mysql',
 
 202 #---------------------------------------------------------------------
 
 204 #---------------------------------------------------------------------
 
 206 my %cache_options = (
 
 207     'namespace'          => $config{cache_namespace},
 
 208     'default_expires_in' => 60,
 
 211 my $shared_memory_cache ;
 
 214   $shared_memory_cache = Cache::SharedMemoryCache->new(\%cache_options)
 
 215     or die("Couldn't instantiate SharedMemoryCache");
 
 218 #---------------------------------------------------------------------
 
 219 #  G R A P H   D E F I N I T I O N S
 
 220 #---------------------------------------------------------------------
 
 222 # These are defaults to save typing in the graph definitions
 
 225         args   => '--base 1000',
 
 227     data_source_attrs => {
 
 234 # %graphs contains the graph definitions, it is indexed on the graph
 
 235 # name. The information stored for each graph is used for both showing
 
 236 # data source values and for printing the graph configuration. Each
 
 237 # graph follows the followingformat:
 
 241 #         # The global attributes for this graph
 
 243 #         # Attributes common to all data sources in this graph
 
 244 #         data_source_attrs => {}
 
 247 #         # NAME - The name of the data source (e.g. variable names
 
 248 #         #        from SHOW STATUS)
 
 249 #         # DATA_SOURCE_ATTRS - key-value pairs with data source
 
 251 #         {name => 'NAME', (DATA_SOURCE_ATTRS)},
 
 256 #---------------------------------------------------------------------
 
 259 $graphs{wsrep_cluster_status} = {
 
 262                 title  => 'Galera Status',
 
 265             data_source_attrs => {
 
 272             {name => 'wsrep_cluster_status', label => '0-Primary, 1-Non-Primary',
 
 273                                            info  => 'If the host is primary',
 
 279 $graphs{wsrep_cluster_size} = {
 
 282                 title  => 'Galera cluster size',
 
 285             data_source_attrs => {
 
 290             {name => 'wsrep_cluster_size', label => 'Cluster size',
 
 291                                            info  => 'The number of hosts in the cluster.',
 
 297 # http://www.codership.com/wiki/doku.php?id=galera_node_fsm
 
 298 $graphs{wsrep_local_state} = {
 
 301                 title  => 'Galera node state',
 
 302                 vlabel => 'State (galera_node_fsm)',
 
 303                 args   => '--lower-limit 0 --upper-limit 6',
 
 305             data_source_attrs => {
 
 312             {name => 'wsrep_local_state', label => '1-Joining, 2-Donor, 3-Joined, 4-Synced, 5-Donor, 6-Join after Donor ',
 
 313                                           info  => 'The state of the node in the cluster.',
 
 315                                           warning => '3.5:4.5'},
 
 319 #---------------------------------------------------------------------
 
 321 $graphs{wsrep_transactions} = {
 
 324                 title  => 'Galera transactions',
 
 326             data_source_attrs => {
 
 331             {name => 'wsrep_last_committed', label => 'Committed transactions',
 
 332                                              info  => '# of committed transactions.',
 
 335             {name => 'wsrep_local_commits',  label => 'Locally Committed transactions',
 
 336                                              info  => '# of locally committed transactions.',
 
 342 #---------------------------------------------------------------------
 
 344 $graphs{wsrep_writesets} = {
 
 347                 title  => 'Galera writesets',
 
 349             data_source_attrs => {
 
 354             {name => 'wsrep_replicated', label => 'Writesets sent',
 
 355                                          info  => '# of writesets sent to other nodes',
 
 359             {name => 'wsrep_received',   label => 'Writesets received',
 
 360                                          info  => '# of writesets received from other nodes',
 
 363                                          negative => 'wsrep_replicated'},
 
 367 #-------------------------
 
 370 $graphs{wsrep_writesetbytes} = {
 
 373                 title  => 'Galera writesets bytes/sec',
 
 375             data_source_attrs => {
 
 379             {name => 'wsrep_received_bytes',   label => 'Writesets bytes received',
 
 380                                                info  => '# of bytes in writesets received from other nodes',
 
 384             {name => 'wsrep_replicated_bytes', label => 'Writesets bytes sent',
 
 385                                                info  => '# of bytes in writesets sent to other nodes',
 
 389                                                negative => 'wsrep_received_bytes'},
 
 390             {name => 'wsrep_repl_keys_bytes', label => 'Writeset key size sent',
 
 391                                                info  => '# of bytes in writesets of keys sent to other nodes',
 
 394             {name => 'wsrep_repl_data_bytes', label => 'Writeset data size sent',
 
 395                                                info  => '# of bytes in writesets of data sent to other nodes',
 
 398             {name => 'wsrep_repl_other_bytes', label => 'Writeset other size sent',
 
 399                                                info  => '# of bytes in writesets of other data sent to other nodes',
 
 406 #-------------------------
 
 408 $graphs{wsrep_errors} = {
 
 411                 title  => 'Galera transaction problems'
 
 413             data_source_attrs => {
 
 418             {name => 'wsrep_local_cert_failures', label => 'Certification failures',
 
 421             {name => 'wsrep_local_bf_aborts',     label => 'Aborted local transactions',
 
 424             {name => 'wsrep_local_replays',       label => 'Replays',
 
 430 #-------------------------
 
 432 $graphs{wsrep_queue} = {
 
 435                 title  => 'Galera queues',
 
 436                       vlabel => 'queue length received (-) / sent (+) per ${graph_period}',
 
 438             data_source_attrs => {
 
 443             {name => 'wsrep_local_recv_queue',     label => 'Receive queue length',
 
 446             {name => 'wsrep_local_recv_queue_min', label => 'Receive queue length min',
 
 449             {name => 'wsrep_local_recv_queue_avg', label => 'Average receive queue length',
 
 452             {name => 'wsrep_local_recv_queue_max', label => 'Receive queue length max',
 
 455             {name => 'wsrep_local_send_queue',     label => 'Send queue length',
 
 457                                                    negative => 'wsrep_local_recv_queue'},
 
 458             {name => 'wsrep_local_send_queue_min', label => 'Send queue length min',
 
 460                                                    negative => 'wsrep_local_recv_queue_min'},
 
 461             {name => 'wsrep_local_send_queue_avg', label => 'Average send queue length',
 
 463                                                    negative => 'wsrep_local_recv_queue_avg'},
 
 464             {name => 'wsrep_local_send_queue_max', label => 'Send queue length max',
 
 466                                                    negative => 'wsrep_local_recv_queue_max'},
 
 470 #-------------------------
 
 472 $graphs{wsrep_concurrency} = {
 
 475                 title  => 'Galera Performance - Apply to Commit',
 
 476                 vlabel => 'commit + / apply - '
 
 478             data_source_attrs => {
 
 484             {name => 'wsrep_apply_window',         label => 'apply window',
 
 486             {name => 'wsrep_apply_oooe',           label => 'apply out of order',
 
 488             {name => 'wsrep_apply_oool',           label => 'apply out of order (slowness)',
 
 490             {name => 'wsrep_commit_window',        label => 'commit window',
 
 491                                                    negative => 'wsrep_apply_window'},
 
 492             {name => 'wsrep_commit_oooe',          label => 'commit out of order',
 
 493                                                    negative => 'wsrep_apply_oooe'},
 
 494             {name => 'wsrep_commit_oool',          label => 'commit out of order (slowness)',
 
 495                                                    negative => 'wsrep_apply_oool'},
 
 499 #-------------------------
 
 501 $graphs{wsrep_flow} = {
 
 504                 title  => 'Galera flow control',
 
 505                       vlabel => 'events received (-) / sent (+) per ${graph_period}',
 
 507             data_source_attrs => {
 
 512             {name => 'wsrep_flow_control_recv',      label => 'Pause events received',
 
 516             {name => 'wsrep_flow_control_sent',      label => 'Pause events sent',
 
 519                                                      negative => 'wsrep_flow_control_recv'},
 
 525 #-------------------------
 
 527 $graphs{wsrep_flow_paused} = {
 
 530                 title  => 'Galera flow control paused ratio',
 
 532             data_source_attrs => {
 
 537             {name => 'wsrep_flow_control_paused',    label => 'Ratio flow control was paused',
 
 546 #-------------------------
 
 548 $graphs{wsrep_flow_paused_ns} = {
 
 551                 title  => 'Galera flow control paused time',
 
 552                 vlabel => 'nanoseconds',
 
 554             data_source_attrs => {
 
 559             {name => 'wsrep_flow_control_paused_ns',    label => 'Time flow control was paused (ns)',
 
 562                                                      critical => 1000000},
 
 566 #-------------------------
 
 568 $graphs{wsrep_distance} = {
 
 571                 title  => 'Galera distance',
 
 573             data_source_attrs => {
 
 578             {name => 'wsrep_cert_deps_distance',    label => 'cert_deps_distance',
 
 580             {name => 'wsrep_cert_index_size',       label => 'wsrep_cert_index_size',
 
 582             {name => 'wsrep_slave_threads',         label => 'wsrep_slave_threads',
 
 584             {name => 'wsrep_commit_window',         label => 'commit_window',
 
 589 #-------------------------
 
 590 $graphs{bin_relay_log} = {
 
 593             title  => 'Binary/Relay Logs',
 
 594             vlabel => 'Log activity',
 
 596         data_source_attrs => {
 
 601         {name => 'Binlog_cache_disk_use', label => 'Binlog Cache Disk Use'},
 
 602         {name => 'Binlog_cache_use',      label => 'Binlog Cache Use'},
 
 603         {name => 'Binlog_stmt_cache_disk_use', label => 'Binlog Statement Cache Disk Use'},
 
 604         {name => 'Binlog_stmt_cache_use',      label => 'Binlog Statement Cache Use'},
 
 605         {name => 'ma_binlog_size',        label => 'Binary Log Space'},
 
 606         {name => 'relay_log_space',       label => 'Relay Log Space'},
 
 610 #---------------------------------------------------------------------
 
 612 $graphs{commands} = {
 
 615             title  => 'Command Counters',
 
 616             vlabel => 'Commands per ${graph_period}',
 
 617             total  => 'Questions',
 
 619         data_source_attrs => {},
 
 622         {name => 'Com_delete',         label => 'Delete'},
 
 623         {name => 'Com_insert',         label => 'Insert'},
 
 624         {name => 'Com_insert_select',  label => 'Insert select'},
 
 625         {name => 'Com_load',           label => 'Load Data'},
 
 626         {name => 'Com_replace',        label => 'Replace'},
 
 627         {name => 'Com_replace_select', label => 'Replace select'},
 
 628         {name => 'Com_select',         label => 'Select'},
 
 629         {name => 'Com_update',         label => 'Update'},
 
 630         {name => 'Com_update_multi',   label => 'Update multi'},
 
 634 #---------------------------------------------------------------------
 
 636 $graphs{connections} = {
 
 639             title  => 'Connections',
 
 640             vlabel => 'Connections per ${graph_period}',
 
 642         data_source_attrs => {
 
 647         {name => 'max_connections',      label  => 'Max connections',
 
 651         {name => 'Max_used_connections', label  => 'Max used',
 
 655         {name => 'Aborted_clients',      label => 'Aborted clients'},
 
 656         {name => 'Aborted_connects',     label => 'Aborted connects'},
 
 657         {name => 'Threads_connected',    label => 'Threads connected',
 
 659         {name => 'Threads_running',      label => 'Threads running',
 
 661         {name => 'Connections',          label => 'New connections'},
 
 665 #---------------------------------------------------------------------
 
 672         data_source_attrs => {
 
 678         {name => 'open_files_limit', label  => 'File Limit',
 
 681         {name => 'Open_files',       label => 'Open files',
 
 687 #---------------------------------------------------------------------
 
 694         data_source_attrs => {
 
 700         {name => 'table_open_cache',   label  => 'Table cache',
 
 703         {name => 'innodb_open_files',  label => 'Innodb Table Cache Limit',
 
 706         {name => 'Open_tables',        label => 'Open tables'},
 
 707         {name => 'Slave_open_temp_tables',  label => 'Open Slave Temp Tables'},
 
 708         {name => 'Opened_tables',      label => 'Opened tables',
 
 711         {name => 'Opened_views',       label => 'Opened Views',
 
 717 #---------------------------------------------------------------------
 
 719 $graphs{table_definitions} = {
 
 722             title  => 'Tables Definitions',
 
 724         data_source_attrs => {
 
 730         {name => 'table_definition_cache',  label => 'Cache Limit',
 
 733         {name => 'Open_table_definitions',  label => 'Open'},
 
 734         {name => 'Opened_table_definitions',  label => 'Opened',
 
 740 #---------------------------------------------------------------------
 
 742 $graphs{innodb_bpool} = {
 
 745             title  => 'InnoDB Buffer Pool',
 
 747             args   => '--base 1024',
 
 749         data_source_attrs => {
 
 755         {name => 'ib_bpool_size',     label  => 'Buffer pool size',
 
 758         {name => 'ib_bpool_dbpages',  label  => 'Database pages',
 
 761         {name => 'ib_bpool_free',     label => 'Free pages'},
 
 762         {name => 'ib_bpool_modpages', label => 'Modified pages'},
 
 763         {name => 'ib_bpool_oldpages', label => 'Old pages'},
 
 767 #---------------------------------------------------------------------
 
 769 $graphs{innodb_bpool_act} = {
 
 772             title  => 'InnoDB Buffer Pool Activity',
 
 773             vlabel => 'Pages per ${graph_period}',
 
 776         data_source_attrs => {
 
 781         {name => 'ib_bpool_read',           label => 'Read'},
 
 782         {name => 'ib_bpool_created',        label => 'Created'},
 
 783         {name => 'ib_bpool_written',        label => 'Written'},
 
 784         {name => 'ib_bpool_made_young',     label => 'Made young'},
 
 785         {name => 'ib_bpool_made_not_young', label => 'Made not young'},
 
 789 #---------------------------------------------------------------------
 
 791 $graphs{innodb_bpool_internal_breakdown} = {
 
 794             title  => 'InnoDB Buffer Pool Internal breakdown',
 
 795             args   => '--base 1024 --lower-limit 0',
 
 798         data_source_attrs => {
 
 805         {name => 'ib_bpool_internal_adaptive_hash_size_const',    label => 'Adaptive Hash const'},
 
 806         {name => 'ib_bpool_internal_adaptive_hash_size_var',      label => 'Adaptive Hash var'},
 
 807         {name => 'ib_bpool_internal_page_hash_size_total',        label => 'Page Hash'},
 
 808         {name => 'ib_bpool_internal_dictionary_cache_size_const', label => 'Dictionary const'},
 
 809         {name => 'ib_bpool_internal_dictionary_cache_size_var',   label => 'Dictionary var'},
 
 810         {name => 'ib_bpool_internal_file_system_size_const',      label => 'Filesystem const'},
 
 811         {name => 'ib_bpool_internal_file_system_size_var',        label => 'Filesystem var'},
 
 812         {name => 'ib_bpool_internal_lock_system_size_const',      label => 'Lock system const'},
 
 813         {name => 'ib_bpool_internal_lock_system_size_var',        label => 'Lock system var'},
 
 814         {name => 'ib_bpool_internal_recovery_system_size_const',  label => 'Recovery system const'},
 
 815         {name => 'ib_bpool_internal_recovery_system_size_var',    label => 'Recovery system var'},
 
 819 #---------------------------------------------------------------------
 
 821 $graphs{innodb_insert_buf} = {
 
 824             title  => 'InnoDB Insert Buffer',
 
 825             vlabel => 'Activity per ${graph_period}',
 
 827         data_source_attrs => {
 
 832       {name => 'ib_ibuf_inserts',    label => 'Merge Inserts'},
 
 833       {name => 'ib_ibuf_delete',     label => 'Merge Deletes'},
 
 834       {name => 'ib_ibuf_merged_rec', label => 'Merged Records'},
 
 835       {name => 'ib_ibuf_merges',     label => 'Merges'},
 
 836       {name => 'ib_ibuf_discard_inserts', label => 'Discard Inserts'},
 
 837       {name => 'ib_ibuf_discard_delete',  label => 'Discard Deletes'},
 
 841 #---------------------------------------------------------------------
 
 843 $graphs{innodb_adaptive_hash} = {
 
 846             title  => 'InnoDB Adaptive Hash Optimiser',
 
 848         data_source_attrs => {
 
 853         {name => 'Innodb_adaptive_hash_hash_searches',    label => 'Hash Searches'},
 
 854         {name => 'Innodb_adaptive_hash_non_hash_searches', label => 'Nonhash Searches'},
 
 858 #---------------------------------------------------------------------
 
 860 $graphs{innodb_io} = {
 
 863             title  => 'InnoDB IO',
 
 864             vlabel => 'IO operations per ${graph_period}',
 
 866         data_source_attrs => {
 
 871         {name => 'ib_io_read',  label => 'File reads'},
 
 872         {name => 'ib_io_write', label => 'File writes'},
 
 873         {name => 'ib_io_log',   label => 'Log writes'},
 
 874         {name => 'ib_io_fsync', label => 'File syncs'},
 
 878 #---------------------------------------------------------------------
 
 880 $graphs{innodb_io_pend} = {
 
 883             title  => 'InnoDB IO Pending',
 
 884             vlabel => 'Pending operations',
 
 886         data_source_attrs => {
 
 891         {name => 'ib_iop_log',         label => 'AIO Log'},
 
 892         {name => 'ib_iop_sync',        label => 'AIO Sync'},
 
 893         {name => 'ib_iop_flush_bpool', label => 'Buf Pool Flush'},
 
 894         {name => 'ib_iop_flush_log',   label => 'Log Flushes'},
 
 895         {name => 'ib_iop_ibuf_aio',    label => 'Insert Buf AIO Read'},
 
 896         {name => 'ib_iop_aioread',     label => 'Normal AIO Reads'},
 
 897         {name => 'ib_iop_aiowrite',    label => 'Normal AIO Writes'},
 
 901 #---------------------------------------------------------------------
 
 903 $graphs{innodb_log} = {
 
 906             title  => 'InnoDB Log',
 
 907             vlabel => 'Log activity per ${graph_period}',
 
 909         data_source_attrs => {
 
 914         {name => 'innodb_log_buffer_size', label  => 'Buffer Size',
 
 918         {name => 'ib_log_flush',           label => 'KB Flushed'},
 
 919         {name => 'ib_log_written',         label => 'KB Written'},
 
 923 #---------------------------------------------------------------------
 
 925 $graphs{innodb_rows} = {
 
 928             title  => 'InnoDB Row Operations',
 
 929             vlabel => 'Operations per ${graph_period}',
 
 932         data_source_attrs => {},
 
 935         {name => 'Innodb_rows_deleted',  label => 'Deletes'},
 
 936         {name => 'Innodb_rows_inserted', label => 'Inserts'},
 
 937         {name => 'Innodb_rows_read',     label => 'Reads'},
 
 938         {name => 'Innodb_rows_updated',  label => 'Updates'},
 
 942 #---------------------------------------------------------------------
 
 944 $graphs{innodb_semaphores} = {
 
 947             title  => 'InnoDB Semaphores',
 
 948             vlabel => 'Semaphores per ${graph_period}',
 
 950         data_source_attrs => {
 
 955         {name => 'ib_spin_rounds', label => 'Spin Rounds'},
 
 956         {name => 'ib_spin_waits',  label => 'Spin Waits'},
 
 957         {name => 'ib_os_waits',    label => 'OS Waits'},
 
 958   {name => 'ib_rw_shared_rounds', label => 'RW/S Rounds'},
 
 959   {name => 'ib_rw_shared_waits',  label => 'RW/S Waits'},
 
 960   {name => 'ib_rw_shared_os_waits',    label => 'RW/S OS Waits'},
 
 961   {name => 'ib_rw_excl_rounds', label => 'RW/X Rounds'},
 
 962   {name => 'ib_rw_excl_waits',  label => 'RW/X Waits'},
 
 963   {name => 'ib_rw_excl_os_waits',    label => 'RW/X OS Waits'},
 
 967 #---------------------------------------------------------------------
 
 969 $graphs{innodb_tnx} = {
 
 972             title  => 'InnoDB Transactions',
 
 973             vlabel => 'Transactions per ${graph_period}',
 
 975         data_source_attrs => {
 
 980         {name => 'ib_tnx', label => 'Transactions created'},
 
 984 #---------------------------------------------------------------------
 
 986 $graphs{innodb_history_list_length} = {
 
 989             title  => 'InnoDB History List Length',
 
 990             vlabel => 'Undo log units',
 
 992         data_source_attrs => {
 
 998         {name => 'ib_tnx_hist', label => 'History List Length'},
 
1002 #---------------------------------------------------------------------
 
1004 $graphs{innodb_srv_master_thread} = {
 
1007             title  => 'InnoDB Master Thread',
 
1009         data_source_attrs => {
 
1011             draw  => 'AREASTACK',
 
1015         {name => 'ib_srv_main_flush_loops', label => 'Flush Loop'},
 
1016         {name => 'ib_srv_main_background_loops', label => 'Background Loop'},
 
1017         {name => 'ib_srv_main_flushs_writes', label => 'Flushes/Writes', draw  => 'LINE1'},
 
1021 #---------------------------------------------------------------------
 
1023 $graphs{innodb_queries} = {
 
1026            title  => 'InnoDB Engine Queries and Transactions',
 
1027            args   => '--lower-limit 0',
 
1029        data_source_attrs => {
 
1034        {name => 'ib_innodb_queries', label => 'Active'},
 
1035        {name => 'ib_innodb_transactions_active', label => 'Transactions'},
 
1036        {name => 'ib_innodb_query_queue_len', label => 'Queued'},
 
1040 #---------------------------------------------------------------------
 
1042 $graphs{innodb_read_views} = {
 
1045            title  => 'InnoDB Read Views',
 
1046            args   => '--lower-limit 0',
 
1048        data_source_attrs => {
 
1053        {name => 'ib_innodb_read_views', label => 'Views'},
 
1057 #---------------------------------------------------------------------
 
1059 $graphs{innodb_descriptors} = {
 
1062            title  => 'InnoDB Descriptors',
 
1063            args   => '--lower-limit 0',
 
1065        data_source_attrs => {
 
1070        {name => 'ib_innodb_descriptors', label => 'Descriptors'},
 
1071        {name => 'ib_innodb_descriptors_max', label => 'Max', draw => 'AREA', colour => 'ffd660'},
 
1075 #---------------------------------------------------------------------
 
1077 $graphs{performance} = {
 
1080             title  => 'Performance Schema Losses',
 
1082         data_source_attrs => {
 
1087   {name => 'Performance_schema_cond_classes_lost',     label => 'Condition classes'},
 
1088   {name => 'Performance_schema_cond_instances_lost',   label => 'Condition instances'},
 
1089   {name => 'Performance_schema_file_classes_lost',     label => 'File classes'},
 
1090   {name => 'Performance_schema_file_handles_lost',     label => 'File handles'},
 
1091   {name => 'Performance_schema_file_instances_lost',   label => 'File instances'},
 
1092   {name => 'Performance_schema_locker_lost',           label => 'Locker'},
 
1093   {name => 'Performance_schema_mutex_classes_lost',    label => 'Mutex classes'},
 
1094   {name => 'Performance_schema_mutex_instances_lost',  label => 'Mutex instances'},
 
1095   {name => 'Performance_schema_rwlock_classes_lost',   label => 'Read/Write lock classes'},
 
1096   {name => 'Performance_schema_rwlock_instances_lost', label => 'Read/Write lock instances'},
 
1097   {name => 'Performance_schema_table_handles_lost',    label => 'Table handles'},
 
1098   {name => 'Performance_schema_table_instances_lost',  label => 'Table instances'},
 
1099   {name => 'Performance_schema_thread_classes_lost',   label => 'Thread classes'},
 
1100   {name => 'Performance_schema_thread_instances_lost', label => 'Thread instances'},
 
1104 #---------------------------------------------------------------------
 
1106 $graphs{myisam_indexes} = {
 
1109             title  => 'MyISAM Indexes',
 
1110             vlabel => 'Requests per ${graph_period}',
 
1112         data_source_attrs => {
 
1117         {name => 'Key_read_requests',  label => 'Key read requests'},
 
1118         {name => 'Key_reads',          label => 'Key reads'},
 
1119         {name => 'Key_write_requests', label => 'Key write requests'},
 
1120         {name => 'Key_writes',         label => 'Key writes'},
 
1124 #---------------------------------------------------------------------
 
1126 $graphs{network_traffic} = {
 
1129             title  => 'Network Traffic',
 
1130             args   => '--base 1024',
 
1131             vlabel => 'Bytes received (-) / sent (+) per ${graph_period}',
 
1133         data_source_attrs => {
 
1138         {name => 'Bytes_received', label => 'Bytes transferred',
 
1140         {name => 'Bytes_sent',     label    => 'Bytes transferred',
 
1141                                    negative => 'Bytes_received'},
 
1145 #---------------------------------------------------------------------
 
1150             title  => 'Query Cache',
 
1151             vlabel => 'Commands per ${graph_period}',
 
1153         data_source_attrs => {
 
1158         {name => 'Qcache_queries_in_cache', label => 'Queries in cache', type  => 'GAUGE'},
 
1159         {name => 'Qcache_hits',             label => 'Cache hits'},
 
1160         {name => 'Subquery_cache_hit',      label => 'Subquery Cache hits'},
 
1161         {name => 'Subquery_cache_miss',     label => 'Subquery Cache misses'},
 
1162         {name => 'Qcache_inserts',          label => 'Inserts'},
 
1163         {name => 'Qcache_not_cached',       label => 'Not cached'},
 
1164         {name => 'Qcache_lowmem_prunes',    label => 'Low-memory prunes'},
 
1168 #---------------------------------------------------------------------
 
1170 $graphs{qcache_mem} = {
 
1173             title  => 'Query Cache Memory',
 
1175             args   => '--base 1024 --lower-limit 0',
 
1177         data_source_attrs => {
 
1183         {name => 'query_cache_size',    label => 'Cache size'},
 
1184         {name => 'Qcache_free_memory',  label => 'Free mem'},
 
1189 #---------------------------------------------------------------------
 
1191 $graphs{max_mem} = {
 
1194             title  => 'Maximum memory that Mysql could use',
 
1196             args   => '--base 1024 --lower-limit 0',
 
1198         data_source_attrs => {
 
1199             draw => 'AREASTACK',
 
1204         {name => 'mysql_connection_memory',    label => 'Connection Memory'},
 
1205         {name => 'mysql_base_memory',  label => 'Base MYSQL Memory'},
 
1210 #---------------------------------------------------------------------
 
1212 $graphs{replication} = {
 
1215             title  => 'Replication',
 
1216             vlabel => 'Activity',
 
1218         data_source_attrs => {
 
1223         {name => 'slave_io_running',           label => 'Slave IO Running',
 
1226         {name => 'slave_sql_running',          label => 'Slave SQL Running',
 
1229         {name => 'Slave_retried_transactions', label => 'Retried Transactions'},
 
1230         {name => 'Slave_open_temp_tables',     label => 'Open Temp Tables'},
 
1231         {name => 'seconds_behind_master',      label => 'Secs Behind Master',
 
1236 #---------------------------------------------------------------------
 
1238 $graphs{select_types} = {
 
1241             title  => 'Select types',
 
1242             vlabel => 'Commands per ${graph_period}',
 
1245         data_source_attrs => {},
 
1248         {name => 'Select_full_join',       label => 'Full join'},
 
1249         {name => 'Select_full_range_join', label => 'Full range'},
 
1250         {name => 'Select_range',           label => 'Range'},
 
1251         {name => 'Select_range_check',     label => 'Range check'},
 
1252         {name => 'Select_scan',            label => 'Scan'},
 
1256 #---------------------------------------------------------------------
 
1261             title  => 'Slow Queries',
 
1262             vlabel => 'Slow queries per ${graph_period}',
 
1264         data_source_attrs => {
 
1269         {name => 'Slow_queries', label => 'Slow queries'},
 
1273 #---------------------------------------------------------------------
 
1279             vlabel => 'Sorts / ${graph_period}',
 
1281         data_source_attrs => {
 
1286         {name => 'Sort_rows',         label => 'Rows sorted'},
 
1287         {name => 'Sort_range',        label => 'Range'},
 
1288         {name => 'Sort_merge_passes', label => 'Merge passes'},
 
1289         {name => 'Sort_scan',         label => 'Scan'},
 
1293 #---------------------------------------------------------------------
 
1295 $graphs{table_locks} = {
 
1298             title  => 'Table locks',
 
1299             vlabel => 'locks per ${graph_period}',
 
1301         data_source_attrs => {
 
1306         {name => 'Table_locks_immediate', label => 'Table locks immed'},
 
1307         {name => 'Table_locks_waited',    label => 'Table locks waited'},
 
1311 #---------------------------------------------------------------------
 
1313 $graphs{tmp_tables} = {
 
1316             title  => 'Temporary objects',
 
1317             vlabel => 'Objects per ${graph_period}',
 
1319         data_source_attrs => {
 
1324         {name => 'Created_tmp_disk_tables', label => 'Temp disk tables'},
 
1325         {name => 'Created_tmp_tables',      label => 'Temp tables'},
 
1326         {name => 'Created_tmp_files',       label => 'Temp files'},
 
1331 #---------------------------------------------------------------------
 
1338   data_source_attrs => {
 
1344   {name => 'Rows_read',     label => 'Read'},
 
1345   {name => 'Rows_sent',     label => 'Sent'},
 
1346   {name => 'Rows_tmp_read', label => 'Temp Read'},
 
1350 #---------------------------------------------------------------------
 
1352 $graphs{handler_read} = {
 
1355       title  => 'Read Handler',
 
1357   data_source_attrs => {
 
1358       draw  => 'AREASTACK',
 
1363   {name => 'Handler_read_first',       label => 'Key First'},
 
1364   {name => 'Handler_read_key',         label => 'Key Read'},
 
1365   {name => 'Handler_read_last',        label => 'Key Last'},
 
1366   {name => 'Handler_read_prev',        label => 'Key Prev'},
 
1367   {name => 'Handler_read_rnd',         label => 'Row position'},
 
1368   {name => 'Handler_read_rnd_deleted', label => 'Row position delete'},
 
1369   {name => 'Handler_read_rnd_next',    label => 'Row position next'},
 
1373 #---------------------------------------------------------------------
 
1375 $graphs{handler_transaction} = {
 
1378       title  => 'Transactions Handler',
 
1380   data_source_attrs => {
 
1381       draw  => 'AREASTACK',
 
1386   {name => 'Handler_commit',             label => 'Commit'},
 
1387   {name => 'Handler_rollback',           label => 'Rollback'},
 
1388   {name => 'Handler_savepoint',          label => 'Savepoint'},
 
1389   {name => 'Handler_savepoint_rollback', label => 'Savepoint Rollback'},
 
1393 #---------------------------------------------------------------------
 
1395 $graphs{handler_write} = {
 
1398       title  => 'Write/Update Handler',
 
1400   data_source_attrs => {
 
1406   {name => 'Handler_write',  label => 'Writes'},
 
1407   {name => 'Handler_update', label => 'Updates'},
 
1411 #---------------------------------------------------------------------
 
1413 $graphs{handler_tmp} = {
 
1416       title  => 'Temporary Write/Update Handler',
 
1418   data_source_attrs => {
 
1424   {name => 'Handler_tmp_write',  label => 'Writes'},
 
1425   {name => 'Handler_tmp_update', label => 'Updates'},
 
1429 #---------------------------------------------------------------------
 
1431 $graphs{execution} = {
 
1434       title  => 'Execution Events',
 
1436   data_source_attrs => {
 
1442   {name => 'Executed_events',   label => 'Events'},
 
1443   {name => 'Executed_triggers', label => 'Triggers'},
 
1447 #---------------------------------------------------------------------
 
1452             title  => 'Index Condition Pushdown',
 
1454         data_source_attrs => {
 
1460         {name => 'Handler_icp_attempts', label => 'Attempts'},
 
1461         {name => 'Handler_icp_match',    label => 'Matches'},
 
1465 #---------------------------------------------------------------------
 
1470             title  => 'Multi Range Read optimizations',
 
1472         data_source_attrs => {
 
1478         {name => 'Handler_mrr_init', label => 'Uses'},
 
1479         {name => 'Handler_mrr_key_refills',  label => 'Key refills'},
 
1480         {name => 'Handler_mrr_rowid_refills',  label => 'Row refills'},
 
1484 #---------------------------------------------------------------------
 
1486 #  These are mysql plugins of type INFORMATION SCHEMA
 
1488 #  These will be added to $graphs if available
 
1489 #---------------------------------------------------------------------
 
1491 my %graph_plugins = ();
 
1493 $graph_plugins{query_response_time} = {
 
1497                 title  => 'Query Response Time Count',
 
1498                 vlabel  => 'queries per ${graph_period}',
 
1500             data_source_attrs => {
 
1505         # data_sources are populated by sub plugin_query_response_time
 
1512                 title  => 'Query Response Time Total',
 
1513                 vlabel  => 'query time (microseconds) per ${graph_period}',
 
1515             data_source_attrs => {
 
1520         # data_sources are populated by sub plugin_query_response_time
 
1526 $graph_plugins{user_statistics} = {
 
1530                 title  => 'User Connections',
 
1531                 vlabel  => 'connections per ${graph_period}',
 
1533             data_source_attrs => {
 
1538         cols => { 'total_connections' => {}, 'concurrent_connections' => {}, 'denied_connections' => {}, 'lost_connections'  => {}},
 
1545                 title  => 'User Time',
 
1546                 vlabel  => 'seconds',
 
1548             data_source_attrs => {
 
1553         cols => { 'connected_time' => {}, 'busy_time' => {}, 'cpu_time' => {} },
 
1560                 title  => 'User Bytes',
 
1563             data_source_attrs => {
 
1568         cols => { 'bytes_received' => {}, 'bytes_sent' => {}, 'binlog_bytes_written' => {} },
 
1575                 title  => 'User Rows',
 
1578             data_source_attrs => {
 
1583         cols => { 'rows_read' => {}, 'rows_sent' => {}, 'rows_deleted' => {}, 'rows_inserted' => {}, 'rows_updated' => {} },
 
1590                 title  => 'Command breakdown by user',
 
1591                 vlabel  => 'commands',
 
1593             data_source_attrs => {
 
1598         cols => { 'select_commands' => {}, 'update_commands' => {}, 'other_commands' => {}, 'commit_transactions' => {}, 'rollback_transactions' => {} },
 
1604 #---------------------------------------------------------------------
 
1606 #---------------------------------------------------------------------
 
1610 # Global hash holding the data collected from mysql.
 
1612 our $data; # Was 'my'. Changed to 'our' to facilitate testing.
 
1616     my $graph = basename($0);
 
1617     $graph =~ s/^mysql[0-9]*_//g; # allow multiple instances
 
1618     my $command = $ARGV[0] || 'show';
 
1621         'autoconf' => \&autoconf,
 
1622         'config'   => \&config,
 
1624         'suggest'  => \&suggest,
 
1627     die "Unknown command: $command"
 
1628         unless exists $command_map{$command};
 
1630     die "Missing dependency Cache::Cache"
 
1631         unless $has_cache || $command eq 'autoconf';
 
1633     return $command_map{$command}->($graph);
 
1637 #---------------------------------------------------------------------
 
1638 #  C O M M A N D   H A N D L E R S
 
1639 #---------------------------------------------------------------------
 
1641 # Each command handler should return an appropriate exit code
 
1644 # http://munin-monitoring.org/wiki/ConcisePlugins#autoconf
 
1646     unless ($has_cache) {
 
1647         print "no (Missing dependency Cache::Cache)\n";
 
1656         $err =~ s{\s at \s \S+ \s line .*}{}xms;
 
1657         print "no ($err)\n";
 
1665 # http://munin-monitoring.org/wiki/ConcisePlugins#suggest
 
1668     # What is the best way to decide which graphs is applicable to a
 
1671     # Use lack of variables to indicate that the capability doesn't exist
 
1672     # Use variable values to indicate some graph isn't currently used.
 
1673     # update_data() now does this.
 
1678     foreach my $graph (sort keys(%graphs)) {
 
1679         next if $graph =~ /innodb_/ && $data->{_innodb_disabled};
 
1680         next if $graph =~ /wsrep_/ && $data->{_galera_disabled};
 
1689     my $graph_name = shift;
 
1691     # In MySQL 5.1 (and probably erlier versions as well) status
 
1692     # variables are unique when looking at the last 19 characters.
 
1694     #   SELECT RIGHT(variable_name, 19), COUNT(*)
 
1695     #     FROM information_schema.global_status
 
1696     #    GROUP BY RIGHT(variable_name, 19)
 
1697     #   HAVING COUNT(*) > 1;
 
1699     #   Empty set (0.06 sec)
 
1701     # There is one duplicate when looking at server variables
 
1703     #   SELECT RIGHT(variable_name, 19), COUNT(*)
 
1704     #     FROM information_schema.global_variables
 
1705     #    GROUP BY RIGHT(variable_name, 19)
 
1706     #   HAVING COUNT(*) > 1;
 
1708     #   +--------------------------+----------+
 
1709     #   | RIGHT(variable_name, 19) | COUNT(*) |
 
1710     #   +--------------------------+----------+
 
1711     #   | OW_PRIORITY_UPDATES      |        2 |
 
1712     #   +--------------------------+----------+
 
1713     #   1 row in set (0.05 sec)
 
1715     #   show global variables like '%OW_PRIORITY_UPDATES';
 
1717     #   +--------------------------+-------+
 
1718     #   | Variable_name            | Value |
 
1719     #   +--------------------------+-------+
 
1720     #   | low_priority_updates     | OFF   |
 
1721     #   | sql_low_priority_updates | OFF   |
 
1722     #   +--------------------------+-------+
 
1723     #   2 rows in set (0.00 sec)
 
1725     # Not a problem since we don't graph these
 
1729     die 'Unknown graph ' . ($graph_name ? $graph_name : '')
 
1730         unless $graphs{$graph_name};
 
1732     my $graph = $graphs{$graph_name};
 
1734     my %conf = (%{$defaults{global_attrs}}, %{$graph->{config}{global_attrs}});
 
1735     while (my ($k, $v) = each %conf) {
 
1736         print "graph_$k $v\n";
 
1738     if ($graph_name =~ /wsrep_/) {
 
1739       print "graph_category galera\n";
 
1741       print "graph_category mysql\n";
 
1745     for my $ds (@{$graph->{data_sources}}) {
 
1747             %{$defaults{data_source_attrs}},
 
1748             %{$graph->{config}{data_source_attrs}},
 
1751         while (my ($k, $v) = each %ds_spec) {
 
1752             # 'name' is only used internally in this script, not
 
1753             # understood by munin.
 
1754             next if ($k eq 'name');
 
1756             # AREASTACK is part of munin as of version 1.3.3 (not
 
1757             # released yet). Until then ...
 
1758             if ($k eq 'draw' && $v eq 'AREASTACK') {
 
1759                 printf("%s.%s %s\n",
 
1760                        clean_fieldname($ds->{name}), $k, ($i ? 'STACK' : 'AREA'));
 
1763                 printf("%s.%s %s\n", clean_fieldname($ds->{name}), $k, $v);
 
1767         print_thresholds(clean_fieldname($ds->{name}));
 
1774     my $graph_name = shift;
 
1778     die 'Unknown graph ' . ($graph_name ? $graph_name : '')
 
1779         unless $graphs{$graph_name};
 
1781     my $graph = $graphs{$graph_name};
 
1783     die "Can't show data for '$graph_name' because InnoDB is disabled."
 
1784         if $graph_name =~ /innodb_/ && $data->{_innodb_disabled};
 
1786     die "Can't show data for '$graph_name' because not a Galera mysql version."
 
1787         if $graph_name =~ /wsrep_/ && $data->{_galera_disabled};
 
1789     for my $ds (@{$graph->{data_sources}}) {
 
1790         printf "%s.value %s\n",
 
1791             clean_fieldname($ds->{name}),
 
1792             defined $data->{$ds->{name}} ? $data->{$ds->{name}} : 'U';
 
1800 #---------------------------------------------------------------------
 
1801 #  U T I L I T Y   S U B S
 
1802 #---------------------------------------------------------------------
 
1806     my $dsn = "$config{dsn};mysql_connect_timeout=5";
 
1808     return DBI->connect($dsn, $config{user}, $config{password}, {
 
1811         FetchHashKeyName => 'NAME_lc',
 
1817     $data = $shared_memory_cache->get('data');
 
1818     my $graphs_stored = $shared_memory_cache->get('graphs');
 
1819     %graphs = %{thaw($graphs_stored)} if $graphs_stored;
 
1824     my $dbh = db_connect();
 
1826     update_variables($dbh);
 
1827     update_plugins($dbh);
 
1828     update_innodb($dbh);
 
1829     update_master($dbh);
 
1830     delete $graphs{replication} if update_slave($dbh)==1;
 
1832     delete $graphs{bin_relay_log} if not defined $data->{relay_log_space}
 
1833         && not defined $data->{ma_binlog_size};
 
1835     delete $graphs{execution} if not defined $data->{Executed_events}
 
1836         && not defined $data->{Executed_triggers};
 
1838     delete $graphs{icp} if not defined $data->{Handler_icp_attempts}
 
1839         && not defined $data->{Handler_icp_matches};
 
1841     delete $graphs{innodb_adaptive_hash}
 
1842         if not defined $data->{Innodb_adaptive_hash_hash_searches}
 
1843         && not defined $data->{Innodb_adaptive_hash_non_hash_searches};
 
1845     delete $graphs{innodb_bpool_internal_breakdown}
 
1846         if not defined $data->{ib_bpool_internal_adaptive_hash_size_const};
 
1848     delete $graphs{innodb_descriptors}
 
1849         if not defined $data->{ib_innodb_descriptors};
 
1851     delete $graphs{mrr} if not defined $data->{Handler_mrr_init};
 
1853     delete $graphs{rows} if not defined $data->{Rows_sent};
 
1855     delete $graphs{handler_temp} if not defined $data->{Handler_tmp_write};
 
1857     $shared_memory_cache->set('data', $data);
 
1858     $shared_memory_cache->set('graphs', nfreeze(\%graphs));
 
1862 sub update_plugins {
 
1866         'query_response_time'         => \&plugin_query_response_time,
 
1870       my ($f, $sec, $dbh, %g) = @_;
 
1871       if ($f->($dbh) == 0) {
 
1872         while (my ($k, $v) = each %g) {
 
1873           $graphs{$sec . '_' . $k} = $v;
 
1878     my $sth = $dbh->prepare("SHOW PLUGINS");
 
1880     while (my $row = $sth->fetchrow_hashref()) {
 
1881         next if $row->{'type'} ne 'INFORMATION SCHEMA';
 
1882         my $sec = lc $row->{'name'};
 
1883         next if not exists $plugin_map{$sec};
 
1884         add_graphs($plugin_map{$sec}, $sec, $dbh, %{$graph_plugins{$sec}});
 
1889         'user_statistics'         => \&is_user_statistics,
 
1892     $sth = $dbh->prepare("SHOW TABLES IN INFORMATION_SCHEMA");
 
1894     while (my $row = $sth->fetchrow_hashref()) {
 
1895         my $sec = lc $row->{'tables_in_information_schema'};
 
1896         next if not exists $is_map{$sec};
 
1897         add_graphs($is_map{$sec}, $sec, $dbh, %{$graph_plugins{$sec}});
 
1902 sub update_variables {
 
1905         'SHOW GLOBAL STATUS',
 
1906         'SHOW GLOBAL VARIABLES',
 
1909     my %variable_name_map = (
 
1910         table_cache => 'table_open_cache', # table_open_cache was
 
1911                                            # previously known as
 
1912                                            # table_cache in MySQL
 
1913                                            # 5.1.2 and earlier.
 
1915     my %wsrep_cluster_status_map = (
 
1920     for my $query (@queries) {
 
1921         $data->{$query} = {};
 
1923         my $sth = $dbh->prepare($query);
 
1925         while (my $row = $sth->fetch) {
 
1926             my $var = $variable_name_map{$row->[0]} || $row->[0];
 
1927             $data->{$var} = $row->[1];
 
1932     $data->{'mysql_base_memory'} = $data->{'key_buffer_size'} 
 
1933                                          + $data->{'query_cache_size'}
 
1934                                          + $data->{'innodb_buffer_pool_size'}
 
1935                                          + ( $data->{'innodb_additional_mem_pool_size'} || 0 )
 
1936                                          + $data->{'innodb_log_buffer_size'}
 
1937                                          + ( $data->{'tokudb_cache_size'} || 0 );
 
1939     my $tmp_table_size = $data->{'tmp_table_size'};
 
1940     my $max_heap_table_size = $data->{'max_heap_table_size'};
 
1941     $data->{'mysql_connection_memory'} = $data->{'read_buffer_size'}
 
1942                                    + $data->{'read_rnd_buffer_size'}
 
1943                                    + $data->{'sort_buffer_size'}
 
1944                                    + $data->{'join_buffer_size'}
 
1945                                    + $data->{'binlog_cache_size'}
 
1946                                    + $data->{'thread_stack'}
 
1947                                    + ( $tmp_table_size >= $max_heap_table_size ? $tmp_table_size : $max_heap_table_size )
 
1948                                    + ( $data->{'tokudb_read_buf_size'} || 0 );
 
1950    # wsrep_thread_count was separated from max_connections for mariadb-5.5.38 https://mariadb.atlassian.net/browse/MDEV-6206
 
1951    $data->{'mysql_connection_memory'} *= $data->{'max_connections'} + ( $data->{'wsrep_thread_count'} || 0 );
 
1953     if ($data->{wsrep_cluster_status}) {
 
1954       my $var = $wsrep_cluster_status_map{$data->{wsrep_cluster_status}};
 
1955       $data->{wsrep_cluster_status} = $var;
 
1957     $data->{_galera_disabled} = 1 unless ($data->{wsrep_provider_name});
 
1964     my $sth = $dbh->prepare('SHOW /*!50000 ENGINE*/ INNODB STATUS');
 
1969         if ($@ =~ /Unknown (storage|table) engine 'INNODB'|Cannot call SHOW INNODB STATUS because skip-innodb is defined/i) {
 
1970             $data->{_innodb_disabled} = 1;
 
1975     my $row = $sth->fetchrow_hashref();
 
1976     my $status = $row->{'status'};
 
1979     parse_innodb_status($status);
 
1986     my $sth = $dbh->prepare('SHOW MASTER LOGS');
 
1991         # SHOW MASTER LOGS failed because binlog is not enabled
 
1992         return if $@ =~ /You are not using binary logging/;
 
1996     while (my $row = $sth->fetch) {
 
1997         $data->{ma_binlog_size} += $row->[1];
 
2007     my $sth = $dbh->prepare('SHOW SLAVE STATUS');
 
2009     my $row = $sth->fetchrow_hashref();
 
2010     return 1 unless $row;
 
2011     while (my ($k, $v) = each %$row) {
 
2016     # We choose master_host here as a stopped slave
 
2017     # may not indicate that we have reset all slave capability
 
2018     # however the minimium requirement is a master_host
 
2019     return 1 if not defined $data->{master_host};
 
2021     # undef when slave is stopped, or when MySQL fails to calculate
 
2022     # the lag (which happens depresingly often). (mk-heartbeat fixes
 
2024     $data->{seconds_behind_master} ||= 0;
 
2026     # Track these two fields so we can trigger warnings if the slave stops
 
2028     $data->{slave_sql_running} = ($data->{slave_sql_running} eq 'Yes')
 
2030     $data->{slave_io_running} = ($data->{slave_io_running} eq 'Yes')
 
2036 #---------------------------------------------------------------------
 
2037 #  Information SCHEMA tables represent data to be processed
 
2038 #---------------------------------------------------------------------
 
2041 sub plugin_query_response_time {
 
2044     return 1 if not defined $data->{query_response_time_stats};
 
2045     return 1 if $data->{query_response_time_stats} eq 'OFF';
 
2047     my $sth = $dbh->prepare("SELECT * FROM INFORMATION_SCHEMA.QUERY_RESPONSE_TIME");
 
2049     while (my $row = $sth->fetchrow_hashref()) {
 
2050         my $time = $row->{'time'};
 
2051         $data->{'query_response_time_count_' . $time} = $row->{'count'};
 
2052         push @{$graph_plugins{query_response_time}->{count}->{data_sources}}, {name => 'query_response_time_count_' . $time, label => $time };
 
2053         next if $row->{'total'} eq 'TOO LONG';
 
2054         $data->{'query_response_time_total_' . $time} = $row->{'total'} * 1e6;
 
2055         push @{$graph_plugins{query_response_time}->{total}->{data_sources}}, {name => 'query_response_time_total_' . $time, label => $time };
 
2062 sub is_user_statistics {
 
2065     return 1 if not defined $data->{userstat};
 
2066     return 1 if $data->{userstat} eq 'OFF';
 
2068     my $sth = $dbh->prepare("SELECT * FROM INFORMATION_SCHEMA.USER_STATISTICS");
 
2070     while (my $row = $sth->fetchrow_hashref()) {
 
2071         my $user = $row->{'user'};
 
2073         while (my ($g, $v) = each %{$graph_plugins{user_statistics}}) {
 
2074           while (my ($userstat,$conf) = each %{$v->{cols}}) {
 
2075             $var = 'user_stats_' . $user . '_' . $userstat;
 
2076             $data->{$var} = int $row->{$userstat};
 
2077             my $ds = { %$conf };
 
2079             $ds->{label} = $user . ' ' . $userstat;
 
2080             push @{$graph_plugins{user_statistics}->{$g}->{data_sources}}, $ds;
 
2089 # In 'SHOW ENGINE INNODB STATUS' 64 bit integers are not formated as
 
2090 # plain integers. They are either:
 
2092 #   - split in two and needs to be shifted together,
 
2099         ? Math::BigInt->new($x)->blsft(32) + $y
 
2100         : Math::BigInt->new("0x$x");
 
2103 #---------------------------------------------------------------------
 
2104 #  P A R S E   'SHOW ENGINE INNODB STATUS'   O U T P U T
 
2105 #---------------------------------------------------------------------
 
2108 # A nice walk through
 
2109 # http://www.mysqlperformanceblog.com/2006/07/17/show-innodb-status-walk-through/
 
2111 # The parsing is split in one subrutine per section. Each subroutine
 
2112 # should parse a block with the following structure
 
2118 sub parse_innodb_status {
 
2121     # Add a dummy section to the end in case the innodb status output
 
2122     # has been truncated (Happens for status > 64K characters)
 
2123     $_ .= "\n----------\nDUMMY\n----------\n";
 
2127         'BUFFER POOL AND MEMORY'      => \&parse_buffer_pool_and_memory,
 
2128         'INDIVIDUAL BUFFER POOL INFO' => \&parse_individual_buffer_pool,
 
2129         'FILE I/O'                    => \&parse_file_io,
 
2130         'INSERT BUFFER AND ADAPTIVE HASH INDEX'
 
2131             => \&parse_insert_buffer_and_adaptive_hash_index,
 
2132         'LATEST DETECTED DEADLOCK'    => \&skip,
 
2133         'LATEST FOREIGN KEY ERROR'    => \&skip,
 
2134         'LOG'                         => \&parse_log,
 
2135         'ROW OPERATIONS'              => \&parse_row_operations,
 
2136         'SEMAPHORES'                  => \&parse_semaphores,
 
2137         'TRANSACTIONS'                => \&parse_transactions,
 
2138         'BACKGROUND THREAD'           => \&parse_background_thread,
 
2142     parse_header_seconds();
 
2147         last if $sec eq 'END OF INNODB MONITOR OUTPUT';
 
2148         if ($sec eq 'DUMMY') {
 
2149             handle_incomplete_innodb_status();
 
2153         if (exists $section_map{$sec}) {
 
2154       $section_map{$sec}->();
 
2156       #warn "Unknown section: $1";
 
2163 # This regular expression handles the different formating of 64-bit
 
2164 # integers in different versions of the innodb engine. Either two
 
2165 # decimal 32-bit integers separated by a space, or a single
 
2166 # hexadecimal 64-bit integer.
 
2167 my $innodb_bigint_rx = qr{([[a-fA-F\d]+)(?: (\d+))?};
 
2170 sub match_new_section {
 
2172             -+\n                 # ---------------------------
 
2173             (?= [A-Z\/ ]+\n      # SECTION NAME
 
2174                 [=-]+\n)/gcx;    # --------------------------- ('=' on end of output)
 
2178 sub skip_line    { return m/\G.*\n/gc; }
 
2182     # Heading is 3 lines
 
2183     for my $foo (1...3) {
 
2184         skip_line or die('Parse error');
 
2192     #warn substr($_, pos(), 10);
 
2194         return if match_new_section;
 
2195         next if $parser->();
 
2201 sub skip { parse_section(sub {}); }
 
2204 sub parse_header_seconds {
 
2207             m/\GPer second averages calculated from the last (\d+) seconds\n/gc && do {
 
2208                 $data->{innodb_engine_status_seconds}  = $1;
 
2216 sub parse_background_thread {
 
2219             m/\Gsrv_master_thread loops: \d+ 1_second, \d+ sleeps, \d+ 10_second, (\d+) background, (\d+) flush\n/gc && do {
 
2220                 $data->{ib_srv_main_flush_loops} = $1;
 
2221                 $data->{ib_srv_main_background_loops} = $2;
 
2224             m/\Gsrv_master_thread log flush and writes: (\d+)\n/gc && do {
 
2225                 $data->{ib_srv_main_flushs_writes}  = $1;
 
2232 sub parse_row_operations {
 
2235             m/\G(\d+) queries inside InnoDB, (\d+) queries in queue\n/gc && do {
 
2236                 $data->{ib_innodb_queries}  = $1;
 
2237                 $data->{ib_innodb_query_queue_len} = $2;
 
2240             m/\G(\d+) read views open inside InnoDB\n/gc && do {
 
2241                 $data->{ib_innodb_read_views}  = $1;
 
2244             m/\G(\d+) transactions active inside InnoDB\n/gc && do {
 
2245                 $data->{ib_innodb_transactions_active}  = $1;
 
2248             m/\G(\d+) out of (\d+) descriptors used\n/gc && do {
 
2249                 $data->{ib_innodb_descriptors}  = $1;
 
2250                 $data->{ib_innodb_descriptors_max} = $2;
 
2253             # no need for this - its exposed as status variables
 
2254             # m/\GNumber of rows inserted (\d+), updated (\d+), deleted (\d+), read (\d+)\n/gc && do {
 
2255             #     $data->{ib_innodb_rows_inserted} = $1;
 
2256             #     $data->{ib_innodb_rows_updated}  = $2;
 
2257             #     $data->{ib_innodb_rows_deleted}  = $3;
 
2258             #     $data->{ib_innodb_rows_read}     = $4;
 
2265 sub parse_semaphores {
 
2268             m/\GMutex spin waits (\d+), rounds (\d+), OS waits (\d+)\n/gc && do {
 
2269                 $data->{ib_spin_waits}  = $1;
 
2270                 $data->{ib_spin_rounds} = $2;
 
2271                 $data->{ib_os_waits}    = $3;
 
2274             m/\GRW-shared spins (\d+), rounds (\d+), OS waits (\d+)\n/gc && do {
 
2275                 $data->{ib_rw_shared_waits}  = $1;
 
2276                 $data->{ib_rw_shared_rounds} = $2;
 
2277                 $data->{ib_rw_shared_os_waits}    = $3;
 
2280             m/\GRW-excl spins (\d+), rounds (\d+), OS waits (\d+)\n/gc && do {
 
2281                 $data->{ib_rw_excl_waits}  = $1;
 
2282                 $data->{ib_rw_excl_rounds} = $2;
 
2283                 $data->{ib_rw_excl_os_waits}    = $3;
 
2291 sub parse_transactions {
 
2294             m/\GTrx id counter $innodb_bigint_rx\n/gc && do {
 
2295                 $data->{ib_tnx} = innodb_bigint($1, $2);
 
2298             m/\GPurge done for trx's n:o < $innodb_bigint_rx undo n:o < $innodb_bigint_rx\n/gc && do {
 
2301                     $data->{ib_tnx_prg} = innodb_bigint($1, $2);
 
2302                     # FIX add to data? innodb_bigint($3, $4);
 
2306                     $data->{ib_tnx_prg} = innodb_bigint($1);
 
2307                     # FIX add to data? innodb_bigint($2);
 
2311             m/\GHistory list length (\d+)\n/gc && do {
 
2312                 $data->{ib_tnx_hist} = $1;
 
2324             m/\GPending normal aio reads: (\d+)(?: \[(?:\d+, )*\d+\] )?, aio writes: (\d+)(?: \[(?:\d+, )*\d+\] )?,\n\s*ibuf aio reads: (\d+), log i\/o's: (\d+), sync i\/o's: (\d+)\n/gc && do {
 
2325                 $data->{ib_iop_aioread}  = $1;
 
2326                 $data->{ib_iop_aiowrite} = $2;
 
2327                 $data->{ib_iop_ibuf_aio} = $3;
 
2328                 $data->{ib_iop_log}      = $4;
 
2329                 $data->{ib_iop_sync}     = $5;
 
2332             m/\GPending flushes \(fsync\) log: (\d+); buffer pool: (\d+)\n/gc && do {
 
2333                 $data->{ib_iop_flush_log}   = $1;
 
2334                 $data->{ib_iop_flush_bpool} = $2;
 
2337             m/\G(\d+) OS file reads, (\d+) OS file writes, (\d+) OS fsyncs\n/gc && do {
 
2338                 $data->{ib_io_read}  = $1;
 
2339                 $data->{ib_io_write} = $2;
 
2340                 $data->{ib_io_fsync} = $3;
 
2348 sub parse_insert_buffer_and_adaptive_hash_index {
 
2352       m/\G(\d+) inserts, (\d+) merged recs, (\d+) merges\n/gc && do {
 
2353         $data->{ib_ibuf_inserts}    = $1;
 
2354         $data->{ib_ibuf_merged_rec} = $2;
 
2355         $data->{ib_ibuf_merges}     = $3;
 
2359       m/\Gmerged operations:\n insert (\d+), delete mark (\d+), delete (\d+)\ndiscarded operations:\n insert (\d+), delete mark (\d+), delete (\d+)\n/gc && do {
 
2360         $data->{ib_ibuf_inserts} = $1;
 
2361         $data->{ib_ibuf_delete_mark} = $2;
 
2362         $data->{ib_ibuf_delete} = $3;
 
2363         $data->{ib_ibuf_discard_inserts} = $4;
 
2364         $data->{ib_ibuf_discard_delete_mark} = $5;
 
2365         $data->{ib_ibuf_discard_delete} = $6;
 
2366         $data->{ib_ibuf_merged_rec} = $data->{ib_ibuf_inserts} +  $data->{ib_ibuf_discard_inserts};
 
2370       m/\GIbuf: size (\d+), free list len (\d+), seg size (\d+),(?: (\d+) merges)?\n/gc && do {
 
2371     $data->{ib_ibuf_size}     = $1;
 
2372     $data->{ib_ibuf_free_len} = $2;
 
2373     $data->{ib_ibuf_seg_size} = $3;
 
2374     $data->{ib_ibuf_merges}   = $4 if defined $4; # MySQL >= 5.5
 
2385             m/\GLog sequence number $innodb_bigint_rx\n/gc && do {
 
2386                 $data->{ib_log_written} = innodb_bigint($1, $2);
 
2389             m/\GLog flushed up to\s+$innodb_bigint_rx\n/gc && do {
 
2390                 $data->{ib_log_flush} = innodb_bigint($1, $2);
 
2393             m/\G(\d+) log i\/o's done.*\n/gc && do {
 
2394                 $data->{ib_io_log} = $1;
 
2402 sub parse_buffer_pool_and_memory {
 
2405             m/\GBuffer pool size\s+(\d+)\n/gc && do {
 
2406                 $data->{ib_bpool_size} = $1;
 
2409       m/\GBuffer pool size, bytes\s+(\d+)\n/gc && do {
 
2410     $data->{ib_bpool_size_bytes} = $1;
 
2413             m/\GFree buffers\s+(\d+)\n/gc && do {
 
2414                 $data->{ib_bpool_free} = $1;
 
2417             m/\GDatabase pages\s+(\d+)\n/gc && do {
 
2418                 $data->{ib_bpool_dbpages} = $1;
 
2421             m/\GModified db pages\s+(\d+)\n/gc && do {
 
2422                 $data->{ib_bpool_modpages} = $1;
 
2425             m/\GOld database pages\s+(\d+)\n/gc && do {
 
2426                 $data->{ib_bpool_oldpages} = $1;
 
2429       m/\GPages made young (\d+), not young (\d+)\n/gc && do {
 
2430     $data->{ib_bpool_made_young}     = $1;
 
2431     $data->{ib_bpool_made_not_young} = $2;
 
2434             m/\GPages read (\d+), created (\d+), written (\d+)\n/gc && do {
 
2435                 $data->{ib_bpool_read}    = $1;
 
2436                 $data->{ib_bpool_created} = $2;
 
2437                 $data->{ib_bpool_written} = $3;
 
2441       m/\GInternal hash tables \(constant factor \+ variable factor\)\n\s*Adaptive hash index\s*(\d+)\s*\((\d+) \+ (\d+)\) *\n\s+Page hash +(\d+) +\(buffer pool \d+ only\) *\n\s+Dictionary cache\s*(\d+)\s+\((\d+) \+ (\d+)\) *\n\s+File system\s+(\d+)\s+\((\d+) \+ (\d+)\) *\n\s+Lock system\s+(\d+)\s+\((\d+) \+ (\d+)\) *\n\s+Recovery system\s*(\d+)\s+\((\d+) \+ (\d+)\) *\n/gc  
 
2443           $data->{ib_bpool_internal_adaptive_hash_size_total}  = $1;
 
2444           $data->{ib_bpool_internal_adaptive_hash_size_const}  = $2;
 
2445           $data->{ib_bpool_internal_adaptive_hash_size_var}    = $3;
 
2446           $data->{ib_bpool_internal_page_hash_size_total}  = $4;
 
2447           $data->{ib_bpool_internal_dictionary_cache_size_total}  = $5;
 
2448           $data->{ib_bpool_internal_dictionary_cache_size_const}  = $6;
 
2449           $data->{ib_bpool_internal_dictionary_cache_size_var}    = $7;
 
2450           $data->{ib_bpool_internal_file_system_size_total}  = $8;
 
2451           $data->{ib_bpool_internal_file_system_size_const}  = $9;
 
2452           $data->{ib_bpool_internal_file_system_size_var}    = $10;
 
2453           $data->{ib_bpool_internal_lock_system_size_total}  = $11;
 
2454           $data->{ib_bpool_internal_lock_system_size_const}  = $12;
 
2455           $data->{ib_bpool_internal_lock_system_size_var}    = $13;
 
2456           $data->{ib_bpool_internal_recovery_system_size_total}  = $14;
 
2457           $data->{ib_bpool_internal_recovery_system_size_const}  = $15;
 
2458           $data->{ib_bpool_internal_recovery_system_size_var}    = $16;
 
2466 sub parse_individual_buffer_pool {
 
2469     m/\G---BUFFER POOL (\d+)\n/gc && do {
 
2471       $data->{ib_bpool_individual_pool_count} = $pool + 1;
 
2472       m/\GBuffer pool size\s+(\d+)\n/gc && do {
 
2473         $data->{"ib_bpool_individual_pool_${pool}_size"} = $1;
 
2475       m/\GBuffer pool size, bytes\s+(\d+)\n/gc && do {
 
2476         $data->{"ib_bpool_individual_pool_${pool}_size_bytes"} = $1;
 
2478       m/\GFree buffers\s+(\d+)\nDatabase pages\s+(\d+)\nOld database pages\s+(\d+)\nModified db pages\s+(\d+)\nPending reads\s+(\d+)\nPending writes: LRU\s+(\d+), flush list\s+(\d+), single page\s+(\d+)\n/gc && do {
 
2479         $data->{"ib_bpool_individual_pool_${pool}_free"} = $1;
 
2480         $data->{"ib_bpool_individual_pool_${pool}_dbpages"} = $2;
 
2481         $data->{"ib_bpool_individual_pool_${pool}_oldpages"} = $3;
 
2482         $data->{"ib_bpool_individual_pool_${pool}_modpages"} = $4;
 
2483         $data->{"ib_bpool_individual_pool_${pool}_pending_reads"} = $5;
 
2484         $data->{"ib_bpool_individual_pool_${pool}_pending_writes_lru"} = $6;
 
2485         $data->{"ib_bpool_individual_pool_${pool}_pending_writes_flush"} = $7;
 
2486         $data->{"ib_bpool_individual_pool_${pool}_pending_writes_single"} = $8;
 
2489       m/\GPages read (\d+), created (\d+), written (\d+)\n/gc && do {
 
2490         $data->{"ib_bpool_individual_pool_${pool}_read"}    = $1;
 
2491         $data->{"ib_bpool_individual_pool_${pool}_created"} = $2;
 
2492         $data->{"ib_bpool_individual_pool_${pool}_written"} = $3;
 
2500 sub handle_incomplete_innodb_status {
 
2502     warn "Output from SHOW ENGINE INNODB STATUS was truncated. "
 
2503         . "This happens if the output of STATUS exceeds 64KB. "
 
2504         . "Several of the InnoDB graphs might be affected by this.";
 
2506     # FIX Is it possible to find some of the missing values from SHOW
 
2511 exit main() unless caller;