]> jfr.im git - munin-plugins.git/blob - mysql_
support for a 2nd unbound plugin
[munin-plugins.git] / mysql_
1 #!/usr/bin/perl
2 # -*- perl -*-
3
4 =encoding utf8
5
6 =head1 NAME
7
8 mysql_ - Munin plugin to display misc MySQL server status
9
10 =head1 APPLICABLE SYSTEMS
11
12 Any MySQL platform, tested by the authors on:
13 * MySQL 5.0.51
14 * MariaDB 5.5.39
15 * MariaDB-5.5.39(galera).
16 * MySQL 5.6.12
17 * MariaDB 10.0.18
18
19 Plugins:
20 * MariaDB-10 Query Response Time: https://mariadb.com/kb/en/mariadb/query_response_time-plugin/
21
22 Information Schema tables:
23 * User statistics - MariaDB-5.2+, OurDelta, Percona Server - https://mariadb.com/kb/en/mariadb/user-statistics
24
25 =head1 CONFIGURATION
26
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.
30
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.
35
36 To get a list of symlinks that can be created, run:
37
38 ./mysql_ suggest
39
40 In addition you might need to specify connection parameters in the
41 plugin configuration to override the defaults. These are the defaults:
42
43 [mysql_*]
44 env.mysqlconnection DBI:mysql:information_schema
45 env.mysqluser root
46
47 Non-default example:
48
49 [mysql_*]
50 env.mysqlconnection DBI:mysql:information_schema;host=127.0.0.1;port=3306
51 env.mysqluser munin
52 env.mysqlpassword geheim
53 env.cachenamespace munin_mysql_pri
54 [mysql2_*]
55 env.mysqlconnection DBI:mysql:information_schema;host=127.0.0.1;port=13306
56 env.mysqluser munin
57 env.mysqlpassword ryuWyawEv
58 env.cachenamespace munin_mysql_alt
59 [mysql10_*]
60 user munin
61 env.mysqluser munin
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
65 # be specified here.
66
67 Creating a munin user:
68
69 CREATE USER 'munin'@'localhost' IDENTIFIED BY 'ryuWyawEv';
70
71 or with a unix_socket plugin (INSTALL PLUGIN unix_socket SONAME 'auth_socket')
72
73 CREATE USER 'munin'@'localhost' IDENTIFIED WITH unix_socket;
74
75 Note: requires 'user munin' in the configuration.
76
77 The minimum required priviledges of the munin database user is:
78
79 GRANT PROCESS, REPLICATION CLIENT ON *.* TO 'munin'@'localhost';
80
81
82 Warning and critical values can be set via the environment in the usual way.
83 For example:
84
85 [mysql_replication]
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
90
91 =head1 DEPENDENCIES
92
93 =over
94
95 =item Cache::Cache
96
97 The plugin uses shared memory to cache the statistics gathered from
98 MySQL. This ensures minimal inpact on the MySQL server.
99
100 =item DBD::mysql
101
102 =back
103
104 =head1 INTERPRETATION
105
106 =head2 InnoDB
107
108 The statistics from innodb are mainly collected from the command
109
110 SHOW ENGINE INNODB STATUS
111
112 A nice walk through is found at
113 L<http://www.mysqlperformanceblog.com/2006/07/17/show-innodb-status-walk-through/>
114
115 =head2 The graphs
116
117 FIX point to relevant sections in the MySQL manual and other www
118 resources for each graph
119
120 =over
121
122 =item mysql_replication
123
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.
128
129 =back
130
131 =head1 LICENSE
132
133 Copyright (C) 2008,2009 Kjell-Magne Øierud
134
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.
138
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.
143
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.
147
148 =head1 VERSION
149
150 git-master + a few munin modifications
151
152 This plugin was downloaded from L<http://github.com/kjellm/munin-mysql/>
153
154 =head1 MAGICK MARKERS
155
156 #%# family=auto
157 #%# capabilities=suggest autoconf
158
159 =cut
160
161 use warnings;
162 use strict;
163 use utf8;
164
165 use DBI;
166 use File::Basename;
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
170 # debugging effort.
171 use Storable qw(nfreeze thaw);
172
173 use Munin::Plugin;
174
175 my $has_cache;
176
177 BEGIN {
178 eval 'require Cache::SharedMemoryCache';
179 $has_cache = $@ ? 0 : 1;
180 }
181
182
183 #---------------------------------------------------------------------
184 # C O N F I G
185 #---------------------------------------------------------------------
186
187 my %config = (
188 'dsn' => $ENV{'mysqlconnection'} || 'DBI:mysql:information_schema',
189 'user' => $ENV{'mysqluser'} || 'root',
190 'password' => $ENV{'mysqlpassword'} || '',
191 'cache_namespace' => $ENV{'cachenamespace'} || 'munin_mysql',
192 );
193
194
195 #---------------------------------------------------------------------
196 # C A C H E
197 #---------------------------------------------------------------------
198
199 my %cache_options = (
200 'namespace' => $config{cache_namespace},
201 'default_expires_in' => 60,
202 );
203
204 my $shared_memory_cache ;
205 if ($has_cache)
206 {
207 $shared_memory_cache = Cache::SharedMemoryCache->new(\%cache_options)
208 or die("Couldn't instantiate SharedMemoryCache");
209 }
210
211 #---------------------------------------------------------------------
212 # G R A P H D E F I N I T I O N S
213 #---------------------------------------------------------------------
214
215 # These are defaults to save typing in the graph definitions
216 my %defaults = (
217 global_attrs => {
218 args => '--base 1000',
219 },
220 data_source_attrs => {
221 min => '0',
222 type => 'DERIVE',
223 draw => 'AREASTACK',
224 },
225 );
226
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:
231 #
232 # $graph{NAME} => {
233 # config => {
234 # # The global attributes for this graph
235 # global_attrs => {}
236 # # Attributes common to all data sources in this graph
237 # data_source_attrs => {}
238 # },
239 # data_sources => [
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
243 # # attributes
244 # {name => 'NAME', (DATA_SOURCE_ATTRS)},
245 # {...},
246 # ],
247 my %graphs = ();
248
249 #---------------------------------------------------------------------
250
251 $graphs{bin_relay_log} = {
252 config => {
253 global_attrs => {
254 title => 'Binary/Relay Logs',
255 vlabel => 'Log activity (txn/s)',
256 },
257 data_source_attrs => {
258 draw => 'LINE1',
259 },
260 },
261 data_sources => [
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'},
264 ],
265 };
266
267 #---------------------------------------------------------------------
268 $graphs{binlog_space} = {
269 config => {
270 global_attrs => {
271 title => 'Binary log space',
272 vlabel => 'Log space',
273 args => '--base 1024',
274 },
275 data_source_attrs => {
276 draw => 'LINE1',
277 type => 'GAUGE',
278 },
279 },
280 data_sources => [
281 {name => 'ma_binlog_size', label => 'Binary Log Space'},
282 ],
283 };
284
285 #-------------------------
286 $graphs{binlog_groupcommit} = {
287 config => {
288 global_attrs => {
289 title => 'Binary Log Group Commits',
290 vlabel => 'Commits/Groups',
291 },
292 data_source_attrs => {
293 draw => 'LINE1',
294 },
295 },
296 data_sources => [
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'},
302 ],
303 };
304
305 #---------------------------------------------------------------------
306
307 $graphs{commands} = {
308 config => {
309 global_attrs => {
310 title => 'Command Counters',
311 vlabel => 'Commands per ${graph_period}',
312 total => 'Questions',
313 },
314 data_source_attrs => {},
315 },
316 data_sources => [
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'},
326 ],
327 };
328
329 #---------------------------------------------------------------------
330
331 $graphs{connections} = {
332 config => {
333 global_attrs => {
334 title => 'Connections',
335 vlabel => 'Connections per ${graph_period}',
336 },
337 data_source_attrs => {
338 draw => 'LINE1',
339 },
340 },
341 data_sources => [
342 {name => 'max_connections', label => 'Max connections',
343 type => 'GAUGE',
344 draw => 'AREA',
345 colour => 'cdcfc4'},
346 {name => 'Max_used_connections', label => 'Max used',
347 type => 'GAUGE',
348 draw => 'AREA',
349 colour => 'ffd660'},
350 {name => 'Aborted_clients', label => 'Aborted clients'},
351 {name => 'Aborted_connects', label => 'Aborted connects'},
352 {name => 'Threads_connected', label => 'Threads connected',
353 type => 'GAUGE'},
354 {name => 'Threads_running', label => 'Threads running',
355 type => 'GAUGE'},
356 {name => 'Connections', label => 'New connections'},
357 ],
358 };
359
360 #---------------------------------------------------------------------
361
362 $graphs{files_tables} = {
363 config => {
364 global_attrs => {
365 title => 'Files and tables',
366 vlabel => 'Tables',
367 },
368 data_source_attrs => {
369 type => 'GAUGE',
370 draw => 'LINE1',
371 },
372 },
373 data_sources => [
374 {name => 'table_open_cache', label => 'Table cache',
375 draw => 'AREA',
376 colour => 'cdcfc4'},
377 {name => 'Open_files', label => 'Open files'},
378 {name => 'Open_tables', label => 'Open tables'},
379 {name => 'Opened_tables', label => 'Opened tables',
380 type => 'DERIVE',
381 min => 0},
382 ],
383 };
384
385 #---------------------------------------------------------------------
386
387 $graphs{innodb_bpool} = {
388 config => {
389 global_attrs => {
390 title => 'InnoDB Buffer Pool',
391 vlabel => 'Pages',
392 args => '--base 1024',
393 },
394 data_source_attrs => {
395 draw => 'LINE2',
396 type => 'GAUGE',
397 },
398 },
399 data_sources => [
400 {name => 'ib_bpool_size', label => 'Buffer pool size',
401 draw => 'AREA',
402 colour => 'ffd660'},
403 {name => 'ib_bpool_dbpages', label => 'Database pages',
404 draw => 'AREA',
405 colour => 'cdcfc4'},
406 {name => 'ib_bpool_free', label => 'Free pages'},
407 {name => 'ib_bpool_modpages', label => 'Modified pages'},
408 ],
409 };
410
411 #---------------------------------------------------------------------
412
413 $graphs{innodb_bpool_act} = {
414 config => {
415 global_attrs => {
416 title => 'InnoDB Buffer Pool Activity',
417 vlabel => 'Activity per ${graph_period}',
418 total => 'Total',
419 },
420 data_source_attrs => {
421 draw => 'LINE2',
422 },
423 },
424 data_sources => [
425 {name => 'ib_bpool_read', label => 'Pages read'},
426 {name => 'ib_bpool_created', label => 'Pages created'},
427 {name => 'ib_bpool_written', label => 'Pages written'},
428 ],
429 };
430
431 #---------------------------------------------------------------------
432
433 $graphs{innodb_insert_buf} = {
434 config => {
435 global_attrs => {
436 title => 'InnoDB Insert Buffer',
437 vlabel => 'Activity per ${graph_period}',
438 },
439 data_source_attrs => {
440 draw => 'LINE1',
441 },
442 },
443 data_sources => [
444 {name => 'ib_ibuf_inserts', label => 'Inserts'},
445 {name => 'ib_ibuf_merged_rec', label => 'Merged Records'},
446 {name => 'ib_ibuf_merges', label => 'Merges'},
447 ],
448 };
449
450 #---------------------------------------------------------------------
451
452 $graphs{innodb_io} = {
453 config => {
454 global_attrs => {
455 title => 'InnoDB IO',
456 vlabel => 'IO operations per ${graph_period}',
457 },
458 data_source_attrs => {
459 draw => 'LINE1',
460 },
461 },
462 data_sources => [
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'},
467 ],
468 };
469
470 #---------------------------------------------------------------------
471
472 $graphs{innodb_io_pend} = {
473 config => {
474 global_attrs => {
475 title => 'InnoDB IO Pending',
476 vlabel => 'Pending operations',
477 },
478 data_source_attrs => {
479 draw => 'LINE1',
480 },
481 },
482 data_sources => [
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'},
490 ],
491 };
492
493 #---------------------------------------------------------------------
494
495 $graphs{innodb_log} = {
496 config => {
497 global_attrs => {
498 title => 'InnoDB Log',
499 vlabel => 'Log activity per ${graph_period}',
500 },
501 data_source_attrs => {
502 draw => 'LINE1',
503 },
504 },
505 data_sources => [
506 {name => 'innodb_log_buffer_size', label => 'Buffer Size',
507 type => 'GAUGE',
508 draw => 'AREA',
509 colour => 'fafd9e'},
510 {name => 'ib_log_flush', label => 'KB Flushed'},
511 {name => 'ib_log_written', label => 'KB Written'},
512 ],
513 };
514
515 #---------------------------------------------------------------------
516
517 $graphs{innodb_rows} = {
518 config => {
519 global_attrs => {
520 title => 'InnoDB Row Operations',
521 vlabel => 'Operations per ${graph_period}',
522 total => 'Total',
523 },
524 data_source_attrs => {},
525 },
526 data_sources => [
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'},
531 ],
532 };
533
534 #---------------------------------------------------------------------
535
536 $graphs{innodb_semaphores} = {
537 config => {
538 global_attrs => {
539 title => 'InnoDB Semaphores',
540 vlabel => 'Semaphores per ${graph_period}',
541 },
542 data_source_attrs => {
543 draw => 'LINE1',
544 },
545 },
546 data_sources => [
547 {name => 'ib_spin_rounds', label => 'Spin Rounds'},
548 {name => 'ib_spin_waits', label => 'Spin Waits'},
549 {name => 'ib_os_waits', label => 'OS Waits'},
550 ],
551 };
552
553 #---------------------------------------------------------------------
554
555 $graphs{innodb_tnx} = {
556 config => {
557 global_attrs => {
558 title => 'InnoDB Transactions',
559 vlabel => 'Transactions per ${graph_period}',
560 },
561 data_source_attrs => {
562 draw => 'LINE1',
563 },
564 },
565 data_sources => [
566 {name => 'ib_tnx', label => 'Transactions created'},
567 ],
568 };
569
570 #---------------------------------------------------------------------
571
572 $graphs{myisam_indexes} = {
573 config => {
574 global_attrs => {
575 title => 'MyISAM Indexes',
576 vlabel => 'Requests per ${graph_period}',
577 },
578 data_source_attrs => {
579 draw => 'LINE2',
580 },
581 },
582 data_sources => [
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'},
587 ],
588 };
589
590 #---------------------------------------------------------------------
591
592 $graphs{network_traffic} = {
593 config => {
594 global_attrs => {
595 title => 'Network Traffic',
596 args => '--base 1024',
597 vlabel => 'Bytes received (-) / sent (+) per ${graph_period}',
598 },
599 data_source_attrs => {
600 draw => 'LINE2',
601 },
602 },
603 data_sources => [
604 {name => 'Bytes_received', label => 'Bytes transfered',
605 graph => 'no'},
606 {name => 'Bytes_sent', label => 'Bytes transfered',
607 negative => 'Bytes_received'},
608 ],
609 };
610
611 #---------------------------------------------------------------------
612
613 $graphs{qcache} = {
614 config => {
615 global_attrs => {
616 title => 'Query Cache',
617 vlabel => 'Commands per ${graph_period}',
618 },
619 data_source_attrs => {
620 draw => 'LINE1',
621 },
622 },
623 data_sources => [
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'},
629 ],
630 };
631
632 #---------------------------------------------------------------------
633
634 $graphs{qcache_mem} = {
635 config => {
636 global_attrs => {
637 title => 'Query Cache Memory',
638 vlabel => 'Bytes',
639 args => '--base 1024 --lower-limit 0',
640 },
641 data_source_attrs => {
642 draw => 'AREA',
643 type => 'GAUGE',
644 },
645 },
646 data_sources => [
647 {name => 'query_cache_size', label => 'Cache size'},
648 {name => 'Qcache_free_memory', label => 'Free mem'},
649 ],
650 };
651
652 #---------------------------------------------------------------------
653
654 $graphs{replication} = {
655 config => {
656 global_attrs => {
657 title => 'Replication',
658 vlabel => 'Activity',
659 },
660 data_source_attrs => {
661 draw => 'LINE1',
662 },
663 },
664 data_sources => [
665 {name => 'slave_io_running', label => 'Slave IO Running',
666 type => 'GAUGE',
667 draw => 'AREA'},
668 {name => 'slave_sql_running', label => 'Slave SQL Running',
669 type => 'GAUGE',
670 draw => 'AREA'},
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',
674 type => 'GAUGE'},
675 ],
676 };
677
678 #---------------------------------------------------------------------
679
680 $graphs{select_types} = {
681 config => {
682 global_attrs => {
683 title => 'Select types',
684 vlabel => 'Commands per ${graph_period}',
685 total => 'Total',
686 },
687 data_source_attrs => {},
688 },
689 data_sources => [
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'},
695 ],
696 };
697
698 #---------------------------------------------------------------------
699
700 $graphs{slow} = {
701 config => {
702 global_attrs => {
703 title => 'Slow Queries',
704 vlabel => 'Slow queries per ${graph_period}',
705 period => 'minute',
706 },
707 data_source_attrs => {
708 draw => 'LINE2',
709 },
710 },
711 data_sources => [
712 {name => 'Slow_queries', label => 'Slow queries'},
713 ],
714 };
715
716 #---------------------------------------------------------------------
717
718 $graphs{sorts} = {
719 config => {
720 global_attrs => {
721 title => 'Sorts',
722 vlabel => 'Sorts / ${graph_period}',
723 },
724 data_source_attrs => {
725 draw => 'LINE2',
726 },
727 },
728 data_sources => [
729 {name => 'Sort_rows', label => 'Rows sorted'},
730 {name => 'Sort_range', label => 'Range'},
731 {name => 'Sort_merge_passes', label => 'Merge passes'},
732 {name => 'Sort_scan', label => 'Scan'},
733 ],
734 };
735
736 #---------------------------------------------------------------------
737
738 $graphs{table_locks} = {
739 config => {
740 global_attrs => {
741 title => 'Table locks',
742 vlabel => 'locks per ${graph_period}',
743 },
744 data_source_attrs => {
745 draw => 'LINE2',
746 },
747 },
748 data_sources => [
749 {name => 'Table_locks_immediate', label => 'Table locks immed'},
750 {name => 'Table_locks_waited', label => 'Table locks waited'},
751 ],
752 };
753
754 #---------------------------------------------------------------------
755
756 $graphs{tmp_tables} = {
757 config => {
758 global_attrs => {
759 title => 'Temporary objects',
760 vlabel => 'Objects per ${graph_period}',
761 },
762 data_source_attrs => {
763 draw => 'LINE2',
764 },
765 },
766 data_sources => [
767 {name => 'Created_tmp_disk_tables', label => 'Temp disk tables'},
768 {name => 'Created_tmp_tables', label => 'Temp tables'},
769 {name => 'Created_tmp_files', label => 'Temp files'},
770 ],
771 };
772
773 #---------------------------------------------------------------------
774 # Plugin Graphs
775 # These are mysql plugins of type INFORMATION SCHEMA
776 #
777 # These will be added to $graphs if available
778 #---------------------------------------------------------------------
779
780 my %graph_plugins = ();
781
782 $graph_plugins{query_response_time} = {
783 count => {
784 config => {
785 global_attrs => {
786 title => 'Query Response Time Count',
787 vlabel => 'queries per ${graph_period}',
788 },
789 data_source_attrs => {
790 draw => 'LINE2',
791 type => 'DERIVE',
792 },
793 },
794 # data_sources are populated by sub plugin_query_response_time
795 data_sources => [
796 ],
797 },
798 total => {
799 config => {
800 global_attrs => {
801 title => 'Query Response Time Total',
802 vlabel => 'query time (microseconds) per ${graph_period}',
803 },
804 data_source_attrs => {
805 draw => 'LINE2',
806 type => 'DERIVE',
807 },
808 },
809 # data_sources are populated by sub plugin_query_response_time
810 data_sources => [
811 ],
812 }
813 };
814
815 $graph_plugins{user_statistics} = {
816 connections => {
817 config => {
818 global_attrs => {
819 title => 'User Connections',
820 vlabel => 'connections per ${graph_period}',
821 },
822 data_source_attrs => {
823 draw => 'LINE2',
824 type => 'DERIVE',
825 },
826 },
827 cols => { 'total_connections' => {}, 'concurrent_connections' => {}, 'denied_connections' => {}, 'lost_connections' => {}},
828 data_sources => [
829 ],
830 },
831 usertime => {
832 config => {
833 global_attrs => {
834 title => 'User Time',
835 vlabel => 'seconds',
836 },
837 data_source_attrs => {
838 draw => 'LINE2',
839 type => 'DERIVE',
840 },
841 },
842 cols => { 'connected_time' => {}, 'busy_time' => {}, 'cpu_time' => {} },
843 data_sources => [
844 ],
845 },
846 bytes => {
847 config => {
848 global_attrs => {
849 title => 'User Bytes',
850 vlabel => 'bytes',
851 },
852 data_source_attrs => {
853 draw => 'LINE2',
854 type => 'DERIVE',
855 },
856 },
857 cols => { 'bytes_received' => {}, 'bytes_sent' => {}, 'binlog_bytes_written' => {} },
858 data_sources => [
859 ],
860 },
861 rows => {
862 config => {
863 global_attrs => {
864 title => 'User Rows',
865 vlabel => 'rows',
866 },
867 data_source_attrs => {
868 draw => 'LINE2',
869 type => 'DERIVE',
870 },
871 },
872 cols => { 'rows_read' => {}, 'rows_sent' => {}, 'rows_deleted' => {}, 'rows_inserted' => {}, 'rows_updated' => {} },
873 data_sources => [
874 ],
875 },
876 commands => {
877 config => {
878 global_attrs => {
879 title => 'Command breakdown by user',
880 vlabel => 'commands',
881 },
882 data_source_attrs => {
883 draw => 'LINE2',
884 type => 'DERIVE',
885 },
886 },
887 cols => { 'select_commands' => {}, 'update_commands' => {}, 'other_commands' => {}, 'commit_transactions' => {}, 'rollback_transactions' => {} },
888 data_sources => [
889 ],
890 }
891
892 };
893 #---------------------------------------------------------------------
894 # M A I N
895 #---------------------------------------------------------------------
896
897
898 #
899 # Global hash holding the data collected from mysql.
900 #
901 our $data; # Was 'my'. Changed to 'our' to facilitate testing.
902
903
904 sub main {
905 my $graph = basename($0);
906 $graph =~ s/^mysql[0-9]*_//g; # allow multiple instances
907 my $command = $ARGV[0] || 'show';
908
909 my %command_map = (
910 'autoconf' => \&autoconf,
911 'config' => \&config,
912 'show' => \&show,
913 'suggest' => \&suggest,
914 );
915
916 die "Unknown command: $command"
917 unless exists $command_map{$command};
918
919 die "Missing dependency Cache::Cache"
920 unless $has_cache || $command eq 'autoconf';
921
922 return $command_map{$command}->($graph);
923 }
924
925
926 #---------------------------------------------------------------------
927 # C O M M A N D H A N D L E R S
928 #---------------------------------------------------------------------
929
930 # Each command handler should return an appropriate exit code
931
932
933 # http://munin-monitoring.org/wiki/ConcisePlugins#autoconf
934 sub autoconf {
935 unless ($has_cache) {
936 print "no (Missing dependency Cache::Cache)\n";
937 return 0;
938 }
939
940 eval {
941 db_connect();
942 };
943 if ($@) {
944 my $err = $@;
945 $err =~ s{\s at \s \S+ \s line .*}{}xms;
946 print "no ($err)\n";
947 return 0;
948 }
949 print "yes\n";
950 return 0;
951 }
952
953
954 # http://munin-monitoring.org/wiki/ConcisePlugins#suggest
955 sub suggest {
956
957 # What is the best way to decide which graphs is applicable to a
958 # given system?
959 #
960 # Does the database use InnoDB? A zero count from:
961 #
962 # SELECT COUNT(*)
963 # FROM tables
964 # WHERE table_type = 'base table'
965 # AND engine = 'innodb'
966 #
967 # Does the database use binary logs? 'OFF' as the result from:
968 #
969 # SHOW GLOBAL variables LIKE 'log_bin'
970 #
971 # Is the database setup as a slave? Empty result from:
972 #
973 # SHOW SLAVE STATUS
974
975 foreach my $graph (sort keys(%graphs)) {
976 next if $graph =~ /innodb_/ && $data->{_innodb_disabled};
977 next if $graph =~ /wsrep_/ && $data->{_galera_disabled};
978 print "$graph\n";
979 }
980
981 return 0;
982 }
983
984
985 sub config {
986 my $graph_name = shift;
987
988 # In MySQL 5.1 (and probably erlier versions as well) status
989 # variables are unique when looking at the last 19 characters.
990 #
991 # SELECT RIGHT(variable_name, 19), COUNT(*)
992 # FROM information_schema.global_status
993 # GROUP BY RIGHT(variable_name, 19)
994 # HAVING COUNT(*) > 1;
995 #
996 # Empty set (0.06 sec)
997 #
998 # There is one duplicate when looking at server variables
999 #
1000 # SELECT RIGHT(variable_name, 19), COUNT(*)
1001 # FROM information_schema.global_variables
1002 # GROUP BY RIGHT(variable_name, 19)
1003 # HAVING COUNT(*) > 1;
1004 #
1005 # +--------------------------+----------+
1006 # | RIGHT(variable_name, 19) | COUNT(*) |
1007 # +--------------------------+----------+
1008 # | OW_PRIORITY_UPDATES | 2 |
1009 # +--------------------------+----------+
1010 # 1 row in set (0.05 sec)
1011 #
1012 # show global variables like '%OW_PRIORITY_UPDATES';
1013 #
1014 # +--------------------------+-------+
1015 # | Variable_name | Value |
1016 # +--------------------------+-------+
1017 # | low_priority_updates | OFF |
1018 # | sql_low_priority_updates | OFF |
1019 # +--------------------------+-------+
1020 # 2 rows in set (0.00 sec)
1021 #
1022 # Not a problem since we don't graph these
1023
1024 update_data();
1025
1026 die 'Unknown graph ' . ($graph_name ? $graph_name : '')
1027 unless $graphs{$graph_name};
1028
1029 my $graph = $graphs{$graph_name};
1030
1031 my %conf = (%{$defaults{global_attrs}}, %{$graph->{config}{global_attrs}});
1032 while (my ($k, $v) = each %conf) {
1033 print "graph_$k $v\n";
1034 }
1035 print "graph_category mysql2\n";
1036
1037 for my $ds (@{$graph->{data_sources}}) {
1038 my %ds_spec = (
1039 %{$defaults{data_source_attrs}},
1040 %{$graph->{config}{data_source_attrs}},
1041 %$ds,
1042 );
1043 while (my ($k, $v) = each %ds_spec) {
1044 # 'name' is only used internally in this script, not understood by munin.
1045 printf("%s.%s %s\n", clean_fieldname($ds->{name}), $k, $v) unless ($k eq 'name');
1046 }
1047 print_thresholds(clean_fieldname($ds->{name}));
1048 }
1049
1050 return 0;
1051 }
1052
1053 sub show {
1054 my $graph_name = shift;
1055
1056 update_data();
1057
1058 die 'Unknown graph ' . ($graph_name ? $graph_name : '')
1059 unless $graphs{$graph_name};
1060
1061 my $graph = $graphs{$graph_name};
1062
1063 die "Can't show data for '$graph_name' because InnoDB is disabled."
1064 if $graph_name =~ /innodb_/ && $data->{_innodb_disabled};
1065
1066 for my $ds (@{$graph->{data_sources}}) {
1067 my $value = exists $ds->{value}
1068 ? $ds->{value}($data)
1069 : $data->{$ds->{name}};
1070
1071 printf "%s.value %s\n", clean_fieldname($ds->{name}), defined($value) ? $value : 'U';
1072 }
1073
1074 return 0;
1075 }
1076
1077
1078
1079 #---------------------------------------------------------------------
1080 # U T I L I T Y S U B S
1081 #---------------------------------------------------------------------
1082
1083
1084 sub db_connect {
1085 my $dsn = "$config{dsn};mysql_connect_timeout=5";
1086
1087 return DBI->connect($dsn, $config{user}, $config{password}, {
1088 RaiseError => 1,
1089 PrintError => 0,
1090 FetchHashKeyName => 'NAME_lc',
1091 });
1092 }
1093
1094
1095 sub update_data {
1096 $data = $shared_memory_cache->get('data');
1097 my $graphs_stored = $shared_memory_cache->get('graphs');
1098 %graphs = %{thaw($graphs_stored)} if $graphs_stored;
1099 return if $data;
1100
1101 #warn "Need to update cache";
1102
1103 $data = {};
1104
1105 my $dbh = db_connect();
1106
1107 # Set up defaults in case the server is not a slave
1108 $data->{relay_log_space} = 0;
1109 $data->{slave_running} = 0;
1110 $data->{slave_stopped} = 0;
1111
1112 # Set up defaults in case binlog is not enabled
1113 $data->{ma_binlog_size} = 0;
1114
1115 update_variables($dbh);
1116 update_plugins($dbh);
1117 update_innodb($dbh);
1118 update_master($dbh);
1119 update_slave($dbh);
1120
1121 $shared_memory_cache->set('data', $data);
1122 $shared_memory_cache->set('graphs', nfreeze(\%graphs));
1123 }
1124
1125
1126 sub update_plugins {
1127 my ($dbh) = @_;
1128
1129 my %plugin_map = (
1130 'query_response_time' => \&plugin_query_response_time,
1131 );
1132
1133 sub add_graphs {
1134 my ($f, $sec, $dbh, %g) = @_;
1135 if ($f->($dbh) == 0) {
1136 while (my ($k, $v) = each %g) {
1137 $graphs{$sec . '_' . $k} = $v;
1138 }
1139 }
1140 }
1141
1142 my $sth = $dbh->prepare("SHOW PLUGINS");
1143 $sth->execute();
1144 while (my $row = $sth->fetchrow_hashref()) {
1145 next if $row->{'type'} ne 'INFORMATION SCHEMA';
1146 my $sec = lc $row->{'name'};
1147 next if not exists $plugin_map{$sec};
1148 add_graphs($plugin_map{$sec}, $sec, $dbh, %{$graph_plugins{$sec}});
1149 }
1150 $sth->finish();
1151
1152 my %is_map = (
1153 'user_statistics' => \&is_user_statistics,
1154 );
1155
1156 $sth = $dbh->prepare("SHOW TABLES IN INFORMATION_SCHEMA");
1157 $sth->execute();
1158 while (my $row = $sth->fetchrow_hashref()) {
1159 my $sec = lc $row->{'tables_in_information_schema'};
1160 next if not exists $is_map{$sec};
1161 add_graphs($is_map{$sec}, $sec, $dbh, %{$graph_plugins{$sec}});
1162 }
1163 $sth->finish();
1164 }
1165
1166 sub update_variables {
1167 my ($dbh) = @_;
1168 my @queries = (
1169 'SHOW GLOBAL STATUS',
1170 'SHOW GLOBAL VARIABLES',
1171 );
1172
1173 my %variable_name_map = (
1174 table_cache => 'table_open_cache', # table_open_cache was
1175 # previously known as
1176 # table_cache in MySQL
1177 # 5.1.2 and earlier.
1178 );
1179
1180 for my $query (@queries) {
1181 $data->{$query} = {};
1182
1183 my $sth = $dbh->prepare($query);
1184 $sth->execute();
1185 while (my $row = $sth->fetch) {
1186 my $var = $variable_name_map{$row->[0]} || $row->[0];
1187 $data->{$var} = $row->[1];
1188 }
1189 $sth->finish();
1190 }
1191 }
1192
1193
1194 sub update_innodb {
1195 my ($dbh) = @_;
1196
1197 my $sth = $dbh->prepare('SHOW /*!50000 ENGINE*/ INNODB STATUS');
1198 eval {
1199 $sth->execute();
1200 };
1201 if ($@) {
1202 if ($@ =~ /Unknown (storage|table) engine 'INNODB'|Cannot call SHOW INNODB STATUS because skip-innodb is defined/i) {
1203 $data->{_innodb_disabled} = 1;
1204 return;
1205 }
1206 die $@;
1207 }
1208 my $row = $sth->fetchrow_hashref();
1209 my $status = $row->{'status'};
1210 $sth->finish();
1211
1212 parse_innodb_status($status);
1213 }
1214
1215
1216 sub update_master {
1217 my ($dbh) = @_;
1218
1219 my $sth = $dbh->prepare('SHOW MASTER LOGS');
1220 eval {
1221 $sth->execute();
1222 };
1223 if ($@) {
1224 # SHOW MASTER LOGS failed becuase binlog is not enabled
1225 return if $@ =~ /You are not using binary logging/;
1226 die $@;
1227 }
1228
1229 while (my $row = $sth->fetch) {
1230 $data->{ma_binlog_size} += $row->[1];
1231 }
1232
1233 $sth->finish();
1234 }
1235
1236
1237 sub update_slave {
1238 my ($dbh) = @_;
1239
1240 my $sth = $dbh->prepare('SHOW SLAVE STATUS');
1241 $sth->execute();
1242 my $row = $sth->fetchrow_hashref();
1243 return unless $row;
1244 while (my ($k, $v) = each %$row) {
1245 $data->{$k} = $v;
1246 }
1247 $sth->finish();
1248
1249 # undef when slave is stopped, or when MySQL fails to calculate
1250 # the lag (which happens depresingly often). (mk-heartbeat fixes
1251 # this problem.)
1252 $data->{seconds_behind_master} ||= 0;
1253
1254 # Track these two fields so we can trigger warnings if the slave stops
1255 # running
1256 $data->{slave_sql_running} = ($data->{slave_sql_running} eq 'Yes')
1257 ? 0 : 1;
1258 $data->{slave_io_running} = ($data->{slave_io_running} eq 'Yes')
1259 ? 0 : 1;
1260
1261 }
1262
1263
1264 #---------------------------------------------------------------------
1265 # Information SCHEMA tables represent data to be processed
1266 #---------------------------------------------------------------------
1267
1268
1269 sub plugin_query_response_time {
1270 my ($dbh) = @_;
1271
1272 return 1 if not defined $data->{query_response_time_stats};
1273 return 1 if $data->{query_response_time_stats} eq 'OFF';
1274
1275 my $sth = $dbh->prepare("SELECT * FROM INFORMATION_SCHEMA.QUERY_RESPONSE_TIME");
1276 $sth->execute();
1277 while (my $row = $sth->fetchrow_hashref()) {
1278 my $time = $row->{'time'};
1279 $data->{'query_response_time_count_' . $time} = $row->{'count'};
1280 push @{$graph_plugins{query_response_time}->{count}->{data_sources}}, {name => 'query_response_time_count_' . $time, label => $time };
1281 next if $row->{'total'} eq 'TOO LONG';
1282 $data->{'query_response_time_total_' . $time} = $row->{'total'} * 1e6;
1283 push @{$graph_plugins{query_response_time}->{total}->{data_sources}}, {name => 'query_response_time_total_' . $time, label => $time };
1284 }
1285 $sth->finish();
1286
1287 return 0;
1288 }
1289
1290 sub is_user_statistics {
1291 my ($dbh) = @_;
1292
1293 return 1 if not defined $data->{userstat};
1294 return 1 if $data->{userstat} eq 'OFF';
1295
1296 my $sth = $dbh->prepare("SELECT * FROM INFORMATION_SCHEMA.USER_STATISTICS");
1297 $sth->execute();
1298 while (my $row = $sth->fetchrow_hashref()) {
1299 my $user = $row->{'user'};
1300 my $var;
1301 while (my ($g, $v) = each %{$graph_plugins{user_statistics}}) {
1302 while (my ($userstat,$conf) = each %{$v->{cols}}) {
1303 $var = 'user_stats_' . $user . '_' . $userstat;
1304 $data->{$var} = int $row->{$userstat};
1305 my $ds = { %$conf };
1306 $ds->{name} = $var;
1307 $ds->{label} = $user . ' ' . $userstat;
1308 push @{$graph_plugins{user_statistics}->{$g}->{data_sources}}, $ds;
1309 }
1310 }
1311 }
1312 $sth->finish();
1313 return 0;
1314 }
1315
1316 #
1317 # In 'SHOW ENGINE INNODB STATUS' 64 bit integers are not formated as
1318 # plain integers. They are either:
1319 #
1320 # - split in two and needs to be shifted together,
1321 # - or hexadecimal
1322 #
1323 sub innodb_bigint {
1324 my ($x, $y) = @_;
1325
1326 return defined $y
1327 ? Math::BigInt->new($x)->blsft(32) + $y
1328 : Math::BigInt->new("0x$x");
1329 }
1330
1331 #---------------------------------------------------------------------
1332 # P A R S E 'SHOW ENGINE INNODB STATUS' O U T P U T
1333 #---------------------------------------------------------------------
1334
1335
1336 # A nice walk through
1337 # http://www.mysqlperformanceblog.com/2006/07/17/show-innodb-status-walk-through/
1338
1339 # The parsing is split in one subrutine per section. Each subroutine
1340 # should parse a block with the following structure
1341 #
1342 # block body ...
1343 # more lines ....
1344 # ----------
1345
1346 sub parse_innodb_status {
1347 local $_ = shift;
1348
1349 # Add a dummy section to the end in case the innodb status output
1350 # has been truncated (Happens for status > 64K characters)
1351 $_ .= "\n----------\nDUMMY\n";
1352
1353 my %section_map = (
1354
1355 'BUFFER POOL AND MEMORY' => \&parse_buffer_pool_and_memory,
1356 'INDIVIDUAL BUFFER POOL INFO' => \&skip,
1357 'FILE I/O' => \&parse_file_io,
1358 'INSERT BUFFER AND ADAPTIVE HASH INDEX'
1359 => \&parse_insert_buffer_and_adaptive_hash_index,
1360 'LATEST DETECTED DEADLOCK' => \&skip,
1361 'LATEST FOREIGN KEY ERROR' => \&skip,
1362 'LOG' => \&parse_log,
1363 'ROW OPERATIONS' => \&skip,
1364 'SEMAPHORES' => \&parse_semaphores,
1365 'TRANSACTIONS' => \&parse_transactions,
1366 'BACKGROUND THREAD' => \&skip,
1367 );
1368
1369 skip_heading();
1370 for (;;) {
1371 m/\G(.*)\n/gc;
1372 my $sec = $1;
1373
1374 last if $sec eq 'END OF INNODB MONITOR OUTPUT';
1375 if ($sec eq 'DUMMY') {
1376 handle_incomplete_innodb_status();
1377 last;
1378 }
1379
1380 die "Unknown section: $1" unless exists $section_map{$sec};
1381 die "Parse error. Expected a section separator" unless m/\G-+\n/gc;
1382
1383 $section_map{$sec}->();
1384 }
1385 }
1386
1387
1388 # This regular expression handles the different formating of 64-bit
1389 # integers in different versions of the innodb engine. Either two
1390 # decimal 32-bit integers seperated by a space, or a single
1391 # hexadecimal 64-bit integer.
1392 my $innodb_bigint_rx = qr{([[a-fA-F\d]+)(?: (\d+))?};
1393
1394
1395 sub match_dashes { return m/\G-+\n(?!-)/gc; }
1396
1397
1398 sub skip_line { return m/\G.*\n/gc; }
1399
1400
1401 sub skip_heading {
1402 # Heading is 6 lines
1403 for my $foo (1...6) {
1404 skip_line or die('Parse error');
1405 }
1406 }
1407
1408
1409 sub parse_section {
1410 my ($parser) = @_;
1411
1412 #warn substr($_, pos(), 10);
1413 for (;;) {
1414 return if match_dashes();
1415 next if $parser->();
1416 skip_line();
1417 }
1418 }
1419
1420
1421 sub skip { parse_section(sub {}); }
1422
1423
1424 sub parse_semaphores {
1425 parse_section(
1426 sub {
1427 m/\GMutex spin waits (\d+), rounds (\d+), OS waits (\d+)\n/gc && do {
1428 $data->{ib_spin_waits} = $1;
1429 $data->{ib_spin_rounds} = $2;
1430 $data->{ib_os_waits} = $3;
1431 return 1;
1432 };
1433 }
1434 );
1435 }
1436
1437
1438 sub parse_transactions {
1439 parse_section(
1440 sub {
1441 m/\GTrx id counter $innodb_bigint_rx\n/gc && do {
1442 $data->{ib_tnx} = innodb_bigint($1, $2);
1443 return 1;
1444 };
1445 m/\GPurge done for trx's n:o < $innodb_bigint_rx undo n:o < $innodb_bigint_rx\n/gc && do {
1446 if (defined $3) {
1447 # old format
1448 $data->{ib_tnx_prg} = innodb_bigint($1, $2);
1449 # FIX add to data? innodb_bigint($3, $4);
1450 }
1451 else {
1452 # new format
1453 $data->{ib_tnx_prg} = innodb_bigint($1);
1454 # FIX add to data? innodb_bigint($2);
1455 }
1456 return 1;
1457 };
1458 m/\GHistory list length (\d+)\n/gc && do {
1459 $data->{ib_tnx_hist} = $1;
1460 return 1;
1461 };
1462 }
1463 );
1464
1465 }
1466
1467
1468 sub parse_file_io {
1469 parse_section(
1470 sub {
1471 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 {
1472 $data->{ib_iop_aioread} = $1;
1473 $data->{ib_iop_aiowrite} = $2;
1474 $data->{ib_iop_ibuf_aio} = $3;
1475 $data->{ib_iop_log} = $4;
1476 $data->{ib_iop_sync} = $5;
1477 return 1;
1478 };
1479 m/\GPending flushes \(fsync\) log: (\d+); buffer pool: (\d+)\n/gc && do {
1480 $data->{ib_iop_flush_log} = $1;
1481 $data->{ib_iop_flush_bpool} = $2;
1482 return 1;
1483 };
1484 m/\G(\d+) OS file reads, (\d+) OS file writes, (\d+) OS fsyncs\n/gc && do {
1485 $data->{ib_io_read} = $1;
1486 $data->{ib_io_write} = $2;
1487 $data->{ib_io_fsync} = $3;
1488 return 1;
1489 };
1490 }
1491 );
1492 }
1493
1494
1495 sub parse_insert_buffer_and_adaptive_hash_index {
1496 parse_section(
1497 sub {
1498 # MySQL < 5.5
1499 m/\G(\d+) inserts, (\d+) merged recs, (\d+) merges\n/gc && do {
1500 $data->{ib_ibuf_inserts} = $1;
1501 $data->{ib_ibuf_merged_rec} = $2;
1502 $data->{ib_ibuf_merges} = $3;
1503 return 1;
1504 };
1505 # MySQL >= 5.5
1506 m/\Gmerged operations:\n insert (\d+), delete mark \d+, delete \d+\ndiscarded operations:\n insert (\d+), delete mark \d+, delete \d+\n/gc && do {
1507 $data->{ib_ibuf_inserts} = $1;
1508 $data->{ib_ibuf_merged_rec} = $1 + $2;
1509 return 1;
1510 };
1511 m/\GIbuf: size (\d+), free list len (\d+), seg size (\d+),(?: (\d+) merges)?\n/gc && do {
1512 $data->{ib_ibuf_size} = $1;
1513 $data->{ib_ibuf_free_len} = $2;
1514 $data->{ib_ibuf_seg_size} = $3;
1515 $data->{ib_ibuf_merges} = $4 if defined $4; # MySQL >= 5.5
1516 return 1;
1517 };
1518 }
1519 );
1520 }
1521
1522
1523 sub parse_log {
1524 parse_section(
1525 sub {
1526 m/\GLog sequence number $innodb_bigint_rx\n/gc && do {
1527 $data->{ib_log_written} = innodb_bigint($1, $2);
1528 return 1;
1529 };
1530 m/\GLog flushed up to\s+$innodb_bigint_rx\n/gc && do {
1531 $data->{ib_log_flush} = innodb_bigint($1, $2);
1532 return 1;
1533 };
1534 m/\G(\d+) log i\/o's done.*\n/gc && do {
1535 $data->{ib_io_log} = $1;
1536 return 1;
1537 };
1538 }
1539 );
1540 }
1541
1542
1543 sub parse_buffer_pool_and_memory {
1544 parse_section(
1545 sub {
1546 m/\GBuffer pool size\s+(\d+)\n/gc && do {
1547 $data->{ib_bpool_size} = $1;
1548 return 1;
1549 };
1550 m/\GFree buffers\s+(\d+)\n/gc && do {
1551 $data->{ib_bpool_free} = $1;
1552 return 1;
1553 };
1554 m/\GDatabase pages\s+(\d+)\n/gc && do {
1555 $data->{ib_bpool_dbpages} = $1;
1556 return 1;
1557 };
1558 m/\GModified db pages\s+(\d+)\n/gc && do {
1559 $data->{ib_bpool_modpages} = $1;
1560 return 1;
1561 };
1562 m/\GPages read (\d+), created (\d+), written (\d+)\n/gc && do {
1563 $data->{ib_bpool_read} = $1;
1564 $data->{ib_bpool_created} = $2;
1565 $data->{ib_bpool_written} = $3;
1566 return 1;
1567 };
1568 }
1569 );
1570 }
1571
1572
1573 sub handle_incomplete_innodb_status {
1574
1575 warn "Output from SHOW ENGINE INNDOB STATUS was truncated. "
1576 . "This happens if the output of SEIS exceeds 64KB. "
1577 . "Several of the InnoDB graphs might be affected by this.";
1578
1579 # FIX Is it possible to find some of the missing values from SHOW
1580 # STATUS?
1581 }
1582
1583
1584 exit main() unless caller;
1585
1586
1587 1;