8 mysql_ - Munin plugin to display misc MySQL server status
10 =head1 APPLICABLE SYSTEMS
12 Any MySQL platform, tested by the authors on:
15 * 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/>
117 FIX point to relevant sections in the MySQL manual and other www
118 resources for each graph
122 =item mysql_replication
124 slave_io_running and slave_sql_running both translate the "Yes" values to 0 and
125 anything else to 1 for their respective fields in the "SHOW SLAVE STATUS" output.
126 This can be used to warn on slave failure if the warning and critical values
127 are set as seen in a previous section.
133 Copyright (C) 2008,2009 Kjell-Magne Øierud
135 This program is free software; you can redistribute it and/or modify
136 it under the terms of the GNU General Public License as published by
137 the Free Software Foundation; version 2 dated June, 1991.
139 This program is distributed in the hope that it will be useful, but
140 WITHOUT ANY WARRANTY; without even the implied warranty of
141 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
142 General Public License for more details.
144 You should have received a copy of the GNU General Public License along
145 with this program; if not, write to the Free Software Foundation, Inc.,
146 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
150 git-master + a few munin modifications
152 This plugin was downloaded from L<http://github.com/kjellm/munin-mysql/>
154 =head1 MAGICK MARKERS
157 #%# capabilities=suggest autoconf
167 use Math
::BigInt
; # Used to append "=> lib 'GMP'" here, but GMP caused
168 # segfault on some occasions. Removed as I don't
169 # think the tiny performance boost is worth the
171 use Storable
qw(nfreeze thaw);
178 eval 'require Cache::SharedMemoryCache';
179 $has_cache = $@ ? 0 : 1;
183 #---------------------------------------------------------------------
185 #---------------------------------------------------------------------
188 'dsn' => $ENV{'mysqlconnection'} || 'DBI:mysql:information_schema',
189 'user' => $ENV{'mysqluser'} || 'root',
190 'password' => $ENV{'mysqlpassword'} || '',
191 'cache_namespace' => $ENV{'cachenamespace'} || 'munin_mysql',
195 #---------------------------------------------------------------------
197 #---------------------------------------------------------------------
199 my %cache_options = (
200 'namespace' => $config{cache_namespace
},
201 'default_expires_in' => 60,
204 my $shared_memory_cache ;
207 $shared_memory_cache = Cache
::SharedMemoryCache-
>new(\
%cache_options)
208 or die("Couldn't instantiate SharedMemoryCache");
211 #---------------------------------------------------------------------
212 # G R A P H D E F I N I T I O N S
213 #---------------------------------------------------------------------
215 # These are defaults to save typing in the graph definitions
218 args
=> '--base 1000',
220 data_source_attrs
=> {
227 # %graphs contains the graph definitions, it is indexed on the graph
228 # name. The information stored for each graph is used for both showing
229 # data source values and for printing the graph configuration. Each
230 # graph follows the followingformat:
234 # # The global attributes for this graph
236 # # Attributes common to all data sources in this graph
237 # data_source_attrs => {}
240 # # NAME - The name of the data source (e.g. variable names
241 # # from SHOW STATUS)
242 # # DATA_SOURCE_ATTRS - key-value pairs with data source
244 # {name => 'NAME', (DATA_SOURCE_ATTRS)},
249 #---------------------------------------------------------------------
251 $graphs{bin_relay_log
} = {
254 title
=> 'Binary/Relay Logs',
255 vlabel
=> 'Log activity (txn/s)',
257 data_source_attrs
=> {
262 {name
=> 'Binlog_cache_disk_use', label
=> 'Binlog Cache Disk Use', info
=> 'Number of transactions which used a temporary disk cache because they could not fit in the regular binary log cache, being larger than binlog_cache_size'},
263 {name
=> 'Binlog_cache_use', label
=> 'Binlog Cache Use', info
=> 'Number of transaction which used the regular binary log cache, being smaller than binlog_cache_size'},
267 #---------------------------------------------------------------------
268 $graphs{binlog_space
} = {
271 title
=> 'Binary log space',
272 vlabel
=> 'Log space',
273 args
=> '--base 1024',
275 data_source_attrs
=> {
281 {name
=> 'ma_binlog_size', label
=> 'Binary Log Space'},
285 #-------------------------
286 $graphs{binlog_groupcommit
} = {
289 title
=> 'Binary Log Group Commits',
290 vlabel
=> 'Commits/Groups',
292 data_source_attrs
=> {
297 {name
=> 'Binlog_commits', label
=> 'Binlog commits'},
298 {name
=> 'Binlog_group_commits', label
=> 'Binlog Group Commits'},
299 {name
=> 'Binlog_group_commit_trigger_count', label
=> 'Binlog Groups because of binlog_commit_wait_count'},
300 {name
=> 'Binlog_group_commit_trigger_timeout', label
=> 'Binlog Groups because of binlog_commit_wait_usec'},
301 {name
=> 'Binlog_group_commit_trigger_lock_wait', label
=> 'Binlog Groups because of transactions'},
305 #---------------------------------------------------------------------
307 $graphs{commands
} = {
310 title
=> 'Command Counters',
311 vlabel
=> 'Commands per ${graph_period}',
312 total
=> 'Questions',
314 data_source_attrs
=> {},
317 {name
=> 'Com_delete', label
=> 'Delete'},
318 {name
=> 'Com_insert', label
=> 'Insert'},
319 {name
=> 'Com_insert_select', label
=> 'Insert select'},
320 {name
=> 'Com_load', label
=> 'Load Data'},
321 {name
=> 'Com_replace', label
=> 'Replace'},
322 {name
=> 'Com_replace_select', label
=> 'Replace select'},
323 {name
=> 'Com_select', label
=> 'Select'},
324 {name
=> 'Com_update', label
=> 'Update'},
325 {name
=> 'Com_update_multi', label
=> 'Update multi'},
329 #---------------------------------------------------------------------
331 $graphs{connections
} = {
334 title
=> 'Connections',
335 vlabel
=> 'Connections per ${graph_period}',
337 data_source_attrs
=> {
342 {name
=> 'max_connections', label
=> 'Max connections',
346 {name
=> 'Max_used_connections', label
=> 'Max used',
350 {name
=> 'Aborted_clients', label
=> 'Aborted clients'},
351 {name
=> 'Aborted_connects', label
=> 'Aborted connects'},
352 {name
=> 'Threads_connected', label
=> 'Threads connected',
354 {name
=> 'Threads_running', label
=> 'Threads running',
356 {name
=> 'Connections', label
=> 'New connections'},
360 #---------------------------------------------------------------------
362 $graphs{files_tables
} = {
365 title
=> 'Files and tables',
368 data_source_attrs
=> {
374 {name
=> 'table_open_cache', label
=> 'Table cache',
377 {name
=> 'Open_files', label
=> 'Open files'},
378 {name
=> 'Open_tables', label
=> 'Open tables'},
379 {name
=> 'Opened_tables', label
=> 'Opened tables',
385 #---------------------------------------------------------------------
387 $graphs{innodb_bpool
} = {
390 title
=> 'InnoDB Buffer Pool',
392 args
=> '--base 1024',
394 data_source_attrs
=> {
400 {name
=> 'ib_bpool_size', label
=> 'Buffer pool size',
403 {name
=> 'ib_bpool_dbpages', label
=> 'Database pages',
406 {name
=> 'ib_bpool_free', label
=> 'Free pages'},
407 {name
=> 'ib_bpool_modpages', label
=> 'Modified pages'},
411 #---------------------------------------------------------------------
413 $graphs{innodb_bpool_act
} = {
416 title
=> 'InnoDB Buffer Pool Activity',
417 vlabel
=> 'Activity per ${graph_period}',
420 data_source_attrs
=> {
425 {name
=> 'ib_bpool_read', label
=> 'Pages read'},
426 {name
=> 'ib_bpool_created', label
=> 'Pages created'},
427 {name
=> 'ib_bpool_written', label
=> 'Pages written'},
431 #---------------------------------------------------------------------
433 $graphs{innodb_insert_buf
} = {
436 title
=> 'InnoDB Insert Buffer',
437 vlabel
=> 'Activity per ${graph_period}',
439 data_source_attrs
=> {
444 {name
=> 'ib_ibuf_inserts', label
=> 'Inserts'},
445 {name
=> 'ib_ibuf_merged_rec', label
=> 'Merged Records'},
446 {name
=> 'ib_ibuf_merges', label
=> 'Merges'},
450 #---------------------------------------------------------------------
452 $graphs{innodb_io
} = {
455 title
=> 'InnoDB IO',
456 vlabel
=> 'IO operations per ${graph_period}',
458 data_source_attrs
=> {
463 {name
=> 'ib_io_read', label
=> 'File reads'},
464 {name
=> 'ib_io_write', label
=> 'File writes'},
465 {name
=> 'ib_io_log', label
=> 'Log writes'},
466 {name
=> 'ib_io_fsync', label
=> 'File syncs'},
470 #---------------------------------------------------------------------
472 $graphs{innodb_io_pend
} = {
475 title
=> 'InnoDB IO Pending',
476 vlabel
=> 'Pending operations',
478 data_source_attrs
=> {
483 {name
=> 'ib_iop_log', label
=> 'AIO Log'},
484 {name
=> 'ib_iop_sync', label
=> 'AIO Sync'},
485 {name
=> 'ib_iop_flush_bpool', label
=> 'Buf Pool Flush'},
486 {name
=> 'ib_iop_flush_log', label
=> 'Log Flushes'},
487 {name
=> 'ib_iop_ibuf_aio', label
=> 'Insert Buf AIO Read'},
488 {name
=> 'ib_iop_aioread', label
=> 'Normal AIO Reads'},
489 {name
=> 'ib_iop_aiowrite', label
=> 'Normal AIO Writes'},
493 #---------------------------------------------------------------------
495 $graphs{innodb_log
} = {
498 title
=> 'InnoDB Log',
499 vlabel
=> 'Log activity per ${graph_period}',
501 data_source_attrs
=> {
506 {name
=> 'innodb_log_buffer_size', label
=> 'Buffer Size',
510 {name
=> 'ib_log_flush', label
=> 'KB Flushed'},
511 {name
=> 'ib_log_written', label
=> 'KB Written'},
515 #---------------------------------------------------------------------
517 $graphs{innodb_rows
} = {
520 title
=> 'InnoDB Row Operations',
521 vlabel
=> 'Operations per ${graph_period}',
524 data_source_attrs
=> {},
527 {name
=> 'Innodb_rows_deleted', label
=> 'Deletes'},
528 {name
=> 'Innodb_rows_inserted', label
=> 'Inserts'},
529 {name
=> 'Innodb_rows_read', label
=> 'Reads'},
530 {name
=> 'Innodb_rows_updated', label
=> 'Updates'},
534 #---------------------------------------------------------------------
536 $graphs{innodb_semaphores
} = {
539 title
=> 'InnoDB Semaphores',
540 vlabel
=> 'Semaphores per ${graph_period}',
542 data_source_attrs
=> {
547 {name
=> 'ib_spin_rounds', label
=> 'Spin Rounds'},
548 {name
=> 'ib_spin_waits', label
=> 'Spin Waits'},
549 {name
=> 'ib_os_waits', label
=> 'OS Waits'},
553 #---------------------------------------------------------------------
555 $graphs{innodb_tnx
} = {
558 title
=> 'InnoDB Transactions',
559 vlabel
=> 'Transactions per ${graph_period}',
561 data_source_attrs
=> {
566 {name
=> 'ib_tnx', label
=> 'Transactions created'},
570 #---------------------------------------------------------------------
572 $graphs{myisam_indexes
} = {
575 title
=> 'MyISAM Indexes',
576 vlabel
=> 'Requests per ${graph_period}',
578 data_source_attrs
=> {
583 {name
=> 'Key_read_requests', label
=> 'Key read requests'},
584 {name
=> 'Key_reads', label
=> 'Key reads'},
585 {name
=> 'Key_write_requests', label
=> 'Key write requests'},
586 {name
=> 'Key_writes', label
=> 'Key writes'},
590 #---------------------------------------------------------------------
592 $graphs{network_traffic
} = {
595 title
=> 'Network Traffic',
596 args
=> '--base 1024',
597 vlabel
=> 'Bytes received (-) / sent (+) per ${graph_period}',
599 data_source_attrs
=> {
604 {name
=> 'Bytes_received', label
=> 'Bytes transfered',
606 {name
=> 'Bytes_sent', label
=> 'Bytes transfered',
607 negative
=> 'Bytes_received'},
611 #---------------------------------------------------------------------
616 title
=> 'Query Cache',
617 vlabel
=> 'Commands per ${graph_period}',
619 data_source_attrs
=> {
624 {name
=> 'Qcache_queries_in_cache', label
=> 'Queries in cache'},
625 {name
=> 'Qcache_hits', label
=> 'Cache hits'},
626 {name
=> 'Qcache_inserts', label
=> 'Inserts'},
627 {name
=> 'Qcache_not_cached', label
=> 'Not cached'},
628 {name
=> 'Qcache_lowmem_prunes', label
=> 'Low-memory prunes'},
632 #---------------------------------------------------------------------
634 $graphs{qcache_mem
} = {
637 title
=> 'Query Cache Memory',
639 args
=> '--base 1024 --lower-limit 0',
641 data_source_attrs
=> {
647 {name
=> 'query_cache_size', label
=> 'Cache size'},
648 {name
=> 'Qcache_free_memory', label
=> 'Free mem'},
652 #---------------------------------------------------------------------
654 $graphs{replication
} = {
657 title
=> 'Replication',
658 vlabel
=> 'Activity',
660 data_source_attrs
=> {
665 {name
=> 'slave_io_running', label
=> 'Slave IO Running',
668 {name
=> 'slave_sql_running', label
=> 'Slave SQL Running',
671 {name
=> 'Slave_retried_transactions', label
=> 'Retried Transactions'},
672 {name
=> 'Slave_open_temp_tables', label
=> 'Open Temp Tables'},
673 {name
=> 'seconds_behind_master', label
=> 'Secs Behind Master',
678 #---------------------------------------------------------------------
680 $graphs{select_types
} = {
683 title
=> 'Select types',
684 vlabel
=> 'Commands per ${graph_period}',
687 data_source_attrs
=> {},
690 {name
=> 'Select_full_join', label
=> 'Full join'},
691 {name
=> 'Select_full_range_join', label
=> 'Full range'},
692 {name
=> 'Select_range', label
=> 'Range'},
693 {name
=> 'Select_range_check', label
=> 'Range check'},
694 {name
=> 'Select_scan', label
=> 'Scan'},
698 #---------------------------------------------------------------------
703 title
=> 'Slow Queries',
704 vlabel
=> 'Slow queries per ${graph_period}',
706 data_source_attrs
=> {
711 {name
=> 'Slow_queries', label
=> 'Slow queries'},
715 #---------------------------------------------------------------------
721 vlabel
=> 'Sorts / ${graph_period}',
723 data_source_attrs
=> {
728 {name
=> 'Sort_rows', label
=> 'Rows sorted'},
729 {name
=> 'Sort_range', label
=> 'Range'},
730 {name
=> 'Sort_merge_passes', label
=> 'Merge passes'},
731 {name
=> 'Sort_scan', label
=> 'Scan'},
735 #---------------------------------------------------------------------
737 $graphs{table_locks
} = {
740 title
=> 'Table locks',
741 vlabel
=> 'locks per ${graph_period}',
743 data_source_attrs
=> {
748 {name
=> 'Table_locks_immediate', label
=> 'Table locks immed'},
749 {name
=> 'Table_locks_waited', label
=> 'Table locks waited'},
753 #---------------------------------------------------------------------
755 $graphs{tmp_tables
} = {
758 title
=> 'Temporary objects',
759 vlabel
=> 'Objects per ${graph_period}',
761 data_source_attrs
=> {
766 {name
=> 'Created_tmp_disk_tables', label
=> 'Temp disk tables'},
767 {name
=> 'Created_tmp_tables', label
=> 'Temp tables'},
768 {name
=> 'Created_tmp_files', label
=> 'Temp files'},
772 #---------------------------------------------------------------------
774 # These are mysql plugins of type INFORMATION SCHEMA
776 # These will be added to $graphs if available
777 #---------------------------------------------------------------------
779 my %graph_plugins = ();
781 $graph_plugins{query_response_time
} = {
785 title
=> 'Query Response Time Count',
786 vlabel
=> 'queries per ${graph_period}',
788 data_source_attrs
=> {
793 # data_sources are populated by sub plugin_query_response_time
800 title
=> 'Query Response Time Total',
801 vlabel
=> 'query time (microseconds) per ${graph_period}',
803 data_source_attrs
=> {
808 # data_sources are populated by sub plugin_query_response_time
814 $graph_plugins{user_statistics
} = {
818 title
=> 'User Connections',
819 vlabel
=> 'connections per ${graph_period}',
821 data_source_attrs
=> {
826 cols
=> { 'total_connections' => {}, 'concurrent_connections' => {}, 'denied_connections' => {}, 'lost_connections' => {}},
833 title
=> 'User Time',
836 data_source_attrs
=> {
841 cols
=> { 'connected_time' => {}, 'busy_time' => {}, 'cpu_time' => {} },
848 title
=> 'User Bytes',
851 data_source_attrs
=> {
856 cols
=> { 'bytes_received' => {}, 'bytes_sent' => {}, 'binlog_bytes_written' => {} },
863 title
=> 'User Rows',
866 data_source_attrs
=> {
871 cols
=> { 'rows_read' => {}, 'rows_sent' => {}, 'rows_deleted' => {}, 'rows_inserted' => {}, 'rows_updated' => {} },
878 title
=> 'Command breakdown by user',
879 vlabel
=> 'commands',
881 data_source_attrs
=> {
886 cols
=> { 'select_commands' => {}, 'update_commands' => {}, 'other_commands' => {}, 'commit_transactions' => {}, 'rollback_transactions' => {} },
892 #---------------------------------------------------------------------
894 #---------------------------------------------------------------------
898 # Global hash holding the data collected from mysql.
900 our $data; # Was 'my'. Changed to 'our' to facilitate testing.
904 my $graph = basename
($0);
905 $graph =~ s/^mysql[0-9]*_//g; # allow multiple instances
906 my $command = $ARGV[0] || 'show';
909 'autoconf' => \
&autoconf
,
910 'config' => \
&config
,
912 'suggest' => \
&suggest
,
915 die "Unknown command: $command"
916 unless exists $command_map{$command};
918 die "Missing dependency Cache::Cache"
919 unless $has_cache || $command eq 'autoconf';
921 return $command_map{$command}->($graph);
925 #---------------------------------------------------------------------
926 # C O M M A N D H A N D L E R S
927 #---------------------------------------------------------------------
929 # Each command handler should return an appropriate exit code
932 # http://munin-monitoring.org/wiki/ConcisePlugins#autoconf
934 unless ($has_cache) {
935 print "no (Missing dependency Cache::Cache)\n";
944 $err =~ s{\s at \s \S+ \s line .*}{}xms;
953 # http://munin-monitoring.org/wiki/ConcisePlugins#suggest
956 # What is the best way to decide which graphs is applicable to a
959 # Does the database use InnoDB? A zero count from:
963 # WHERE table_type = 'base table'
964 # AND engine = 'innodb'
966 # Does the database use binary logs? 'OFF' as the result from:
968 # SHOW GLOBAL variables LIKE 'log_bin'
970 # Is the database setup as a slave? Empty result from:
974 foreach my $graph (sort keys(%graphs)) {
975 next if $graph =~ /innodb_/ && $data->{_innodb_disabled
};
976 next if $graph =~ /wsrep_/ && $data->{_galera_disabled
};
985 my $graph_name = shift;
987 # In MySQL 5.1 (and probably erlier versions as well) status
988 # variables are unique when looking at the last 19 characters.
990 # SELECT RIGHT(variable_name, 19), COUNT(*)
991 # FROM information_schema.global_status
992 # GROUP BY RIGHT(variable_name, 19)
993 # HAVING COUNT(*) > 1;
995 # Empty set (0.06 sec)
997 # There is one duplicate when looking at server variables
999 # SELECT RIGHT(variable_name, 19), COUNT(*)
1000 # FROM information_schema.global_variables
1001 # GROUP BY RIGHT(variable_name, 19)
1002 # HAVING COUNT(*) > 1;
1004 # +--------------------------+----------+
1005 # | RIGHT(variable_name, 19) | COUNT(*) |
1006 # +--------------------------+----------+
1007 # | OW_PRIORITY_UPDATES | 2 |
1008 # +--------------------------+----------+
1009 # 1 row in set (0.05 sec)
1011 # show global variables like '%OW_PRIORITY_UPDATES';
1013 # +--------------------------+-------+
1014 # | Variable_name | Value |
1015 # +--------------------------+-------+
1016 # | low_priority_updates | OFF |
1017 # | sql_low_priority_updates | OFF |
1018 # +--------------------------+-------+
1019 # 2 rows in set (0.00 sec)
1021 # Not a problem since we don't graph these
1025 die 'Unknown graph ' . ($graph_name ? $graph_name : '')
1026 unless $graphs{$graph_name};
1028 my $graph = $graphs{$graph_name};
1030 my %conf = (%{$defaults{global_attrs
}}, %{$graph->{config
}{global_attrs
}});
1031 while (my ($k, $v) = each %conf) {
1032 print "graph_$k $v\n";
1034 print "graph_category mysql2\n";
1036 for my $ds (@{$graph->{data_sources
}}) {
1038 %{$defaults{data_source_attrs
}},
1039 %{$graph->{config
}{data_source_attrs
}},
1042 while (my ($k, $v) = each %ds_spec) {
1043 # 'name' is only used internally in this script, not understood by munin.
1044 printf("%s.%s %s\n", clean_fieldname
($ds->{name
}), $k, $v) unless ($k eq 'name');
1046 print_thresholds
(clean_fieldname
($ds->{name
}));
1053 my $graph_name = shift;
1057 die 'Unknown graph ' . ($graph_name ? $graph_name : '')
1058 unless $graphs{$graph_name};
1060 my $graph = $graphs{$graph_name};
1062 die "Can't show data for '$graph_name' because InnoDB is disabled."
1063 if $graph_name =~ /innodb_/ && $data->{_innodb_disabled
};
1065 for my $ds (@{$graph->{data_sources
}}) {
1066 my $value = exists $ds->{value
}
1067 ? $ds->{value
}($data)
1068 : $data->{$ds->{name
}};
1070 printf "%s.value %s\n", clean_fieldname
($ds->{name
}), defined($value) ? $value : 'U';
1078 #---------------------------------------------------------------------
1079 # U T I L I T Y S U B S
1080 #---------------------------------------------------------------------
1084 my $dsn = "$config{dsn};mysql_connect_timeout=5";
1086 return DBI-
>connect($dsn, $config{user
}, $config{password
}, {
1089 FetchHashKeyName
=> 'NAME_lc',
1095 $data = $shared_memory_cache->get('data');
1096 my $graphs_stored = $shared_memory_cache->get('graphs');
1097 %graphs = %{thaw
($graphs_stored)} if $graphs_stored;
1100 #warn "Need to update cache";
1104 my $dbh = db_connect
();
1106 # Set up defaults in case the server is not a slave
1107 $data->{relay_log_space
} = 0;
1108 $data->{slave_running
} = 0;
1109 $data->{slave_stopped
} = 0;
1111 # Set up defaults in case binlog is not enabled
1112 $data->{ma_binlog_size
} = 0;
1114 update_variables
($dbh);
1115 update_plugins
($dbh);
1116 update_innodb
($dbh);
1117 update_master
($dbh);
1120 $shared_memory_cache->set('data', $data);
1121 $shared_memory_cache->set('graphs', nfreeze
(\
%graphs));
1125 sub update_plugins
{
1129 'query_response_time' => \
&plugin_query_response_time
,
1133 my ($f, $sec, $dbh, %g) = @_;
1134 if ($f->($dbh) == 0) {
1135 while (my ($k, $v) = each %g) {
1136 $graphs{$sec . '_' . $k} = $v;
1141 my $sth = $dbh->prepare("SHOW PLUGINS");
1143 while (my $row = $sth->fetchrow_hashref()) {
1144 next if $row->{'type'} ne 'INFORMATION SCHEMA';
1145 my $sec = lc $row->{'name'};
1146 next if not exists $plugin_map{$sec};
1147 add_graphs
($plugin_map{$sec}, $sec, $dbh, %{$graph_plugins{$sec}});
1152 'user_statistics' => \
&is_user_statistics
,
1155 $sth = $dbh->prepare("SHOW TABLES IN INFORMATION_SCHEMA");
1157 while (my $row = $sth->fetchrow_hashref()) {
1158 my $sec = lc $row->{'tables_in_information_schema'};
1159 next if not exists $is_map{$sec};
1160 add_graphs
($is_map{$sec}, $sec, $dbh, %{$graph_plugins{$sec}});
1165 sub update_variables
{
1168 'SHOW GLOBAL STATUS',
1169 'SHOW GLOBAL VARIABLES',
1172 my %variable_name_map = (
1173 table_cache
=> 'table_open_cache', # table_open_cache was
1174 # previously known as
1175 # table_cache in MySQL
1176 # 5.1.2 and earlier.
1179 for my $query (@queries) {
1180 $data->{$query} = {};
1182 my $sth = $dbh->prepare($query);
1184 while (my $row = $sth->fetch) {
1185 my $var = $variable_name_map{$row->[0]} || $row->[0];
1186 $data->{$var} = $row->[1];
1196 my $sth = $dbh->prepare('SHOW /*!50000 ENGINE*/ INNODB STATUS');
1201 if ($@ =~ /Unknown (storage|table) engine 'INNODB'|Cannot call SHOW INNODB STATUS because skip-innodb is defined/i) {
1202 $data->{_innodb_disabled
} = 1;
1207 my $row = $sth->fetchrow_hashref();
1208 my $status = $row->{'status'};
1211 parse_innodb_status
($status);
1218 my $sth = $dbh->prepare('SHOW MASTER LOGS');
1223 # SHOW MASTER LOGS failed becuase binlog is not enabled
1224 return if $@ =~ /You are not using binary logging/;
1228 while (my $row = $sth->fetch) {
1229 $data->{ma_binlog_size
} += $row->[1];
1239 my $sth = $dbh->prepare('SHOW SLAVE STATUS');
1241 my $row = $sth->fetchrow_hashref();
1243 while (my ($k, $v) = each %$row) {
1248 # undef when slave is stopped, or when MySQL fails to calculate
1249 # the lag (which happens depresingly often). (mk-heartbeat fixes
1251 $data->{seconds_behind_master
} ||= 0;
1253 # Track these two fields so we can trigger warnings if the slave stops
1255 $data->{slave_sql_running
} = ($data->{slave_sql_running
} eq 'Yes')
1257 $data->{slave_io_running
} = ($data->{slave_io_running
} eq 'Yes')
1263 #---------------------------------------------------------------------
1264 # Information SCHEMA tables represent data to be processed
1265 #---------------------------------------------------------------------
1268 sub plugin_query_response_time
{
1271 return 1 if not defined $data->{query_response_time_stats
};
1272 return 1 if $data->{query_response_time_stats
} eq 'OFF';
1274 my $sth = $dbh->prepare("SELECT * FROM INFORMATION_SCHEMA.QUERY_RESPONSE_TIME");
1276 while (my $row = $sth->fetchrow_hashref()) {
1277 my $time = $row->{'time'};
1278 $data->{'query_response_time_count_' . $time} = $row->{'count'};
1279 push @{$graph_plugins{query_response_time
}->{count
}->{data_sources
}}, {name
=> 'query_response_time_count_' . $time, label
=> $time };
1280 next if $row->{'total'} eq 'TOO LONG';
1281 $data->{'query_response_time_total_' . $time} = $row->{'total'} * 1e6;
1282 push @{$graph_plugins{query_response_time
}->{total
}->{data_sources
}}, {name
=> 'query_response_time_total_' . $time, label
=> $time };
1289 sub is_user_statistics
{
1292 return 1 if not defined $data->{userstat
};
1293 return 1 if $data->{userstat
} eq 'OFF';
1295 my $sth = $dbh->prepare("SELECT * FROM INFORMATION_SCHEMA.USER_STATISTICS");
1297 while (my $row = $sth->fetchrow_hashref()) {
1298 my $user = $row->{'user'};
1300 while (my ($g, $v) = each %{$graph_plugins{user_statistics
}}) {
1301 while (my ($userstat,$conf) = each %{$v->{cols
}}) {
1302 $var = 'user_stats_' . $user . '_' . $userstat;
1303 $data->{$var} = int $row->{$userstat};
1304 my $ds = { %$conf };
1306 $ds->{label
} = $user . ' ' . $userstat;
1307 push @{$graph_plugins{user_statistics
}->{$g}->{data_sources
}}, $ds;
1316 # In 'SHOW ENGINE INNODB STATUS' 64 bit integers are not formated as
1317 # plain integers. They are either:
1319 # - split in two and needs to be shifted together,
1326 ? Math
::BigInt-
>new($x)->blsft(32) + $y
1327 : Math
::BigInt-
>new("0x$x");
1330 #---------------------------------------------------------------------
1331 # P A R S E 'SHOW ENGINE INNODB STATUS' O U T P U T
1332 #---------------------------------------------------------------------
1335 # A nice walk through
1336 # http://www.mysqlperformanceblog.com/2006/07/17/show-innodb-status-walk-through/
1338 # The parsing is split in one subrutine per section. Each subroutine
1339 # should parse a block with the following structure
1345 sub parse_innodb_status
{
1348 # Add a dummy section to the end in case the innodb status output
1349 # has been truncated (Happens for status > 64K characters)
1350 $_ .= "\n----------\nDUMMY\n";
1354 'BUFFER POOL AND MEMORY' => \
&parse_buffer_pool_and_memory
,
1355 'INDIVIDUAL BUFFER POOL INFO' => \
&skip
,
1356 'FILE I/O' => \
&parse_file_io
,
1357 'INSERT BUFFER AND ADAPTIVE HASH INDEX'
1358 => \
&parse_insert_buffer_and_adaptive_hash_index
,
1359 'LATEST DETECTED DEADLOCK' => \
&skip
,
1360 'LATEST FOREIGN KEY ERROR' => \
&skip
,
1361 'LOG' => \
&parse_log
,
1362 'ROW OPERATIONS' => \
&skip
,
1363 'SEMAPHORES' => \
&parse_semaphores
,
1364 'TRANSACTIONS' => \
&parse_transactions
,
1365 'BACKGROUND THREAD' => \
&skip
,
1373 last if $sec eq 'END OF INNODB MONITOR OUTPUT';
1374 if ($sec eq 'DUMMY') {
1375 handle_incomplete_innodb_status
();
1379 die "Unknown section: $1" unless exists $section_map{$sec};
1380 die "Parse error. Expected a section separator" unless m/\G-+\n/gc;
1382 $section_map{$sec}->();
1387 # This regular expression handles the different formating of 64-bit
1388 # integers in different versions of the innodb engine. Either two
1389 # decimal 32-bit integers seperated by a space, or a single
1390 # hexadecimal 64-bit integer.
1391 my $innodb_bigint_rx = qr{([[a-fA-F\d]+)(?: (\d+))?};
1394 sub match_dashes
{ return m/\G-+\n(?!-)/gc; }
1397 sub skip_line
{ return m/\G.*\n/gc; }
1401 # Heading is 6 lines
1402 for my $foo (1...6) {
1403 skip_line
or die('Parse error');
1411 #warn substr($_, pos(), 10);
1413 return if match_dashes
();
1414 next if $parser->();
1420 sub skip
{ parse_section
(sub {}); }
1423 sub parse_semaphores
{
1426 m/\GMutex spin waits (\d+), rounds (\d+), OS waits (\d+)\n/gc && do {
1427 $data->{ib_spin_waits
} = $1;
1428 $data->{ib_spin_rounds
} = $2;
1429 $data->{ib_os_waits
} = $3;
1437 sub parse_transactions
{
1440 m/\GTrx id counter $innodb_bigint_rx\n/gc && do {
1441 $data->{ib_tnx
} = innodb_bigint
($1, $2);
1444 m/\GPurge done for trx's n:o < $innodb_bigint_rx undo n:o < $innodb_bigint_rx\n/gc && do {
1447 $data->{ib_tnx_prg
} = innodb_bigint
($1, $2);
1448 # FIX add to data? innodb_bigint($3, $4);
1452 $data->{ib_tnx_prg
} = innodb_bigint
($1);
1453 # FIX add to data? innodb_bigint($2);
1457 m/\GHistory list length (\d+)\n/gc && do {
1458 $data->{ib_tnx_hist
} = $1;
1470 m/\GPending normal aio reads: (\d+), aio writes: (\d+),\n\s*ibuf aio reads: (\d+), log i\/o's: (\d+), sync i\/o's
: (\d
+)\n/gc
&& do {
1471 $data->{ib_iop_aioread
} = $1;
1472 $data->{ib_iop_aiowrite
} = $2;
1473 $data->{ib_iop_ibuf_aio
} = $3;
1474 $data->{ib_iop_log
} = $4;
1475 $data->{ib_iop_sync
} = $5;
1478 m/\GPending flushes \(fsync\) log: (\d+); buffer pool: (\d+)\n/gc && do {
1479 $data->{ib_iop_flush_log
} = $1;
1480 $data->{ib_iop_flush_bpool
} = $2;
1483 m/\G(\d+) OS file reads, (\d+) OS file writes, (\d+) OS fsyncs\n/gc && do {
1484 $data->{ib_io_read
} = $1;
1485 $data->{ib_io_write
} = $2;
1486 $data->{ib_io_fsync
} = $3;
1494 sub parse_insert_buffer_and_adaptive_hash_index
{
1498 m/\G(\d+) inserts, (\d+) merged recs, (\d+) merges\n/gc && do {
1499 $data->{ib_ibuf_inserts
} = $1;
1500 $data->{ib_ibuf_merged_rec
} = $2;
1501 $data->{ib_ibuf_merges
} = $3;
1505 m/\Gmerged operations:\n insert (\d+), delete mark \d+, delete \d+\ndiscarded operations:\n insert (\d+), delete mark \d+, delete \d+\n/gc && do {
1506 $data->{ib_ibuf_inserts
} = $1;
1507 $data->{ib_ibuf_merged_rec
} = $1 + $2;
1510 m/\GIbuf: size (\d+), free list len (\d+), seg size (\d+),(?: (\d+) merges)?\n/gc && do {
1511 $data->{ib_ibuf_size
} = $1;
1512 $data->{ib_ibuf_free_len
} = $2;
1513 $data->{ib_ibuf_seg_size
} = $3;
1514 $data->{ib_ibuf_merges
} = $4 if defined $4; # MySQL >= 5.5
1525 m/\GLog sequence number $innodb_bigint_rx\n/gc && do {
1526 $data->{ib_log_written
} = innodb_bigint
($1, $2);
1529 m/\GLog flushed up to\s+$innodb_bigint_rx\n/gc && do {
1530 $data->{ib_log_flush
} = innodb_bigint
($1, $2);
1533 m/\G(\d+) log i\/o's done.*\n/gc && do {
1534 $data->{ib_io_log} = $1;
1542 sub parse_buffer_pool_and_memory {
1545 m/\GBuffer pool size\s+(\d+)\n/gc && do {
1546 $data->{ib_bpool_size} = $1;
1549 m/\GFree buffers\s+(\d+)\n/gc && do {
1550 $data->{ib_bpool_free} = $1;
1553 m/\GDatabase pages\s+(\d+)\n/gc && do {
1554 $data->{ib_bpool_dbpages} = $1;
1557 m/\GModified db pages\s+(\d+)\n/gc && do {
1558 $data->{ib_bpool_modpages} = $1;
1561 m/\GPages read (\d+), created (\d+), written (\d+)\n/gc && do {
1562 $data->{ib_bpool_read} = $1;
1563 $data->{ib_bpool_created} = $2;
1564 $data->{ib_bpool_written} = $3;
1572 sub handle_incomplete_innodb_status {
1574 warn "Output from SHOW ENGINE INNDOB STATUS was truncated. "
1575 . "This happens if the output of SEIS exceeds 64KB. "
1576 . "Several of the InnoDB graphs might be affected by this.";
1578 # FIX Is it possible to find some of the missing values from SHOW
1583 exit main() unless caller;