├── .gitignore ├── Vagrantfile ├── manifests ├── variable_notes.txt └── centos6.pp ├── LICENSE.TXT ├── CHANGELOG ├── tester ├── README ├── MySQL_Script_Utils.pm ├── myq_slave_info ├── myq_innodb_status └── myq_status /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .vagrant 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant::Config.run do |config| 5 | config.vm.box = "centos6-simple" 6 | config.vm.host_name = "mysql" 7 | config.vm.share_folder "myq_gadgets", "/myq_gadgets", "../myq_gadgets" 8 | 9 | config.vm.provision :puppet do |puppet| 10 | puppet.manifests_path = "manifests" 11 | puppet.manifest_file = "centos6.pp" 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /manifests/variable_notes.txt: -------------------------------------------------------------------------------- 1 | =Innodb_buffer_pool_pages_made_young == pages read that were in the 'old' block 2 | Accessing to a block in the old sublist makes it “young”, moving it to the head of the buffer pool (the head of the new sublist). If the block was read in because it was required, the first access occurs immediately and the block is made young. If the block was read in due to read-ahead, the first access does not occur immediately (and might not occur at all before the block is evicted). 3 | 4 | =Innodb_buffer_pool_pages_made_not_young -- pages inserted but not accessed? Causes: 5 | - read ahead 6 | - ??? 7 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | Software Copyright License Agreement (BSD License) 2 | 3 | Copyright (c) 2008, Yahoo! Inc. 4 | All rights reserved. 5 | 6 | Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above 9 | copyright notice, this list of conditions and the 10 | following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above 13 | copyright notice, this list of conditions and the 14 | following disclaimer in the documentation and/or other 15 | materials provided with the distribution. 16 | 17 | * Neither the name of Yahoo! Inc. nor the names of its 18 | contributors may be used to endorse or promote products 19 | derived from this software without specific prior 20 | written permission of Yahoo! Inc. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | -------------------------------------------------------------------------------- /manifests/centos6.pp: -------------------------------------------------------------------------------- 1 | group { "puppet": 2 | ensure => "present", 3 | } 4 | 5 | File { owner => 0, group => 0, mode => 0644 } 6 | 7 | file { '/etc/motd': 8 | content => "Welcome to your Vagrant-built virtual machine! 9 | Managed by Puppet.\n" 10 | } 11 | 12 | yumrepo { 13 | 'rpmforge': 14 | descr => "RHEL \$releasever - RPMforge.net -dag", 15 | enabled => 0, 16 | baseurl => "http://apt.sw.be/redhat/el6/en/\$basearch/rpmforge", 17 | mirrorlist => "http://apt.sw.be/redhat/el6/en/mirrors-rpmforge", 18 | gpgcheck => 0; 19 | 'rpmforge-extras': 20 | descr => "RHEL \$releasever - RPMforge.net -extras", 21 | enabled => 0, 22 | baseurl => "http://apt.sw.be/redhat/el6/en/\$basearch/extras", 23 | mirrorlist => "http://apt.sw.be/redhat/el6/en/mirrors-rpmforge-extras", 24 | gpgcheck => 0; 25 | 'rpmforge-testing': 26 | descr => "RHEL \$releasever - RPMforge.net -testing", 27 | enabled => 0, 28 | baseurl => "http://apt.sw.be/redhat/el6/en/\$basearch/testing", 29 | mirrorlist => "http://apt.sw.be/redhat/el6/en/mirrors-rpmforge-testing", 30 | gpgcheck => 0; 31 | 'percona': 32 | descr => "CentOS \$releasever - Percona", 33 | enabled => 1, 34 | baseurl => "http://repo.percona.com/centos/\$releasever/os/\$basearch/", 35 | gpgcheck => 0; 36 | } 37 | 38 | 39 | package { 40 | 'telnet': ensure => 'installed'; 41 | 'vim-minimal': ensure => 'installed'; 42 | 'screen': ensure => 'installed'; 43 | 44 | 'Percona-Server-server-55': ensure => 'installed', 45 | require => [ Package['Percona-Server-shared-compat'] ]; 46 | 'Percona-Server-client-55': ensure => 'installed'; 47 | 'Percona-Server-shared-compat': ensure => 'installed'; 48 | 'percona-toolkit': ensure => 'installed'; 49 | } 50 | 51 | service { 52 | 'mysql': ensure => 'running'; 53 | } 54 | 55 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | Version 0.3.0- 2 | - Added 'wsrep' mode to myq_status for Galera/Xtradb Cluster node status 3 | - Added 'innodb_flush' mode to myq_status to observe innodb flushing and 4 | log checkpoint age (works best with XtraDB) 5 | 6 | Version 0.2.0 - 7 | - Added support for mysqladmin output files from pt-stalk in Percona 8 | toolkit. This allows you to replay all the collected SHOW STATUS 9 | and get myq_status reports. Use '-f ' to bypass normal 10 | timed data collection. 11 | 12 | Version 0.0.11 - 13 | - Added Checkpoint age to myq_innodb_status log output. 14 | Version 0.0.10 - 15 | - Fixed bug with passwordless access (for instance with a .my.cnf file) 16 | Version 0.0.9 - 17 | - Documented new reports in README 18 | - Updated 'thds' column in the 'cttf' report to read 'crtd' instead. 19 | Version 0.0.8 - 20 | - Added a bunch more innodb reports, looking for the good stuff. 21 | Version 0.0.7 - 22 | - Added an 'innodb' mode to myq_status to support 5.0+ STATUS variables. 23 | 'myq_innodb_status' continues to handle SHOW INNODB STATUS output, 24 | mostly for 4.1 and earlier. 25 | - Beefed up the format_memory function to use recursion to determine the 26 | appropriate output multiplier (tera, giga, mega, kilo, etc.) 27 | Version 0.0.6 - 28 | - Changed /usr/local/bin/perl to /usr/bin/env perl to make the open source 29 | crowd happy. Make sure the proper perl directory is first in your $PATH 30 | - Made the usage string contain the valid modes in myq_status 31 | - Added check for mysql binary in MySQL_Script_Utils.pm 32 | - Quoted the --password option in MySQL_Script_Utils.pm to handle 33 | passwords with strange characters in them. 34 | - Setup default options string to be clearer which options are available 35 | to the scripts (like -? and -d) 36 | - Bumped the minimum repeat time for myq_status down to 1 second. Note 37 | that it still might take more than 1 second for the check to run. 38 | 39 | Version 0.0.5 - 40 | - Fixed column arrangement in myq_status for the 'cttf' mode. 41 | - Fixed column widths in myq_status for 'throughput' mode. 42 | 43 | Version 0.0.4 - 44 | - Added the myq_status script to help get a running view of what MySQL 45 | is doing, specifically just about everything inside SHOW STATUS. This 46 | includes information to help you tune just about everything in MySQL. 47 | 48 | Version 0.0.3 - 49 | - Fixed a few bugs in myq_slave_info that was happening when checking 50 | the slave status of the local server. (Thanks to fieldsa@) 51 | 52 | Version 0.0.2 - 53 | - Added myq_slave_info to check slave replication status. 54 | - Created MySQL_Script_Utils.pm to hold common script functions. 55 | 56 | Version 0.0.1 - 57 | - Initial Release with myq_innodb_status, more gadgets coming soon. 58 | -------------------------------------------------------------------------------- /tester: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # Copyright (c) 2008 Yahoo! Inc. All rights reserved. The copyrights 4 | # embodied in the content of this file are licensed by Yahoo! Inc. 5 | # under the BSD (revised) open source license 6 | 7 | use strict; 8 | 9 | # Enable unicode output (microseconds units designator) 10 | use utf8; 11 | binmode(STDOUT, ":utf8"); 12 | 13 | 14 | use FindBin; 15 | 16 | # Users can symlink this script and it will still add the correct locations to 17 | # perls path using FindBin::RealBin, but to ensure compatibility with old perl 18 | # we need to be a bit more clever 19 | sub get_lib { 20 | # Start with a sensible default 21 | my $bin_path = $FindBin::Bin; 22 | { 23 | no warnings 'uninitialized'; 24 | my $real_path="$FindBin::RealBin"; #This may fail on Perl < 5.10 25 | if ($real_path ne ''){ # if the value is set, we'll use it 26 | $bin_path = $real_path; 27 | } 28 | } 29 | return $bin_path; 30 | } 31 | 32 | use lib get_lib(); 33 | use MySQL_Script_Utils; 34 | 35 | sub assert { 36 | my( $name, $expected, $method ) = (shift, shift, shift); 37 | 38 | my $result = $method->(@_); 39 | 40 | if( $result ne $expected ) { 41 | die "$name: Results mismatch: '$result' != '$expected'\n"; 42 | } else { 43 | print "$name: pass\n"; 44 | } 45 | } 46 | 47 | # Test numbers 48 | assert( 'one is the lonliest number', '1', \&format_number, 1, 0, 3 ); 49 | assert( 'one point oh', '1.0', \&format_number, 1, 1, 3 ); 50 | assert( 'five hundred', '500', \&format_number, 500, 0, 3 ); 51 | 52 | assert( 'one kay', '1k', \&format_number, 1000, 0, 3 ); 53 | assert( 'one zero zero zero', '1000', \&format_number, 1000, 0, 4 ); 54 | 55 | assert( 'round up to 1k', '1k', \&format_number, 501, 0, 2 ); 56 | assert( 'round down to 0k', '0k', \&format_number, 500, 0, 2 ); # sprintf rounds 0.5 down 57 | 58 | assert( 'twelve k', '12k', \&format_number, 12300, 0, 4 ); 59 | assert( 'one twenty three k', '123k', \&format_number, 123000, 0, 4 ); 60 | 61 | assert( 'twelve m', '12m', \&format_number, 12300000, 0, 4 ); 62 | assert( 'twelve point three m', '12.3m', \&format_number, 12300000, 0, 5 ); 63 | 64 | 65 | 66 | # Test memory 67 | assert( 'one bee', '1b', \&format_memory, 1, 0, 3 ); 68 | assert( 'one point nil', '1b', \&format_memory, 1, 1, 3 ); 69 | assert( 'one point oh', '1.0b', \&format_memory, 1, 1, 4 ); 70 | 71 | assert( 'five oh oh rounded down', '0K', \&format_memory, 500, 0, 3 ); 72 | assert( 'five fifty rounded up', '1K', \&format_memory, 550, 0, 3 ); 73 | assert( 'five fifty bee', '550b', \&format_memory, 550, 0, 4 ); 74 | 75 | assert( 'one kay', '1K', \&format_memory, 1000, 0, 3 ); 76 | assert( 'one zero zero zero bee', '1000b', \&format_memory, 1000, 0, 5 ); 77 | assert( 'one point oh kay', '1.0K', \&format_memory, 1000, 0, 4 ); 78 | 79 | assert( 'one oh oh one bee', '1001b', \&format_memory, 1001, 0, 5 ); 80 | assert( 'one poing oh kay', '1.0K', \&format_memory, 1001, 0, 4 ); 81 | 82 | 83 | assert( 'round up to one kay', '1K', \&format_memory, 550, 0, 2 ); 84 | assert( 'round down to 0K', '0K', \&format_memory, 500, 0, 2 ); # sprintf rounds 0.5 down 85 | 86 | assert( 'twelve kay', '12K', \&format_memory, 12300, 0, 4 ); 87 | assert( 'one twenty three kay', '120K', \&format_memory, 123000, 0, 4 ); 88 | 89 | assert( 'twelve em', '12M', \&format_memory, 12300000, 0, 4 ); 90 | assert( 'eleven point seven em', '11.7M', \&format_memory, 12300000, 0, 5 ); 91 | 92 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | MySQL Gadgets 2 | 3 | Tools to help make life easier with MySQL. 4 | 5 | Note that you need to have some version of the mysql client, it's up to you to 6 | install it. 7 | 8 | All gadgets take mysql command like options (-u,-p,-h,-P), run with --help to 9 | see all options. 10 | 11 | Gadgets: 12 | 13 | - myq_status: 14 | Various views based on SHOW STATUS. Check 15 | http://dev.mysql.com/doc/refman/4.1/en/server-status-variables.html 16 | for details on what these numbers mean. 17 | 18 | Views: 19 | 'myisam' - myisam specific stats, including key buffer, lock waits 20 | 'commands' - Counts all 'Com_*' and outputs all commands run during 21 | each interval sorted descending by number of occurances. This 22 | should be a good indication if something is running that you are 23 | not aware of. 24 | 'coms' - Tabluar view of groups of common server commands. Not as 25 | complete as 'commands', but easier to read. 26 | 'qcache' - Info on query cache usage. 27 | 'cttf' - Connections, Threads, Tables, Files info. 28 | 'throughput' - Byes received/sent 29 | 'query' - useful for query tuning: Shows the Select_* and Sort_* 30 | metrics. Refer to the mysql doc above for info on how to 31 | interpret. 32 | 'temp' - Temporary table info. 33 | 'handler' -- Table handler stats. Good supplemental information to 34 | use with the 'query' view above.a 35 | 36 | Innodb views are based on the SHOW STATUS variables in 5.0 and up. 37 | For earlier MySQL versions use 'myq_innodb_status'. 38 | 'innodb' - an innodb summary view that tries to give an overall 39 | picture of the more important things happening in innodb. 40 | 'innodb_buffer_pool' -- all status variables regarding the innodb 41 | buffer pool. 42 | 'innodb_io' -- All things reading and writing, including buffer 43 | pool,'pages', 'data', and 'dblwr'. 44 | 'innodb_log' -- all transaction log related status. 'Innodb_log*' and 45 | 'Innodb_os_log*'. 46 | 'innodb_row' -- 'Innodb_row_lock*'. 47 | 48 | 49 | - myq_innodb_status: 50 | like iostat for innodb, connects to mysql and parses the output of SHOW 51 | INNODB STATUS at intervals (defaults 60 secs). Calculates changes for 52 | counter type variables. Tested with 4.1, works with 5.x but probably 53 | isn't as detailed as the innodb views in myq_status. 54 | 55 | Note that the data from this script is exclusively from SHOW INNODB 56 | STATUS, and the meaning of many metrics are not clear. Consult the 57 | MySQL Manual for all the available information on them. 58 | 59 | - myq_slave_info: 60 | Connects to the host given and checks SHOW SLAVE STATUS. Connects to 61 | the master host (using the same username and password as the slave) and 62 | does SHOW MASTER STATUS. Compares the binary log positions and reports the 63 | how far behind the IO and SQL threads are. Also reports replication 64 | delay. 65 | 66 | 67 | Standard Options: 68 | 69 | -u: mysql username 70 | -p: mysql password (leave blank to be prompted) 71 | -h: hostname of the mysql server 72 | -H: file with a list of hosts to use (instead of -h). 73 | -P: port to connect to (default: 3306) 74 | -r: repeat execution of the script (sometimes assumed by the script) 75 | -t: repeat after this many seconds 76 | 77 | 78 | 79 | NOTE: Further releases will have more tools. 80 | 81 | 82 | -------------------------------------------------------------------------------- /MySQL_Script_Utils.pm: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008 Yahoo! Inc. All rights reserved. The copyrights 2 | # embodied in the content of this file are licensed by Yahoo! Inc. 3 | # under the BSD (revised) open source license 4 | 5 | package MySQL_Script_Utils; 6 | 7 | use strict; 8 | use utf8; 9 | 10 | use Exporter; 11 | use Getopt::Long qw/ :config no_ignore_case /; 12 | 13 | use vars qw/ $DEBUG $HELP $USER $PASS $HOST $PORT %DEFAULT_OPTIONS 14 | $PASSWORD_ON @ISA @EXPORT $DEFAULT_OPTIONS_STRING 15 | $LOGIN_PATH 16 | /; 17 | 18 | @ISA = qw/ Exporter /; 19 | @EXPORT = qw/ $HOST &parse_options &print_debug &mysql_call 20 | &format_number &format_percent &format_memory 21 | &format_microseconds 22 | $DEFAULT_OPTIONS_STRING 23 | /; 24 | 25 | 26 | $DEBUG = 0; 27 | $HELP = 0; 28 | $PASS = ''; 29 | 30 | warn "'mysql' binary not found in your \$PATH\n" if !`which mysql`; 31 | 32 | $DEFAULT_OPTIONS_STRING = " [-d] [-?] [--login-path=] [-u [-p []]] [-h ] [-P ]"; 33 | my %DEFAULT_OPTIONS = ( 34 | 'help|?' => \$HELP, 35 | 'debug|d' => \$DEBUG, 36 | 'login-path=s' => \$LOGIN_PATH, 37 | 'host|h=s' => \$HOST, 38 | 'P=i' => \$PORT, 39 | 'user|u=s' => \$USER, 40 | 'p:s' => \$PASS, 41 | ); 42 | 43 | sub print_debug { 44 | return if( !$DEBUG ); 45 | 46 | my( $string ) = @_; 47 | print STDERR "DEBUG: $string\n"; 48 | } 49 | 50 | sub raw_format_number { 51 | my( $units, $num, $sig, $max_len, $debug ) = @_; 52 | 53 | $sig = 0 if( $sig < 0 ); 54 | 55 | print "Num: $num\n" if $debug; 56 | 57 | foreach my $factor( sort keys %$units ) { 58 | my $raw = $num / $factor; 59 | 60 | my $string = sprintf( "%." . $sig . "f%s", $raw, $units->{$factor} ); 61 | print "Trying factor: $factor, $raw => $string\n" if $debug; 62 | 63 | if( $raw != 0 and length( $string ) <= $max_len + $sig ) { 64 | print "\tThese are our units\n" if $debug; 65 | 66 | my $left = $max_len - length( $string ); 67 | if( $left < 0 ) { 68 | print "\tcan we pare down the sig?\n" if $debug; 69 | # Return a pared down $sig or what we've got (may not fit in $max_len) 70 | $sig > 0 ? 71 | return &raw_format_number( $units, $num, $sig - 1, $max_len, $debug ) : 72 | return $string; 73 | } elsif( $left > 1 and $factor ne 1 ) { 74 | print "\tadd some decimal places\n" if $debug; 75 | 76 | # Add some decimal places 77 | my $decimal = $left - 1; 78 | return sprintf( "%." . $decimal . "f" . $units->{$factor}, $raw ); 79 | } else { 80 | print "\tas is\n" if $debug; 81 | 82 | return $string; 83 | } 84 | } 85 | # Else, try the next smaller factor 86 | } 87 | 88 | # if we get here, we have no factor 89 | my $string = sprintf( "%." . $sig . "f", $num ); 90 | print "Using $string\n" if $debug; 91 | 92 | if( length( $string ) <= $max_len ) { 93 | return $string; 94 | } else { 95 | $sig > 0 ? 96 | return &raw_format_number( $units, $num, $sig - 1, $max_len, $debug ) : 97 | return $string; 98 | } 99 | } 100 | 101 | sub format_number { 102 | my %units = ( 103 | 1 => '', 104 | 1000 => 'k', 105 | 1000000 => 'm', 106 | 1000000000 => 'g' 107 | ); 108 | 109 | return &raw_format_number( \%units, @_ ); 110 | } 111 | 112 | sub format_memory { 113 | my %units = ( 114 | 1 => 'b', 115 | 1024 => 'K', 116 | 1048576 => 'M', 117 | 1073741824 => 'G', 118 | 1099511627776 => 'T' 119 | ); 120 | 121 | return &raw_format_number( \%units, @_ ); 122 | } 123 | 124 | # Takes microsecons 125 | sub format_microseconds { 126 | my %units = ( 127 | 1000000000 => 'ks', 128 | 1000000 => 's', 129 | 1000 => 'ms', 130 | 1 => 'µs', 131 | 132 | ); 133 | 134 | return &raw_format_number( \%units, @_ ); 135 | } 136 | 137 | sub format_percent { 138 | my( $top, $bottom ) = @_; 139 | 140 | return 0 if( $bottom == 0 ); 141 | 142 | my $raw = sprintf( "%.1f", ($top / $bottom) * 100 ); 143 | 144 | while( length( $raw ) > 4 ) { 145 | chop $raw; 146 | } 147 | if( $raw =~ m/\.$/ ) { 148 | chop $raw; 149 | $raw = ' ' . $raw; 150 | } 151 | 152 | return "$raw%"; 153 | } 154 | 155 | 156 | sub parse_options { 157 | my( $options ) = @_; 158 | 159 | $PASSWORD_ON = grep( /^-p$/, @ARGV ); 160 | 161 | my $opt_res = GetOptions( (%DEFAULT_OPTIONS, %$options) ); 162 | 163 | return 0 if( not $opt_res or $HELP ); 164 | 165 | 166 | return 1; 167 | } 168 | 169 | 170 | sub mysql_call { 171 | my ( $sql, $host, $port ) = @_; 172 | $host = $HOST unless defined $host; 173 | $port = $PORT unless defined $port; 174 | 175 | # Prompt for a password the first time we need it, and only if -p was 176 | # given on the command line (could be passwordless) 177 | if( $PASSWORD_ON and $PASS eq '' ) { 178 | print "Password: "; 179 | system "stty -echo"; $PASS =; 180 | system "stty echo"; 181 | $PASS =~ s/\s+$//g; # filter training whitespace 182 | print "\n"; 183 | } 184 | 185 | 186 | my $command = "mysql"; 187 | 188 | if( defined( $LOGIN_PATH )) { 189 | $command .= ' --login-path=' . $LOGIN_PATH if defined( $LOGIN_PATH ); 190 | # No other options should matter 191 | } else { 192 | $command .= ' --user=' . $USER if defined $USER; 193 | $command .= ' \'--password=' . $PASS . '\'' if $PASS ne ''; 194 | $command .= ' --host=' . $host if defined $host; 195 | $command .= ' --port=' . $port if defined $port; 196 | } 197 | 198 | $command .= " 2>&1"; 199 | 200 | &print_debug( "echo \"$sql\" | $command" ); 201 | my @output = grep( !/^Warning/, (`echo "$sql" | $command`)); 202 | 203 | my $rc = $? >> 8; 204 | if( $rc ) { 205 | die "Error accessing mysql\n"; 206 | } 207 | 208 | return \@output; 209 | } 210 | 211 | 1; 212 | -------------------------------------------------------------------------------- /myq_slave_info: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # Copyright (c) 2008 Yahoo! Inc. All rights reserved. The copyrights 4 | # embodied in the content of this file are licensed by Yahoo! Inc. 5 | # under the BSD (revised) open source license 6 | 7 | use strict; 8 | 9 | use FindBin; 10 | 11 | 12 | # Users can symlink this script and it will still add the correct locations to 13 | # perls path using FindBin::RealBin, but to ensure compatibility with old perl 14 | # we need to be a bit more clever 15 | sub get_lib { 16 | # Start with a sensible default 17 | my $bin_path = $FindBin::Bin; 18 | { 19 | no warnings 'uninitialized'; 20 | my $real_path="$FindBin::RealBin"; #This may fail on Perl < 5.10 21 | if ($real_path ne ''){ # if the value is set, we'll use it 22 | $bin_path = $real_path; 23 | } 24 | } 25 | return $bin_path; 26 | } 27 | 28 | use lib get_lib(); 29 | 30 | use MySQL_Script_Utils; 31 | 32 | sub GetRecords ($) 33 | { 34 | my( $lines ) = @_; 35 | 36 | my @records; 37 | 38 | my $record; 39 | foreach my $line (@$lines) { 40 | chomp $line; 41 | if( $line =~ m/^\*\*\*\*\*\*\*\*\*\*\*\*/ ) { 42 | if( defined( $record )) { 43 | push( @records, $record ); 44 | } 45 | my %hash; 46 | $record = \%hash; 47 | next; 48 | } 49 | die "Bad parsing: $line\n" if( !defined $record ); 50 | 51 | if( $line =~ m/\s*(\w+):\s(.*)$/ ) { 52 | $record->{$1} = $2; 53 | } else { 54 | #die "Bad parsing: $line\n"; 55 | } 56 | } 57 | 58 | return \@records; 59 | } 60 | 61 | my $HOSTFILE = ''; 62 | my $REPEAT = 0; 63 | my $REPEAT_TIME = 10; 64 | my $QUIET = 0; 65 | 66 | my %options = ( 67 | 'H=s' => \$HOSTFILE, 68 | 'r' => \$REPEAT, 69 | 'q' => \$QUIET, 70 | 't=i' => \$REPEAT_TIME, 71 | ); 72 | 73 | if( !&parse_options( \%options ) or $#ARGV > 0 ) { 74 | print STDERR <]] 76 | USAGE 77 | exit; 78 | } 79 | 80 | my @hosts; 81 | 82 | push( @hosts, $HOST ) if( $HOST ne '' ); 83 | 84 | if( $HOSTFILE ne '' ) { 85 | die "Can't read host file: $HOSTFILE\n" if( !-r $HOSTFILE ); 86 | 87 | my @lines = `cat $HOSTFILE`; 88 | foreach my $line( @lines ) { 89 | next if( $line =~ m/^#/ ); 90 | chop $line; 91 | push( @hosts, $line ); 92 | } 93 | } 94 | 95 | push( @hosts, '' ) if( $#hosts == -1 ); 96 | 97 | do { 98 | foreach my $host( @hosts ) { 99 | my $output = &mysql_call( "SHOW SLAVE STATUS\\G", $host ); 100 | 101 | my %slave_status; 102 | my $last_key = ''; 103 | 104 | foreach my $line( @$output ) { 105 | chop $line; 106 | 107 | if( $line =~ m/^\s*(\w+):\s(.*)$/ ) { 108 | $slave_status{lc $1} = $2; 109 | $last_key = $1; 110 | } elsif( $last_key eq 'last_error' ) { 111 | $slave_status{last_error} .= $line; 112 | } 113 | } 114 | $slave_status{last_error} =~ s/\s{2,}//g; 115 | 116 | # Filter out binary data in the CompressedContents Field 117 | $slave_status{last_error} =~ s/CompressedContents.*$/CompressedContents=(gobbeldygook)/g; 118 | 119 | foreach my $key( keys %slave_status ) { 120 | #print "$key => $slave_status{$key}\n"; 121 | } 122 | 123 | if( !$QUIET ) { 124 | print " Replicating from:\t\t"; 125 | } else { 126 | my $short_host = $host; 127 | $short_host = '127.0.0.1' if( $host eq '' ); 128 | #$short_host =~ s/\.\w+\.\w+$//g; 129 | my $date = `date +%I:%M:%S`; 130 | chop $date; 131 | print $date . " "; 132 | print "$short_host -> "; 133 | } 134 | 135 | my $no_repl = 0; 136 | 137 | if( $slave_status{master_host} ne '' and 138 | $slave_status{master_host} ne 'nothing' and 139 | $slave_status{master_host} ne 'noslavehost' ) 140 | { 141 | 142 | # Assumes the credentials are the same for the master (valid?) 143 | my $master_port = ''; 144 | if( $slave_status{master_port} ) { 145 | $master_port = ' --port=' . $slave_status{master_port}; 146 | } 147 | my $master_host = $slave_status{master_host}; 148 | if( $slave_status{master_host} eq '127.0.0.1' and $host ne '' ) { 149 | # We are remote, but we need to check using the slave's 150 | # hostname instead 151 | $master_host = $host; 152 | } 153 | 154 | my $output = &mysql_call( "SHOW MASTER STATUS\\G", $master_host, $master_port ); 155 | 156 | my %master_status; 157 | foreach my $line( @$output ) { 158 | chop $line; 159 | 160 | # This doesn't seem to handle the case of Last_error having a ':' in 161 | # it 162 | if( $line =~ m/^\s*(\w+):\s(.*)$/ ) { 163 | $master_status{lc $1} = $2; 164 | } 165 | } 166 | 167 | my $master_host = $slave_status{master_host}; 168 | print $master_host; 169 | if( $slave_status{master_port} ne '3306' ) { 170 | print ":" . $slave_status{master_port}; 171 | } 172 | 173 | if( !$QUIET ) { 174 | print "\n"; 175 | 176 | print " Master:\t\t\t" . $master_status{file} . "/" . 177 | $master_status{position} . "\n"; 178 | } 179 | 180 | 181 | my $io_diff = '???'; 182 | if( $master_status{file} eq $slave_status{master_log_file} ) { 183 | $io_diff = &format_memory( 184 | $master_status{position} - 185 | $slave_status{read_master_log_pos} ); 186 | } 187 | 188 | if( !$QUIET ) { 189 | print " Slave I/O:\t\t" . $slave_status{slave_io_running} . 190 | "\t" . $slave_status{master_log_file} . "/" . 191 | $slave_status{read_master_log_pos} . "\t$io_diff\n"; 192 | } else { 193 | print "/$io_diff"; 194 | print "!" if( $slave_status{slave_io_running} eq 'No' ); 195 | } 196 | 197 | 198 | my $relay_diff = '???'; 199 | if( $slave_status{master_log_file} eq 200 | $slave_status{relay_master_log_file} ) 201 | { 202 | $relay_diff = &format_memory( 203 | $slave_status{read_master_log_pos} - 204 | $slave_status{exec_master_log_pos} ); 205 | } 206 | 207 | if( !$QUIET ) { 208 | print " Slave Relay:\t" . $slave_status{slave_sql_running} . 209 | "\t" . $slave_status{relay_master_log_file} . "/" . 210 | $slave_status{exec_master_log_pos} . "\t$relay_diff\n"; 211 | } else { 212 | print "/$relay_diff"; 213 | print "!" if( $slave_status{slave_sql_running} eq 'No' ); 214 | } 215 | 216 | if( $slave_status{slave_sql_running} eq 'No' or 217 | $slave_status{slave_io_running} eq 'No' ) 218 | { 219 | if( !$QUIET ) { 220 | print " Slave Errorno:\t\t" . $slave_status{last_errno} . "\n"; 221 | print " Slave Error:\t\t" . $slave_status{last_error} . "\n"; 222 | } 223 | } 224 | } else { 225 | print "Nowhere"; 226 | $no_repl = 1; 227 | } 228 | 229 | unless( $no_repl ) { 230 | my $special_query = &mysql_call( "show processlist\\G", $host ); 231 | my $processes = GetRecords( $special_query ); 232 | 233 | my $t = -1; 234 | my $io_caught_up = 0; 235 | my $sql_caught_up = 0; 236 | foreach my $process (@$processes) { 237 | if ($process->{User} eq "system user") { 238 | &print_debug( $process->{User} . '/' . $process->{State} . '/' . $process->{Time} ); 239 | if ($process->{State} =~ /Waiting for master/) { 240 | $io_caught_up = 1; 241 | } elsif ($process->{State} !~ /master/) { 242 | $t = $process->{Time}; 243 | if ($process->{State} =~ /read all relay log/) { 244 | $sql_caught_up = 1; 245 | } 246 | } 247 | } 248 | } 249 | if ($io_caught_up && $sql_caught_up) { 250 | $t = 0; 251 | } 252 | print " $t secs"; 253 | 254 | 255 | #if( $slave_status{Seconds_Behind_Master} eq '' ) { 256 | #} else { 257 | #if( !$self->{args}->{q} ) { 258 | #print " Seconds Behind: \t\t" . 259 | #$slave_status{Seconds_Behind_Master} . "\n"; 260 | #} else { 261 | #print " " . $slave_status{Seconds_Behind_Master} . " secs"; 262 | #} 263 | #} 264 | } 265 | 266 | print "\n"; 267 | } 268 | sleep $REPEAT_TIME if( $REPEAT ); 269 | } while( $REPEAT ); 270 | 271 | -------------------------------------------------------------------------------- /myq_innodb_status: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # Copyright (c) 2008 Yahoo! Inc. All rights reserved. The copyrights 4 | # embodied in the content of this file are licensed by Yahoo! Inc. 5 | # under the BSD (revised) open source license 6 | 7 | use strict; 8 | 9 | use FindBin; 10 | 11 | # Users can symlink this script and it will still add the correct locations to 12 | # perls path using FindBin::RealBin, but to ensure compatibility with old perl 13 | # we need to be a bit more clever 14 | sub get_lib { 15 | # Start with a sensible default 16 | my $bin_path = $FindBin::Bin; 17 | { 18 | no warnings 'uninitialized'; 19 | my $real_path="$FindBin::RealBin"; #This may fail on Perl < 5.10 20 | if ($real_path ne ''){ # if the value is set, we'll use it 21 | $bin_path = $real_path; 22 | } 23 | } 24 | return $bin_path; 25 | } 26 | 27 | use lib get_lib(); 28 | 29 | use MySQL_Script_Utils; 30 | 31 | use vars qw/ @ARGV $yinst_settings $TIMEOUT $innodb_status $prev_innodb_status $variables /; 32 | 33 | sub parse_status { 34 | my( $output ) = @_; 35 | 36 | my %local_status; 37 | 38 | foreach my $line( @$output ) { 39 | next if( $line =~ m/^Variable_name/ ); 40 | 41 | $line =~ m/^(.*)\t(.*)$/; 42 | 43 | $local_status{lc $1} = $2; 44 | } 45 | 46 | return \%local_status; 47 | } 48 | 49 | sub parse_innodb_status { 50 | my( $output ) = @_; 51 | 52 | my %innodb_status; 53 | 54 | foreach my $line( @$output ) { 55 | 56 | # SEMAPHORES SECTION 57 | if( $line =~ m/^Per second averages calculated from the last (\d+) seconds$/ ) { 58 | $innodb_status{sample_seconds} = $1; 59 | } elsif( $line =~ m/^OS WAIT ARRAY INFO: reservation count (\d+), signal count (\d+)$/ ) { 60 | $innodb_status{reservation_count} = $1; 61 | $innodb_status{signal_count} = $2; 62 | } elsif( $line =~ m/^Mutex spin waits (\d+), rounds (\d+), OS waits (\d+)$/ ) { 63 | $innodb_status{mutex_spin_waits} = $1; 64 | $innodb_status{mutex_rounds} = $2; 65 | $innodb_status{mutex_os_waits} = $3; 66 | } elsif( $line =~ m/^RW-shared spins (\d+), OS waits (\d+); RW-excl spins (\d+), OS waits (\d+)$/ ) { 67 | $innodb_status{rw_shared_spins} = $1; 68 | $innodb_status{rw_shared_os_waits} = $2; 69 | $innodb_status{rw_excl_spins} = $3; 70 | $innodb_status{rw_excl_os_waits} = $4; 71 | # TRANSACTIONS SECTION 72 | } elsif( $line =~ m/^Trx id counter (\d+) (\d+)$/ ) { 73 | $innodb_status{trx_id_counter_min} = $1; 74 | $innodb_status{trx_id_counter_max} = $2; 75 | # FILE I/O SECTION 76 | } elsif( $line =~ m/^Pending normal aio reads: (\d+), aio writes: (\d+),$/ ) { 77 | $innodb_status{pending_normal_aio_reads} = $1; 78 | $innodb_status{pending_normal_aio_writes} = $2; 79 | } elsif( $line =~ m/^ ibuf aio reads: (\d+), log i\/o's: (\d+), sync i\/o's: (\d+)$/ ) { 80 | $innodb_status{ibuf_aio_reads} = $1; 81 | $innodb_status{ibuf_log_ios} = $2; 82 | $innodb_status{ibuf_sync_ios} = $3; 83 | } elsif( $line =~ m/^Pending flushes \(fsync\) log: (\d+); buffer pool: (\d+)$/ ) { 84 | $innodb_status{pending_flushes_log} = $1; 85 | $innodb_status{pending_flushes_buffer_pool} = $2; 86 | } elsif( $line =~ m/^(\d+) OS file reads, (\d+) OS file writes, (\d+) OS fsyncs$/ ) { 87 | $innodb_status{os_file_reads} = $1; 88 | $innodb_status{os_file_writes} = $2; 89 | $innodb_status{os_fsyncs} = $3; 90 | } elsif( $line =~ m/^(\d+\.\d+) reads\/s, (\d+) avg bytes\/read, (\d+\.\d+) writes\/s, (\d+\.\d+) fsyncs\/s$/ ) { 91 | $innodb_status{os_file_reads_per_sec} = $1; 92 | $innodb_status{os_file_bytes_per_read} = $2; 93 | $innodb_status{os_file_writes_per_sec} = $3; 94 | $innodb_status{os_file_fsyncs_per_sec} = $4; 95 | # INSERT BUFFER AND ADAPTIVE HASH INDEX SECTION 96 | # LOG SECTION 97 | } elsif( $line =~ m/^Log sequence number\s+(\d+)$/ ) { 98 | $innodb_status{log_sequence_number} = $1; 99 | } elsif( $line =~ m/^Log flushed up to\s+(\d+)$/ ) { 100 | $innodb_status{log_flushed_to} = $1; 101 | } elsif( $line =~ m/^Last checkpoint at\s+(\d+)$/ ) { 102 | $innodb_status{log_checkpoint_at} = $1; 103 | } elsif( $line =~ m/^(\d+) pending log writes, (\d+) pending chkp writes$/ ) { 104 | $innodb_status{pending_log_writes} = $1; 105 | $innodb_status{pending_chkp_writes} = $2; 106 | } elsif( $line =~ m/^(\d+) log i\/o's done, (\d+\.\d+) log i\/o's\/second$/ ) { 107 | $innodb_status{log_ios_done} = $1; 108 | $innodb_status{iog_ios_per_sec} = $2; 109 | # BUFFER POOL AND MEMORY SECTION 110 | } elsif( $line =~ m/^Total memory allocated (\d+); in additional pool allocated (\d+)$/ ) { 111 | $innodb_status{memory_allocated} = $1; 112 | $innodb_status{additional_pool_allocated} = $2; 113 | } elsif( $line =~ m/^Buffer pool size (\d+)$/ ) { 114 | $innodb_status{buffer_pool_size} = $1; 115 | } elsif( $line =~ m/^Free buffers (\d+)$/ ) { 116 | $innodb_status{free_buffers} = $1; 117 | } elsif( $line =~ m/^Database pages (\d+)$/ ) { 118 | $innodb_status{database_pages} = $1; 119 | } elsif( $line =~ m/^Modified db pages (\d+)$/ ) { 120 | $innodb_status{modified_db_pages} = $1; 121 | } elsif( $line =~ m/^Pending reads (\d+)$/ ) { 122 | $innodb_status{pending_reads} = $1; 123 | } elsif( $line =~ m/^Pending writes: LRU (\d+), flush list (\d+), single page (\d+)$/ ) { 124 | $innodb_status{pending_writes_lru} = $1; 125 | $innodb_status{pending_writes_flush_list} = $2; 126 | $innodb_status{pending_writes_single_page} = $3; 127 | } elsif( $line =~ m/^Pages read (\d+), created (\d+), written (\d+)$/) { 128 | $innodb_status{pages_read} = $1; 129 | $innodb_status{pages_created} = $2; 130 | $innodb_status{pages_written} = $3; 131 | } elsif( $line =~ m/^(\d+\.\d+) reads\/s, (\d+\.\d+) creates\/s, (\d+\.\d+) writes\/s$/ ) { 132 | $innodb_status{pages_reads_per_sec} = $1; 133 | $innodb_status{pages_creates_per_sec} = $2; 134 | $innodb_status{pages_writes_per_sec} = $3; 135 | } elsif( $line =~ m/^Buffer pool hit rate (\d+) \/ (\d+)/ ) { 136 | $innodb_status{hit_rate} = ($1 / $2) * 100; 137 | # ROW OPERATIONS SECTIONS 138 | } elsif( $line =~ m/^(\d+) queries inside InnoDB, (\d+) queries in queue$/ ) { 139 | $innodb_status{queries_in_innodb} = $1; 140 | $innodb_status{queries_in_queue} = $2; 141 | } elsif( $line =~ m/^(\d+\.\d+) inserts\/s, (\d+\.\d+) updates\/s, (\d+\.\d+) deletes\/s, (\d+\.\d+) reads\/s$/ ) { 142 | $innodb_status{inserts_per_sec} = $1; 143 | $innodb_status{updates_per_sec} = $2; 144 | $innodb_status{deletes_per_sec} = $3; 145 | $innodb_status{reads_per_sec} = $4; 146 | } 147 | 148 | } 149 | 150 | return \%innodb_status; 151 | } 152 | 153 | sub counter_diff { 154 | my( $key ) = @_; 155 | 156 | return $innodb_status->{$key} . '!' 157 | if( !defined( $prev_innodb_status )); 158 | 159 | return $innodb_status->{$key} - 160 | $prev_innodb_status->{$key}; 161 | } 162 | 163 | 164 | my $MODE = 'one'; 165 | my $REPEAT_TIME = 60; 166 | 167 | my $password_on = grep( /-p/, @ARGV ); 168 | 169 | my %options = ( 170 | 't=i' => \$REPEAT_TIME, 171 | ); 172 | 173 | if( !&parse_options( \%options ) or $#ARGV > 0 ) { 174 | print STDERR <] [sema|row|file|log|mem|one] 176 | USAGE 177 | exit; 178 | } 179 | 180 | $MODE = shift @ARGV if( $#ARGV == 0 ); 181 | 182 | die "Mode must be one of 'one', 'sema', 'row', 'file', 'log', 'mem', 'extramem'\n" if( $MODE !~ m/sema|row|file|log|mem|one/ ); 183 | 184 | die "Repeat time must be at least than 10 seconds\n" if( $REPEAT_TIME < 10 ); 185 | 186 | 187 | my $cache_file = "/tmp/myq_innodb_status_$HOST"; 188 | my $count = 0; 189 | my $lines_per_header = 15;; 190 | my $variables = &parse_status( &mysql_call( "SHOW GLOBAL VARIABLES" )); 191 | 192 | LOOP: while( 1 ) { 193 | my $start_time = time; 194 | my @array; 195 | my $output = \@array; 196 | 197 | if( -f $cache_file ) { 198 | (open( CACHEL, "$cache_file" ) and flock( CACHEL, 2 )) 199 | or die "Could not get lock\n"; 200 | } 201 | 202 | if( -s $cache_file and ( time - (stat( $cache_file ))[9] < 10 )) { 203 | # Keep the cache 204 | &print_debug( "Keeping the cache: $cache_file" ); 205 | open( CACHE, "<$cache_file" ) || die "Could not open cache: $cache_file\n"; 206 | while( my $line = ) { 207 | push( @$output, $line ); 208 | } 209 | close( CACHE ); 210 | } else { 211 | # (Re-)generate the cache 212 | &print_debug( "Generating the cache: $cache_file" ); 213 | $output = &mysql_call( "SHOW /*!50000 ENGINE */ INNODB STATUS\\G" ); 214 | if( open( CACHE, ">$cache_file" )) { 215 | foreach my $line( @$output ) { 216 | print CACHE $line; 217 | } 218 | close( CACHE ); 219 | } else { 220 | print "Skipping cache\n"; 221 | } 222 | } 223 | flock( CACHEL, 8 ); 224 | 225 | 226 | $innodb_status = &parse_innodb_status( $output ); 227 | 228 | foreach my $key( keys %$innodb_status ) { 229 | &print_debug( "$key => " . $innodb_status->{$key} ); 230 | } 231 | 232 | if( $innodb_status->{sample_seconds} <= 1 ) { 233 | # Occasionally we get a status that has a very small sample space 234 | # and skewed values... hence sleep 10 and try to fetch it again... 235 | sleep 10; 236 | next LOOP; 237 | } 238 | 239 | #foreach my $key( sort keys %$innodb_status ) { 240 | #print "$key => " . $innodb_status->{$key} . "\n"; 241 | #} 242 | 243 | my $pretty_time = `date '+\%H:\%M:\%S'`; 244 | chop $pretty_time; 245 | 246 | if( $MODE eq 'one' ) { 247 | printf( "row %-20s %-13s %-14s %-4s %-15s %-10s\n", 248 | 'Inno Engine (/sec)', 249 | 'Buffer (/sec)', 250 | '(%)', 251 | 'Log', 252 | 'OS (/sec)', 253 | 'Semaphores', 254 | ) if( $count % $lines_per_header == 0 ); 255 | printf( "time % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s %4s %4s %4s\n", 256 | 'read', 'ins', 'upd','del', 257 | 'new', 'read', 'wrte', 258 | 'full', 'dirt', 'hit', 259 | 'io/s', 260 | 'read', 'wrte', 'fsyc', 261 | 'spw', 'rnds', 'osw', 262 | ) if( $count % $lines_per_header == 0 ); 263 | printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4.0f % 4.0f % 4.0f % 4s % 4s % 4s % 4s % 4s % 4s % 4s ", 264 | $pretty_time, 265 | &format_number( $innodb_status->{reads_per_sec}, 1, 4 ), 266 | &format_number( $innodb_status->{inserts_per_sec}, 1, 4 ), 267 | &format_number( $innodb_status->{updates_per_sec}, 1, 4 ), 268 | &format_number( $innodb_status->{deletes_per_sec}, 1, 4 ), 269 | 270 | &format_number( $innodb_status->{pages_creates_per_sec}, 1, 4 ), 271 | &format_number( $innodb_status->{pages_reads_per_sec}, 1, 4 ), 272 | &format_number( $innodb_status->{pages_writes_per_sec}, 1, 4 ), 273 | 274 | &format_percent( $innodb_status->{database_pages}, 275 | $innodb_status->{buffer_pool_size} ), 276 | &format_percent( $innodb_status->{modified_db_pages}, 277 | $innodb_status->{buffer_pool_size} ), 278 | $innodb_status->{hit_rate}, 279 | 280 | &format_number( $innodb_status->{iog_ios_per_sec}, 1, 4 ), 281 | 282 | &format_number( $innodb_status->{os_file_reads_per_sec}, 1, 4), 283 | &format_number( $innodb_status->{os_file_writes_per_sec}, 1, 4), 284 | &format_number( $innodb_status->{os_file_fsyncs_per_sec}, 1, 4), 285 | 286 | &format_number( &counter_diff( 'mutex_spin_waits' ), 1, 4), 287 | &format_number( &counter_diff( 'mutex_rounds' ), 1, 4), 288 | 289 | &format_number( &counter_diff( 'mutex_os_waits' ), 1, 4 ), 290 | 291 | #&format_percent( 292 | #&counter_diff( 'mutex_spin_waits' ) - 293 | #&counter_diff( 'mutex_os_waits' ), 294 | #&counter_diff( 'mutex_spin_waits' ) 295 | #), 296 | #&format_percent( 297 | #&counter_diff( 'rw_shared_spins' ) - 298 | #&counter_diff( 'rw_shared_os_waits' ), 299 | #&counter_diff( 'rw_shared_spins' ) 300 | #), 301 | #&format_percent( 302 | #&counter_diff( 'rw_excl_spins' ) - 303 | #&counter_diff( 'rw_excl_os_waits' ), 304 | # 305 | #&counter_diff( 'rw_excl_spins' )), 306 | ); 307 | 308 | } elsif( $MODE eq 'sema' ) { 309 | printf( "sema % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s\n", 310 | 'resv cnt', 311 | 'sig cnt', 312 | 'mut_sw', 313 | 'mut_rnds', 314 | 'mut_osw', 315 | 'rwsh_sp', 316 | 'rwsh_os', 317 | 'rwex_sp', 318 | 'rwex_os', 319 | ) if( $count % $lines_per_header == 0 ); 320 | printf( "%s % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s", 321 | $pretty_time, 322 | &format_number( &counter_diff( 'reservation_count' ), 2, 6 ), 323 | &format_number( &counter_diff( 'signal_count' ), 2, 6 ), 324 | &format_number( &counter_diff( 'mutex_spin_waits' ), 2, 6 ), 325 | &format_number( &counter_diff( 'mutex_rounds' ), 2, 6 ), 326 | &format_number( &counter_diff( 'mutex_os_waits' ), 2, 6 ), 327 | &format_number( &counter_diff( 'rw_shared_spins' ), 2, 6 ), 328 | &format_number( &counter_diff( 'rw_shared_os_waits' ), 2, 6 ), 329 | &format_number( &counter_diff( 'rw_excl_spins' ), 2, 6 ), 330 | &format_number( &counter_diff( 'rw_excl_os_waits' ), 2, 6 ), 331 | ); 332 | 333 | } elsif( $MODE eq 'row' ) { 334 | printf( "row % 8s % 8s % 8s % 8s % 8s % 8s\n", 335 | 'ins/sec', 336 | 'upd/sec', 337 | 'del/sec', 338 | 'read/sec', 339 | ) if( $count % $lines_per_header == 0 ); 340 | printf( "%s % 8s % 8s % 8s % 8s % 8s % 8s", 341 | $pretty_time, 342 | &format_number( $innodb_status->{reads_per_sec}, 2, 6 ), 343 | &format_number( $innodb_status->{inserts_per_sec}, 2, 6 ), 344 | &format_number( $innodb_status->{updates_per_sec}, 2, 6 ), 345 | &format_number( $innodb_status->{deletes_per_sec}, 2, 6 ), 346 | ) 347 | } elsif( $MODE eq 'extrarow' ) { 348 | printf( "row % 8s % 8s % 8s % 8s % 8s % 8s\n", 349 | 'qry_inno', 350 | 'qry_que', 351 | 'ins/sec', 352 | 'upd/sec', 353 | 'del/sec', 354 | 'read/sec', 355 | ) if( $count % $lines_per_header == 0 ); 356 | printf( "%s % 8s % 8s % 8s % 8s % 8s % 8s", 357 | $pretty_time, 358 | &format_number( $innodb_status->{queries_in_innodb}, 2, 6 ), 359 | &format_number( $innodb_status->{queries_in_queue}, 2, 6 ), 360 | &format_number( $innodb_status->{reads_per_sec}, 2, 6 ), 361 | &format_number( $innodb_status->{inserts_per_sec}, 2, 6 ), 362 | &format_number( $innodb_status->{updates_per_sec}, 2, 6 ), 363 | &format_number( $innodb_status->{deletes_per_sec}, 2, 6 ), 364 | ) 365 | } elsif( $MODE eq 'file' ) { 366 | printf( "file % 8s % 8s % 8s % 8s % 8s % 8s\n", 367 | 'os_f_r', 368 | 'os_r/sec', 369 | 'os_f_w', 370 | 'os_w/s', 371 | 'os_fsncs', 372 | 'os_fs/s', 373 | ) if( $count % $lines_per_header == 0 ); 374 | printf( "%s % 8s % 8s % 8s % 8s % 8s % 8s", 375 | $pretty_time, 376 | &format_number( &counter_diff( 'os_file_reads' ), 2, 6 ), 377 | &format_number( $innodb_status->{os_file_reads_per_sec}, 2, 6), 378 | &format_number( &counter_diff( 'os_file_writes' ), 2, 6 ), 379 | &format_number( $innodb_status->{os_file_writes_per_sec}, 2, 6), 380 | &format_number( &counter_diff( 'os_fsyncs' ), 2, 6 ), 381 | &format_number( $innodb_status->{os_file_fsyncs_per_sec}, 2, 6), 382 | ); 383 | } elsif( $MODE eq 'extrafile' ) { 384 | printf( "file % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s\n", 385 | 'pen_aior', 386 | 'pen_aiow', 387 | 'ib_aior', 388 | 'iblogio', 389 | 'ibsyncio', 390 | 'pen_lgfl', 391 | 'pen_bpfl', 392 | 'os_f_r', 393 | 'os_f_w', 394 | 'os_fsncs', 395 | 'os_r/sec', 396 | 'os_b/r', 397 | 'os_w/s', 398 | 'os_fs/s', 399 | ) if( $count % $lines_per_header == 0 ); 400 | printf( "%s % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s", 401 | $pretty_time, 402 | &format_number( $innodb_status->{pending_normal_aio_reads}, 2, 6 ), 403 | &format_number( $innodb_status->{pending_normal_aio_writes}, 2, 6 ), 404 | &format_number( $innodb_status->{ibuf_aio_reads}, 2, 6 ), 405 | &format_number( $innodb_status->{ibuf_log_ios}, 2, 6 ), 406 | &format_number( $innodb_status->{ibuf_sync_ios}, 2, 6 ), 407 | &format_number( $innodb_status->{pending_flushes_log}, 2, 6 ), 408 | &format_number( $innodb_status->{pending_flushes_buffer_pool}, 2, 6 ), 409 | &format_number( &counter_diff( 'os_file_reads' ), 2, 6 ), 410 | &format_number( &counter_diff( 'os_file_writes' ), 2, 6 ), 411 | &format_number( &counter_diff( 'os_fsyncs' ), 2, 6 ), 412 | &format_number( $innodb_status->{os_file_reads_per_sec}, 2, 6 ), 413 | &format_memory( $innodb_status->{os_file_bytes_per_read}, 2, 6 ), 414 | &format_number( $innodb_status->{os_file_writes_per_sec}, 2, 6 ), 415 | &format_number( $innodb_status->{os_file_fsyncs_per_sec}, 2, 6 ), 416 | ); 417 | } elsif( $MODE eq 'log' ) { 418 | printf( "log % 8s % 8s % 8s % 8s % 8s % 8s\n", 419 | 'log_io', 420 | 'log_io/s', 421 | 'Chpt Age', 422 | 'Pct fill', 423 | 'Mb W' 424 | ) if( $count % $lines_per_header == 0 ); 425 | printf( "%s % 8s % 8s % 8s % 8s % 8s", 426 | $pretty_time, 427 | &format_number( &counter_diff( 'log_ios_done' ), 2, 6 ), 428 | &format_number( $innodb_status->{iog_ios_per_sec}, 2, 6 ), 429 | &format_memory( ($innodb_status->{log_sequence_number} - 430 | $innodb_status->{log_checkpoint_at}), 2, 6 ), 431 | &format_percent( ($innodb_status->{log_sequence_number} - 432 | $innodb_status->{log_checkpoint_at}), 433 | ($variables->{innodb_log_file_size} * 434 | $variables->{innodb_log_files_in_group}), 2, 6), 435 | &format_number( &counter_diff( 'log_sequence_number' )/1024/1024, 2, 6 ) 436 | ); 437 | } elsif( $MODE eq 'extralog' ) { 438 | printf( "log % 8s % 8s % 8s % 8s\n", 439 | 'pen_logw', 440 | 'pen_chkw', 441 | 'log_io', 442 | 'log_io/s' 443 | ) if( $count % $lines_per_header == 0 ); 444 | printf( "%s % 8s % 8s % 8s % 8s", 445 | $pretty_time, 446 | &format_number( $innodb_status->{pending_log_writes}, 2, 6 ), 447 | &format_number( $innodb_status->{pending_chkp_writes}, 2, 6 ), 448 | &format_number( &counter_diff( 'log_ios_done' ), 2, 6 ), 449 | &format_number( $innodb_status->{iog_ios_per_sec}, 2, 6 ), 450 | ); 451 | } elsif( $MODE eq 'mem' ) { 452 | printf( "mem % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s\n", 453 | 'pgs_db', 454 | 'pgdb_mod', 455 | 'pgs_crt', 456 | 'pgs_c/s', 457 | 'pgs_wrt', 458 | 'pgs_w/s', 459 | 'pgs_read', 460 | 'pgs_r/s' 461 | ) if( $count % $lines_per_header == 0 ); 462 | printf( "%s % 8s % 8s % 8s % 8s % 8s % 8s % 8s % 8s", 463 | $pretty_time, 464 | &format_percent( $innodb_status->{database_pages}, 465 | $innodb_status->{buffer_pool_size} ), 466 | &format_percent( $innodb_status->{modified_db_pages}, 467 | $innodb_status->{database_pages} ), 468 | &format_number( &counter_diff( 'pages_created' ), 2, 6 ), 469 | &format_number( $innodb_status->{pages_creates_per_sec}, 2, 6 ), 470 | &format_number( &counter_diff( 'pages_written' ), 2, 6 ), 471 | &format_number( $innodb_status->{pages_writes_per_sec}, 2, 6 ), 472 | &format_number( &counter_diff( 'pages_read' ), 2, 6 ), 473 | &format_number( $innodb_status->{pages_reads_per_sec}, 2, 6 ), 474 | ); 475 | } elsif( $MODE eq 'extramem' ) { 476 | printf( "mem % 8s % 8s % 8s % 8s % 8s\n", 477 | 'pen_read', 478 | 'pen_wlru', 479 | 'pen_wfll', 480 | 'pen_wsp', 481 | ) if( $count % $lines_per_header == 0 ); 482 | printf( "%s % 8s % 8s % 8s % 8s", 483 | $pretty_time, 484 | &format_number( $innodb_status->{pending_reads}, 2, 6 ), 485 | &format_number( $innodb_status->{pending_writes_lru}, 2, 6 ), 486 | &format_number( $innodb_status->{pending_writes_flush_list}, 2, 6 ), 487 | &format_number( $innodb_status->{pending_writes_single_page}, 2, 6 ), 488 | ); 489 | } 490 | 491 | print "\n"; 492 | 493 | my $end_time = time; 494 | $prev_innodb_status = $innodb_status; 495 | 496 | # By default try to sleep until 5 seconds after the minute 497 | my $run_time = $end_time - $start_time; 498 | &print_debug( "$REPEAT_TIME - $run_time" ); 499 | my $sleep_time = $REPEAT_TIME - $run_time; 500 | $sleep_time = 0 if( $sleep_time < 0 ); 501 | &print_debug( "Sleeping: $sleep_time\n" ); 502 | sleep $sleep_time; 503 | 504 | 505 | $count++; 506 | } 507 | -------------------------------------------------------------------------------- /myq_status: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # Copyright (c) 2008 Yahoo! Inc. All rights reserved. The copyrights 4 | # embodied in the content of this file are licensed by Yahoo! Inc. 5 | # under the BSD (revised) open source license 6 | 7 | use strict; 8 | 9 | # Enable unicode output (microseconds units designator) 10 | use utf8; 11 | binmode(STDOUT, ":utf8"); 12 | 13 | use FindBin; 14 | 15 | # Users can symlink this script and it will still add the correct locations to 16 | # perls path using FindBin::RealBin, but to ensure compatibility with old perl 17 | # we need to be a bit more clever 18 | sub get_lib { 19 | # Start with a sensible default 20 | my $bin_path = $FindBin::Bin; 21 | { 22 | no warnings 'uninitialized'; 23 | my $real_path="$FindBin::RealBin"; #This may fail on Perl < 5.10 24 | if ($real_path ne ''){ # if the value is set, we'll use it 25 | $bin_path = $real_path; 26 | } 27 | } 28 | return $bin_path; 29 | } 30 | 31 | use lib get_lib(); 32 | 33 | use MySQL_Script_Utils; 34 | 35 | use vars qw/ @ARGV $yinst_settings $TIMEOUT $FILENAME $status $prev_status $variables $input_file /; 36 | 37 | sub parse_status { 38 | my( $output ) = @_; 39 | 40 | my %local_status; 41 | 42 | foreach my $line( @$output ) { 43 | next if( $line =~ m/^Variable_name/ ); 44 | 45 | $line =~ m/^(.*)\t(.*)$/; 46 | 47 | $local_status{lc $1} = $2; 48 | } 49 | 50 | 51 | foreach my $key( sort keys %local_status ) { 52 | # print "$key => " . $local_status{$key} . "\n"; 53 | } 54 | 55 | return \%local_status; 56 | } 57 | 58 | sub parse_mysqladmin_status { 59 | my( $output ) = @_; 60 | 61 | my %local_status; 62 | 63 | foreach my $line( @$output ) { 64 | next if( $line =~ m/^\| Variable_name/ ); 65 | next if $line =~ m/^\+\-\-\-\-/; 66 | # print "$line"; 67 | 68 | $line =~ m/^\|\s(\w+)\s+\|\s(\S+)\s+\|/; 69 | # $line =~ m/^\| (\w+)/; 70 | 71 | # print "$1 => $2\n"; 72 | 73 | $local_status{lc $1} = $2; 74 | } 75 | 76 | 77 | foreach my $key( sort keys %local_status ) { 78 | # print "$key => " . $local_status{$key} . "\n"; 79 | } 80 | 81 | return \%local_status; 82 | 83 | } 84 | 85 | sub counter_diff { 86 | my( $key ) = @_; 87 | 88 | return $status->{$key} . '!' 89 | if( !defined( $prev_status )); 90 | 91 | return $status->{$key} - 92 | $prev_status->{$key}; 93 | } 94 | 95 | sub counter_per_second { 96 | my( $key ) = @_; 97 | 98 | my $diff = &counter_diff( $key ); 99 | return $diff unless defined $prev_status; 100 | 101 | my $seconds = $status->{uptime} - 102 | $prev_status->{uptime}; 103 | 104 | # print $status->{uptime} . " ?? " . $prev_status->{uptime} . "\n"; 105 | 106 | die "Invalid time? \n" if( $seconds <= 0 ); 107 | 108 | return $diff / $seconds; 109 | } 110 | 111 | sub group_counter_per_second { 112 | my @search_keys = @_; 113 | #print "Search keys: " . join( ', ', @search_keys ) . "\n"; 114 | # Keys can be regexes, so we need to build the array of all the actual variables we are grouping 115 | my @server_keys = keys %$status; 116 | #print "Server keys: " . join( ', ', @server_keys ) . "\n"; 117 | my @actual_keys; 118 | foreach my $search_key( @search_keys ) { 119 | my @found = grep { $_ =~ m/^$search_key$/i } @server_keys; 120 | push( @actual_keys, @found ); 121 | } 122 | my %dedup; 123 | @dedup{@actual_keys} = @actual_keys; 124 | @actual_keys = values %dedup; 125 | #print "Actual keys: " . join( ', ', @actual_keys ) . "\n"; 126 | my $total_diff = 0; 127 | foreach my $key( @actual_keys ) { 128 | $total_diff += &counter_diff( $key ); 129 | } 130 | return $total_diff unless defined $prev_status; 131 | 132 | my $seconds = $status->{uptime} - $prev_status->{uptime}; 133 | die "Invalid time? \n" if( $seconds <= 0 ); 134 | return $total_diff / $seconds; 135 | } 136 | 137 | sub counter_percent { 138 | my( $key1, $key2 ) = @_; 139 | 140 | return 0 if( !defined( $prev_status )); 141 | 142 | my $diff1 = &counter_diff( $key1 ); 143 | my $diff2 = &counter_diff( $key2 ); 144 | 145 | return $diff1 / $diff2; 146 | } 147 | 148 | 149 | 150 | my $MODE = 'one'; 151 | my $REPEAT_TIME = 1; 152 | 153 | my $password_on = grep( /-p/, @ARGV ); 154 | 155 | my %options = ( 156 | 't=i' => \$REPEAT_TIME, 157 | 'f=s' => \$FILENAME, 158 | ); 159 | my $usage = <] [-t ] [-f ] 161 | 162 | -? Print this message and exit 163 | -d Print debug information 164 | -f Input File to read from 165 | -h MySQL host to connect to 166 | -P MySQL port to connect to 167 | -t Repeat time (default 10 seconds) 168 | -u Username to connect to MySQL 169 | 170 | MODE 'myisam', 'innodb', 'innodb_buffer_pool', 'innodb_io', 171 | 'innodb_log', 'innodb_row', 'innodb_flush', 'innodb_hash', 172 | 'innodb_waits', 'innodb_hist', 'coms', 'commands', 'qcache', 173 | 'cttf', 'throughput', 'query', 'temp', 'handler', 'wsrep', 174 | 'wsrep_latency', 175 | 176 | USAGE 177 | 178 | if( !&parse_options( \%options ) or $#ARGV > 0 ) { 179 | print STDERR $usage; 180 | exit; 181 | } 182 | 183 | $MODE = shift @ARGV if( $#ARGV == 0 ); 184 | 185 | die $usage if( $MODE !~ m/coms|myisam|innodb|innodb_buffer_pool|innodb_io|innodb_log|innodb_row|innodb_flush|innodb_hash|innodb_waits|innodb_hist|commands|qcache|cttf|throughput|query|temp|handler|wsrep|wsrep_latency/ ); 186 | 187 | die "Repeat time must be at least 1 seconds\n" if( $REPEAT_TIME < 1 ); 188 | 189 | 190 | my $cache_file = "/tmp/myq_status_$HOST"; 191 | my $count = 0; 192 | my $lines_per_header = 15; 193 | 194 | sub process_global_status { 195 | my( $count, $pretty_time ) = @_; 196 | 197 | foreach my $key( keys %$status ) { 198 | &print_debug( "$key => " . $status->{$key} ); 199 | } 200 | # 201 | # foreach my $key( sort keys %$status ) { 202 | # print "$key => " . $status->{$key} . "\n"; 203 | # } 204 | 205 | 206 | 207 | if( $MODE eq 'myisam' ) { 208 | printf( "row %-5s %-4s %12s %12s\n", 209 | 'Table', 210 | 'Key Buffer', 211 | 'Reads', 212 | 'Writes', 213 | ) if( $count % $lines_per_header == 0 ); 214 | printf( "time % 5s % 5s % 5s % 5s % 5s % 5s % 5s % 5s % 5s\n", 215 | 'lock', 216 | 'full', '%', 'most', 217 | 'reqs', 'miss', 'reqs', 'miss', 218 | ) if( $count % $lines_per_header == 0 ); 219 | 220 | my $key_buff_used = $variables->{key_buffer_size} - ($status->{key_blocks_unused} * $variables->{key_cache_block_size}); 221 | printf( "%s % 5s % 5s % 5s % 5s % 5s % 5s % 5s % 5s", 222 | $pretty_time, 223 | 224 | &format_percent( &counter_diff( 'table_locks_waited' ), 225 | &counter_diff( 'table_locks_immediate' ) + 226 | &counter_diff( 'table_locks_waited' ) 227 | ), 228 | 229 | &format_memory( $key_buff_used ), 230 | &format_percent( $key_buff_used, $variables->{key_buffer_size} ), 231 | &format_percent( ($status->{key_blocks_used} * $variables->{key_cache_block_size}), $variables->{key_buffer_size} ), 232 | 233 | &format_number( &counter_per_second( 'key_read_requests' ), 1, 4 ), 234 | &format_percent( &counter_diff( 'key_reads'), &counter_diff( 'key_read_requests' )), 235 | &format_number( &counter_per_second( 'key_write_requests' ), 1, 4 ), 236 | &format_percent( &counter_diff( 'key_writes'), &counter_diff( 'key_write_requests' )), 237 | ); 238 | } elsif( $MODE eq 'coms' ) { 239 | printf( "Command Groups\n" ) if( $count % $lines_per_header == 0 ); 240 | printf( "time % 5s % 5s % 5s % 5s % 5s % 5s % 5s % 5s % 5s\n", 241 | 'sel', 'dml', 'ddl', 'admin', 'show', 'set', 'lock', 'trx', 'xa' 242 | ) if( $count % $lines_per_header == 0 ); 243 | printf( "%s % 5s % 5s % 5s % 5s % 5s % 5s % 5s % 5s % 5s", 244 | $pretty_time, 245 | &format_number( &counter_per_second( 'com_select' ), 1, 4 ), 246 | &format_number( &group_counter_per_second( 'com_insert.*', 'com_update.*', 'com_delete.*', 'Com_load', 'Com_replace.*', 'Com_truncate' ), 1, 4 ), 247 | &format_number( &group_counter_per_second( 'com_alter.*', 'com_create.*', 'com_drop.*', 'com_rename_table' ), 1, 4 ), 248 | &format_number( &group_counter_per_second( 'com_admin.*' ), 1, 4 ), 249 | &format_number( &group_counter_per_second( 'com_show.*' ), 1, 4 ), 250 | &format_number( &group_counter_per_second( 'com_set.*' ), 1, 4 ), 251 | &format_number( &group_counter_per_second( 'com_lock.*', 'com_unlock.*' ), 1, 4 ), 252 | &format_number( &group_counter_per_second( 'com_begin', 'com_commit', 'com_rollback.*', 'com_savepoint' ), 1, 4 ), 253 | &format_number( &group_counter_per_second( 'com_xa.*' ), 1, 4 ), 254 | 255 | ); 256 | 257 | } elsif( $MODE eq 'commands' ) { 258 | printf( "row %-19s \n", 259 | 'Total Commands ' 260 | ) if( $count % $lines_per_header == 0 ); 261 | 262 | printf( "%s % 4s ", 263 | $pretty_time, 264 | &format_number( &counter_diff( 'questions' ), 1, 4 ), 265 | ); 266 | 267 | my @commands = keys %$status; 268 | my %run_cmds; 269 | foreach my $command( @commands ) { 270 | next if( $command !~ m/^com_(.*)$/ ); 271 | 272 | my $short = $1; 273 | 274 | my $value = &counter_diff( $command ); 275 | if( $value > 0 ) { 276 | if( defined( $run_cmds{$value} )) { 277 | my $arr = $run_cmds{$value}; 278 | push( @$arr, $short ); 279 | $run_cmds{$value} = $arr; 280 | } else{ 281 | my @arr = ( $short ); 282 | $run_cmds{$value} = \@arr; 283 | } 284 | } 285 | 286 | } 287 | foreach my $value( sort { $b <=> $a } (keys %run_cmds) ) { 288 | my $arr = $run_cmds{$value}; 289 | foreach my $cmd( @$arr ) { 290 | print $cmd . "(" . &format_number( $value, 1, 4 ) . ") "; 291 | } 292 | } 293 | } elsif( $MODE eq 'qcache' ) { 294 | printf( "row %-19s %-17s %-4s \n", 295 | 'Query Cache Info' 296 | ) if( $count % $lines_per_header == 0 ); 297 | printf( "time % 6s % 4s % 4s % 5s % 4s % 5s % 4s % 5s % 4s % 4s % 5s % 7s \n", 298 | 'type', 'sel', 'hits', '%hits', 'ins', '%ins', 'notc', '%notc', 'tot', 'lowm', 299 | 'full', 'free' 300 | ) if( $count % $lines_per_header == 0 ); 301 | printf( "%s % 6s % 4s % 4s % 5s % 4s % 5s % 4s % 4s % 4s % 4s % 5s % 7s ", 302 | $pretty_time, 303 | $variables->{query_cache_type}, 304 | &format_number( &counter_per_second( 'qcache_hits' ) + 305 | &counter_per_second( 'com_select' ), 1, 4 ), 306 | &format_number( &counter_per_second( 'qcache_hits' ), 1, 4 ), 307 | &format_percent( &counter_diff( 'qcache_hits' ), 308 | &counter_diff( 'com_select' ) + &counter_diff( 'qcache_hits' ) ), 309 | &format_number( &counter_per_second( 'qcache_inserts' ), 1, 4 ), 310 | &format_percent( &counter_diff( 'qcache_inserts' ), 311 | &counter_diff( 'com_select' ) + &counter_diff( 'qcache_hits' ) ), 312 | &format_number( &counter_per_second( 'qcache_not_cached' ), 1, 4 ), 313 | &format_percent( &counter_diff( 'qcache_not_cached' ), 314 | &counter_diff( 'com_select' ) + &counter_diff( 'qcache_hits' ) ), 315 | &format_number( $status->{'qcache_queries_in_cache'}, 1, 4 ), 316 | &format_number( &counter_per_second( 'qcache_lowmem_prunes' ), 1, 4 ), 317 | 318 | &format_percent( $status->{qcache_total_blocks} - $status->{qcache_free_blocks}, 319 | $status->{qcache_total_blocks} ), 320 | 321 | &format_memory( $status->{qcache_free_memory} ), 322 | 323 | 324 | ); 325 | } elsif( $MODE eq 'cttf' ) { 326 | printf( "row %-9s %-29s %-9s %-9s %-9s %-10s\n", 327 | 'Connects', 328 | 'Threads', 329 | 'Pool', 330 | 'Tables', 331 | 'Defs', 332 | 'Files', 333 | ) if( $count % $lines_per_header == 0 ); 334 | printf( "time % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s\n", 335 | 'cons', 'acns', 336 | 'conn', 'run', 'cach', 'crtd', 'slow', 'acls', 337 | 'tot', 'idle', 338 | 'open', 'opns', 339 | 'open', 'opns', 340 | 'open', 'opns' 341 | ) if( $count % $lines_per_header == 0 ); 342 | printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s", 343 | $pretty_time, 344 | &format_number( &counter_per_second( 'connections' ), 1, 4 ), 345 | &format_number( &counter_per_second( 'aborted_connects' ), 1, 4 ), 346 | 347 | &format_number( $status->{'threads_connected'}, 0, 4 ), 348 | &format_number( $status->{'threads_running'}, 0, 4 ), 349 | &format_number( $status->{'threads_cached'}, 0, 4 ), 350 | &format_number( &counter_per_second( 'threads_created' ), 1, 4 ), 351 | &format_number( &counter_per_second( 'slow_launch_threads' ), 1, 4 ), 352 | &format_number( &counter_per_second( 'aborted_clients' ), 1, 4 ), 353 | 354 | &format_number( $status->{'threadpool_threads'}, 0, 4 ), 355 | &format_number( $status->{'threadpool_idle_threads'}, 0, 4 ), 356 | 357 | &format_number( $status->{'open_tables'}, 0, 4 ), 358 | &format_number( &counter_per_second( 'opened_tables' ), 1, 4 ), 359 | 360 | &format_number( $status->{'open_table_definitions'}, 0, 4 ), 361 | &format_number( &counter_per_second( 'opened_table_definitions' ), 1, 4 ), 362 | 363 | &format_number( $status->{'open_files'}, 0, 4 ), 364 | &format_number( &counter_per_second( 'opened_files' ), 1, 4 ) 365 | ); 366 | } elsif( $MODE eq 'throughput' ) { 367 | printf( "row %-9s \n", 368 | 'Throughput (bytes)', 369 | ) if( $count % $lines_per_header == 0 ); 370 | printf( "time % 7s % 7s % 7s % 7s\n", 371 | 'recv', 'recv/s', 'sent', 'sent/s' 372 | ) if( $count % $lines_per_header == 0 ); 373 | printf( "%s % 7s % 7s % 7s % 7s", 374 | $pretty_time, 375 | &format_memory( &counter_diff( 'bytes_received' )), 376 | &format_memory( &counter_per_second( 'bytes_received' )), 377 | &format_memory( &counter_diff( 'bytes_sent' )), 378 | &format_memory( &counter_per_second( 'bytes_sent' )), 379 | ); 380 | } elsif( $MODE eq 'query' ) { 381 | printf( "row %-5s %-23s %-10s\n", 382 | ' ', 'Select', 'Sort' 383 | ) if( $count % $lines_per_header == 0 ); 384 | printf( "time % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s\n", 385 | 'slow', 386 | 'fjn', 'frjn', 'rang', 'rchk', 'scan', 387 | 'pass', 'rang', 'rows', 'scan', 388 | ) if( $count % $lines_per_header == 0 ); 389 | printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s", 390 | $pretty_time, 391 | &format_number( &counter_diff( 'slow_queries' )), 392 | 393 | &format_number( &counter_diff( 'select_full_join' )), 394 | &format_number( &counter_diff( 'select_full_range_join' )), 395 | &format_number( &counter_diff( 'select_range' )), 396 | &format_number( &counter_diff( 'select_range_check' )), 397 | &format_number( &counter_diff( 'select_scan' )), 398 | &format_number( &counter_diff( 'select_scan' )), 399 | 400 | &format_number( &counter_diff( 'sort_merge_passes' )), 401 | &format_number( &counter_diff( 'sort_range' )), 402 | &format_number( &counter_diff( 'sort_rows' )), 403 | &format_number( &counter_diff( 'sort_scan' )), 404 | 405 | 406 | ); 407 | } elsif( $MODE eq 'temp' ) { 408 | printf( "row %-5s %-23s %-10s\n", 409 | 'Temporary Tables' 410 | ) if( $count % $lines_per_header == 0 ); 411 | printf( "time % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s\n", 412 | 'tmps', 'disk', 'files', 413 | ) if( $count % $lines_per_header == 0 ); 414 | printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s", 415 | $pretty_time, 416 | &format_number( &counter_per_second( 'created_tmp_tables' ), 1, 4 ), 417 | &format_number( &counter_per_second( 'created_tmp_disk_tables' ), 1, 4 ), 418 | &format_number( &counter_per_second( 'created_tmp_files' ), 1, 4 ), 419 | ); 420 | } elsif( $MODE eq 'handler' ) { 421 | printf( "row %-5s %-23s %-10s\n", 422 | 'Handler' 423 | ) if( $count % $lines_per_header == 0 ); 424 | printf( "time % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s\n", 425 | 'rfst', 'rkey', 'rnex', 'rprv', 'rrd', 'rrdn', 'ins', 'upd', 'del', 'cmt', 'rbk', 'disc' 426 | ) if( $count % $lines_per_header == 0 ); 427 | printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s", 428 | $pretty_time, 429 | &format_number( &counter_per_second( 'handler_read_first' ), 1, 4 ), 430 | &format_number( &counter_per_second( 'handler_read_key' ), 1, 4 ), 431 | &format_number( &counter_per_second( 'handler_read_next' ), 1, 4 ), 432 | &format_number( &counter_per_second( 'handler_read_prev' ), 1, 4 ), 433 | &format_number( &counter_per_second( 'handler_read_rnd' ), 1, 4 ), 434 | &format_number( &counter_per_second( 'handler_read_rnd_next' ), 1, 4 ), 435 | &format_number( &counter_per_second( 'handler_write' ), 1, 4 ), 436 | &format_number( &counter_per_second( 'handler_update' ), 1, 4 ), 437 | &format_number( &counter_per_second( 'handler_delete' ), 1, 4 ), 438 | 439 | &format_number( &counter_per_second( 'handler_commit' ), 1, 4 ), 440 | &format_number( &counter_per_second( 'handler_rollback' ), 1, 4 ), 441 | 442 | &format_number( &counter_per_second( 'handler_discover' ), 1, 4 ), 443 | 444 | ); 445 | } elsif( $MODE eq 'innodb' ) { 446 | printf( "Innodb %-19s %-37s %-14s \n", 447 | 'Operations (rows)', 448 | 'Buffer Pool (pages)', 449 | 'Data (ios)', 450 | 'Log (ios)', 451 | 'Wait', 452 | ) if( $count % $lines_per_header == 0 ); 453 | printf( "time % 4s % 4s % 4s % 4s % 4s % 4s % 5s % 4s % 5s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s\n", 454 | 'read', 'ins', 'upd', 'del', 'new', 'read', '%phy', 'wrte', '%phy', 455 | '%dirt', 'wait', 456 | 'rops', 'rsize', 'wops', 'wsize', 'fsyc', 457 | 'wrte', 'fsyc', 458 | 'wait', 'time' 459 | ) if( $count % $lines_per_header == 0 ); 460 | printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 5s % 4s % 5s % 5s % 4s % 4s % 5s % 4s % 5s % 4s % 4s % 4s % 4s % 4s", 461 | $pretty_time, 462 | &format_number( &counter_per_second( 'innodb_rows_read' ), 1, 4 ), 463 | &format_number( &counter_per_second( 'innodb_rows_inserted' ), 1, 4 ), 464 | &format_number( &counter_per_second( 'innodb_rows_updated' ), 1, 4 ), 465 | &format_number( &counter_per_second( 'innodb_rows_deleted' ), 1, 4 ), 466 | &format_number( &counter_per_second( 'innodb_buffer_pool_pages_created' ), 1, 4 ), 467 | &format_number( &counter_per_second( 'innodb_buffer_pool_read_requests' ), 1, 4 ), 468 | &format_percent( &counter_diff( 'innodb_buffer_pool_reads' ), 469 | &counter_diff( 'innodb_buffer_pool_read_requests' )), 470 | &format_number( &counter_per_second( 'innodb_buffer_pool_write_requests' ), 1, 4 ), 471 | &format_percent( &counter_diff( 'innodb_buffer_pool_pages_flushed' ), 472 | &counter_diff( 'innodb_buffer_pool_write_requests' )), 473 | &format_percent( 474 | $status->{'innodb_buffer_pool_pages_dirty'}, 475 | $status->{'innodb_buffer_pool_pages_total'} 476 | ), 477 | 478 | &format_number( &counter_per_second( 'innodb_buffer_pool_wait_free' ), 1, 4 ), 479 | 480 | &format_number( &counter_per_second( 'innodb_data_reads' ), 1, 4 ), 481 | &format_memory( &counter_per_second( 'innodb_data_read' ), 2, 5 ), 482 | &format_number( &counter_per_second( 'innodb_data_writes' ), 1, 4 ), 483 | &format_memory( &counter_per_second( 'innodb_data_written' ), 2, 5 ), 484 | &format_number( &counter_per_second( 'innodb_data_fsyncs' ), 1, 4 ), 485 | 486 | &format_number( &counter_per_second( 'innodb_log_writes' ), 1, 4 ), 487 | &format_number( &counter_per_second( 'innodb_os_log_fsyncs' ), 1, 4 ), 488 | &format_number( &counter_per_second( 'innodb_row_lock_waits' ), 1, 4 ), 489 | &format_number( &counter_per_second( 'innodb_row_lock_time' ), 1, 4 ), 490 | ); 491 | } elsif( $MODE eq 'innodb_buffer_pool' ) { 492 | printf( "Innodb Buffer Pool%39s%12s%9s%7s%18s\n", 493 | 'Rd Ahead', 494 | 'Reads', 495 | 'Wait', 496 | 'Writes', 497 | 'Midpoint', 498 | ) if( $count % $lines_per_header == 0 ); 499 | printf( "time % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s\n", 500 | 'data', 'old', 'dirt', 'free', 'ltch', 'misc', 'tot', 'evct', 501 | 'rand', 'line', 'evct', 502 | 'reqs', 'phys', 503 | 'free', 504 | 'reqs', 'flsh', 'lruf', 505 | 'old', 'yng' 506 | ) if( $count % $lines_per_header == 0 ); 507 | printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s", 508 | $pretty_time, 509 | &format_number( $status->{'innodb_buffer_pool_pages_data'}, 1, 4 ), 510 | &format_number( $status->{'innodb_buffer_pool_pages_old'}, 1, 4 ), 511 | &format_number( $status->{'innodb_buffer_pool_pages_dirty'}, 1, 4 ), 512 | &format_number( $status->{'innodb_buffer_pool_pages_free'}, 1, 4 ), 513 | &format_number( $status->{'innodb_buffer_pool_pages_latched'}, 1, 4 ), 514 | &format_number( $status->{'innodb_buffer_pool_pages_misc'}, 1, 4 ), 515 | &format_number( $status->{'innodb_buffer_pool_pages_total'}, 1, 4 ), 516 | &format_number( -1 *( &counter_diff( 'innodb_buffer_pool_pages_data') - &counter_diff( 'innodb_buffer_pool_reads' )), 1, 4 ), 517 | 518 | &format_number( &counter_per_second( 'innodb_buffer_pool_read_ahead_rnd' ), 1, 4 ), 519 | &format_number( &counter_per_second( 'innodb_buffer_pool_read_ahead' ), 1, 4 ), 520 | &format_number( &counter_per_second( 'innodb_buffer_pool_read_ahead_evicted' ), 1, 4 ), 521 | 522 | &format_number( &counter_per_second( 'innodb_buffer_pool_read_requests' ), 1, 4 ), 523 | &format_number( &counter_per_second( 'innodb_buffer_pool_reads' ), 1, 4 ), 524 | &format_number( &counter_per_second( 'innodb_buffer_pool_wait_free' ), 1, 4 ), 525 | &format_number( &counter_per_second( 'innodb_buffer_pool_write_requests' ), 1, 4 ), 526 | &format_number( &counter_per_second( 'innodb_buffer_pool_pages_flushed' ), 1, 4 ), 527 | &format_number( &counter_per_second( 'innodb_buffer_pool_pages_lru_flushed' ), 1, 4 ), 528 | 529 | &format_number( &counter_per_second( 'innodb_buffer_pool_pages_made_not_young' ), 1, 4 ), 530 | &format_number( &counter_per_second( 'innodb_buffer_pool_pages_made_young' ), 1, 4 ), 531 | 532 | ); 533 | 534 | } elsif( $MODE eq 'innodb_io' ) { 535 | printf( "Inno I/O %-24s %-14s %-29s %-10s\n", 536 | 'Reads', 537 | 'data', 538 | 'Writes', 539 | 'data' 540 | ) if( $count % $lines_per_header == 0 ); 541 | printf( "time % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s\n", 542 | 'reqs', 'rnd', 'seq', 'rds', 'read', 'pend', 'rds', 'read', 543 | 'reqs', 'pend', 'flsh', 'dbwr', 'dbpw', 'wrtn', 'pend', 'wrts', 'wrtn' 544 | ) if( $count % $lines_per_header == 0 ); 545 | printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s", 546 | $pretty_time, 547 | &format_number( &counter_per_second( 'innodb_buffer_pool_read_requests' ), 1, 4 ), 548 | &format_number( &counter_per_second( 'innodb_buffer_pool_read_ahead_rnd' ), 1, 4 ), 549 | &format_number( &counter_per_second( 'innodb_buffer_pool_read_ahead_seq' ), 1, 4 ), 550 | &format_number( &counter_per_second( 'innodb_buffer_pool_reads' ), 1, 4 ), 551 | &format_number( &counter_per_second( 'innodb_pages_read' ), 1, 4 ), 552 | 553 | &format_number( &counter_per_second( 'innodb_data_pending_reads' ), 1, 4 ), 554 | &format_number( &counter_per_second( 'innodb_data_reads' ), 1, 4 ), 555 | &format_memory( &counter_per_second( 'innodb_data_read' ), 1, 4 ), 556 | 557 | 558 | &format_number( &counter_per_second( 'innodb_buffer_pool_write_requests' ), 1, 4 ), 559 | &format_number( &counter_per_second( 'innodb_data_pending_writes' ), 1, 4 ), 560 | &format_number( &counter_per_second( 'innodb_buffer_pool_pages_flushed' ), 1, 4 ), 561 | &format_number( &counter_per_second( 'innodb_dblwr_writes' ), 1, 4 ), 562 | &format_number( &counter_per_second( 'innodb_dblwr_pages_written' ), 1, 4 ), 563 | &format_number( &counter_per_second( 'innodb_pages_written' ), 1, 4 ), 564 | 565 | &format_number( &counter_per_second( 'innodb_data_pending_writes' ), 1, 4 ), 566 | &format_number( &counter_per_second( 'innodb_data_writes' ), 1, 4 ), 567 | &format_memory( &counter_per_second( 'innodb_data_written' ), 1, 4 ), 568 | ); 569 | } elsif( $MODE eq 'innodb_log' ) { 570 | printf( "Inno Log %-19s %-14s\n", 571 | 'Writes', 572 | 'Fsyncs', 573 | ) if( $count % $lines_per_header == 0 ); 574 | printf( "time % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s \n", 575 | 'wait', 'reqs', 'wrts', 'pend', 'wtn', 'pend', 'fsyn', 576 | ) if( $count % $lines_per_header == 0 ); 577 | printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s", 578 | $pretty_time, 579 | &format_number( &counter_per_second( 'innodb_log_waits' ), 1, 4 ), 580 | &format_number( &counter_per_second( 'innodb_log_write_requests' ), 1, 4 ), 581 | &format_number( &counter_per_second( 'innodb_log_writes' ), 1, 4 ), 582 | &format_number( &counter_per_second( 'innodb_os_log_pending_writes' ), 1, 4 ), 583 | &format_memory( &counter_per_second( 'innodb_os_log_written' ), 1, 4 ), 584 | &format_number( &counter_per_second( 'innodb_os_log_pending_fsyncs' ), 1, 4 ), 585 | &format_number( &counter_per_second( 'innodb_os_log_fsyncs' ), 1, 4 ), 586 | ); 587 | } elsif( $MODE eq 'innodb_row' ) { 588 | printf( "Inno Locks %-19s\n", 589 | "Time (ms)", 590 | ) if( $count % $lines_per_header == 0 ); 591 | printf( "time % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s \n", 592 | 'cur', 'wts', 'avg', 'tot', 'cur', 'avg', 'max', 'tot' 593 | ) if( $count % $lines_per_header == 0 ); 594 | printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s", 595 | $pretty_time, 596 | &format_number( $status->{'innodb_current_row_locks'}, 1, 4 ), 597 | &format_number( $status->{'innodb_row_lock_current_waits'}, 1, 4 ), 598 | &format_number( &counter_per_second( 'innodb_row_lock_waits' ), 1, 4 ), 599 | &format_number( $status->{'innodb_row_lock_waits'}, 1, 4 ), 600 | 601 | &format_number( &counter_per_second( 'innodb_row_lock_time' ), 1, 4 ), 602 | &format_number( $status->{'innodb_row_lock_time_avg'}, 1, 4 ), 603 | &format_number( $status->{'innodb_row_lock_time_max'}, 1, 4 ), 604 | &format_number( $status->{'innodb_row_lock_time'}, 1, 4 ), 605 | ); 606 | } elsif( $MODE eq 'innodb_flush' ) { 607 | printf( "Innodb Flushing%29s%13s%9s\n", 608 | "Checkpoint", 609 | "Pages", 610 | "LSN" 611 | ) if( $count % $lines_per_header == 0 ); 612 | printf( "%8s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s\n", 613 | 'time', 614 | 'meth', 615 | '%dirt', 'expf', 'lruf', 616 | 'age', 'trgt', 'max', 617 | 'wops', 'wsize', 618 | 'curr', 'chkpt' 619 | ) if( $count % $lines_per_header == 0 ); 620 | 621 | printf( "%8s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s", 622 | $pretty_time, 623 | $variables->{innodb_adaptive_flushing_method}, 624 | &format_percent( 625 | $status->{'innodb_buffer_pool_pages_dirty'}, 626 | $status->{'innodb_buffer_pool_pages_total'} 627 | ), 628 | # expf = Explicit Flushes --- those note caused by LRU 629 | &format_number( &counter_per_second( 'innodb_buffer_pool_pages_flushed' ) - &counter_per_second('innodb_buffer_pool_pages_lru_flushed') , 1, 5 ), 630 | # lrfu = LRU Flushes - those caused by a dirty page falling off the LRU 631 | &format_number( &counter_per_second( 'innodb_buffer_pool_pages_lru_flushed' ), 1, 4 ), 632 | &format_memory( $status->{'innodb_checkpoint_age'}, 1, 5 ), 633 | &format_percent( 634 | $status->{'innodb_checkpoint_age'}, 635 | $status->{'innodb_checkpoint_target_age'} 636 | ), 637 | &format_percent( 638 | $status->{'innodb_checkpoint_age'}, 639 | $status->{'innodb_checkpoint_max_age'} 640 | ), 641 | &format_number( &counter_per_second( 'innodb_pages_written' ), 1, 5 ), 642 | &format_memory( &counter_per_second( 'innodb_pages_written' ) * $status->{'innodb_page_size'}, 2, 5 ), 643 | &format_memory( &counter_diff( 'innodb_lsn_current' ), 2, 5 ), 644 | &format_memory( &counter_diff( 'innodb_lsn_last_checkpoint' ), 2, 5 ), 645 | ); 646 | } elsif( $MODE eq 'innodb_hash' ) { 647 | printf( "Innodb Adaptive Hash%22s\n", 648 | "Searches", 649 | ) if( $count % $lines_per_header == 0 ); 650 | printf( "%8s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s\n", 651 | 'time', 652 | 'parts', 653 | 'mem', 'cells', 'buffs', 654 | 'hits', 'miss', 655 | ) if( $count % $lines_per_header == 0 ); 656 | 657 | printf( "%8s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s", 658 | $pretty_time, 659 | $variables->{innodb_adaptive_hash_index_partitions}, 660 | &format_memory( $status->{'innodb_mem_adaptive_hash'}, 1, 5 ), 661 | &format_number( $status->{'innodb_adaptive_hash_cells'}, 1, 5 ), 662 | &format_number( $status->{'innodb_adaptive_hash_heap_buffers'}, 1, 5 ), 663 | 664 | &format_number( &counter_per_second( 'innodb_adaptive_hash_hash_searches' ), 1, 5 ), 665 | &format_number( &counter_per_second( 'innodb_adaptive_hash_non_hash_searches' ), 1, 5 ), 666 | ); 667 | } elsif( $MODE eq 'innodb_waits' ) { 668 | printf( "Innodb %9s%18s%18s\n", 669 | "S-locks", "X-locks", "Mutexes" 670 | ) if( $count % $lines_per_header == 0 ); 671 | printf( "%8s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s\n", 672 | 'time', 673 | 'swait', 'srnd', 'owait', 674 | 'swait', 'srnd', 'owait', 675 | 'swait', 'srnd', 'owait' 676 | ) if( $count % $lines_per_header == 0 ); 677 | 678 | printf( "%8s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s", 679 | $pretty_time, 680 | &format_number( &counter_per_second( 'innodb_s_lock_spin_waits' ), 1, 5 ), 681 | &format_number( &counter_per_second( 'innodb_s_lock_spin_rounds' ), 1, 5 ), 682 | &format_number( &counter_per_second( 'innodb_s_lock_os_waits' ), 1, 5 ), 683 | 684 | &format_number( &counter_per_second( 'innodb_x_lock_spin_waits' ), 1, 5 ), 685 | &format_number( &counter_per_second( 'innodb_x_lock_spin_rounds' ), 1, 5 ), 686 | &format_number( &counter_per_second( 'innodb_x_lock_os_waits' ), 1, 5 ), 687 | 688 | &format_number( &counter_per_second( 'innodb_mutex_spin_waits' ), 1, 5 ), 689 | &format_number( &counter_per_second( 'innodb_mutex_spin_rounds' ), 1, 5 ), 690 | &format_number( &counter_per_second( 'innodb_mutex_os_waits' ), 1, 5 ), 691 | ); 692 | } elsif( $MODE eq 'innodb_hist' ) { 693 | printf( "Innodb Purging %16s\n", "Txns Diff" 694 | ) if( $count % $lines_per_header == 0 ); 695 | printf( "%8s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s\n", 696 | 'time', 697 | 'leng', 'diff', 'new', 'purg', 'max', 'view', 'unno' 698 | ) if( $count % $lines_per_header == 0 ); 699 | 700 | printf( "%8s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s", 701 | $pretty_time, 702 | &format_number( $status->{'innodb_history_list_length'}, 1, 5 ), 703 | &format_number( &counter_diff( 'innodb_history_list_length' ), 1, 5 ), 704 | &format_number( &counter_diff( 'innodb_purge_trx_id' ), 1, 5 ), 705 | &format_number( &counter_diff( 'innodb_max_trx_id' ), 1, 5 ), 706 | &format_number( $status->{'innodb_max_trx_id'} - $status->{'innodb_purge_trx_id'}, 1, 5 ), 707 | &format_number( $status->{'innodb_oldest_view_low_limit_trx_id'} - $status->{'innodb_purge_trx_id'}, 1, 5 ), 708 | &format_number( $status->{'innodb_purge_undo_no'}, 1, 5 ), 709 | ); 710 | 711 | } elsif( $MODE eq 'wsrep' ) { 712 | printf( "%s / %s (idx: %d) / %s %s\n", $variables->{'wsrep_cluster_name'}, $variables->{'wsrep_node_name'}, $status->{'wsrep_local_index'}, $status->{'wsrep_provider_name'}, $status->{'wsrep_provider_version'} 713 | ) if( $count % $lines_per_header == 0 ); 714 | printf( "%-8s %-8s %-4s %-5s %-9s %-9s %-9s %-9s %-9s %-13s %-8s\n", "Wsrep", "Cluster", "Node", "Repl", "Queue", "Ops", "Bytes", "Conflct", 'Gcache', 'Window', 'Flow' 715 | ) if( $count % $lines_per_header == 0 ); 716 | printf( "%8s %1s %3s %2s %-4.4s %5s %4s %4s %4s %4s %4s %4s %4s %4s %4s %4s %3s %4s %4s %5s\n", 717 | 'time', 'P', 'cnf', '#', 'State', 'Laten', 'Up', 'Dn', 'Up', 'Dn', 'Up' , 'Dn', 'lcf', 'bfa', 'ist', 'idx', 'dst', 'appl', 'comm', 'p_ms' 718 | ) if( $count % $lines_per_header == 0 ); 719 | 720 | $status->{'wsrep_local_state_comment'} =~ s/\s+\(.+\)$//; 721 | 722 | my $avg = undef; 723 | if( defined( $status->{'wsrep_evs_repl_latency'})) { 724 | my @latencies = split( '/', $status->{'wsrep_evs_repl_latency'}); 725 | if( $latencies[1] =~ m|(\d+\.\d+)e-(\d+)| ) { 726 | $avg = ($1 * (10** ($2 * - 1))) * 1000000; 727 | } elsif( $latencies[1] =~ m|^(\d+\.\d+)| ) { 728 | $avg = $1 * 1000000; #(put to micro secs) 729 | } 730 | } 731 | my $ist_size = $status->{'wsrep_last_committed'} - $status->{'wsrep_local_cached_downto'}; 732 | $ist_size = 0 if $ist_size < 0; 733 | 734 | printf( "%8s %1s %3s %2s %4.4s %5s %4s %4s %4s %4s %4s %4s %4s %4s %4s %4s %3s %4s %4s %5s%s", 735 | $pretty_time, 736 | ($status->{'wsrep_cluster_status'} eq 'Primary' ? 'P' : 'N'), 737 | substr( $status->{'wsrep_cluster_conf_id'}, -3, 3 ), 738 | &format_number( $status->{'wsrep_cluster_size'}, 0, 2 ), 739 | 740 | $status->{'wsrep_local_state_comment'}, 741 | 742 | defined $avg ? &format_microseconds( $avg, 0, 5 ) : 'N/A', 743 | 744 | &format_number( $status->{'wsrep_local_send_queue'}, 0, 4 ), 745 | &format_number( $status->{'wsrep_local_recv_queue'}, 0, 4 ), 746 | 747 | &format_number( &counter_diff( 'wsrep_replicated' ), 0, 4 ), 748 | &format_number( &counter_diff( 'wsrep_received' ), 0, 4 ), 749 | 750 | &format_memory( &counter_diff( 'wsrep_replicated_bytes' ), 1, 4 ), 751 | &format_memory( &counter_diff( 'wsrep_received_bytes' ), 1, 4 ), 752 | 753 | &format_number( &counter_diff( 'wsrep_local_cert_failures' ), 0, 3 ), 754 | &format_number( &counter_diff( 'wsrep_local_bf_aborts' ), 0, 3 ), 755 | 756 | &format_number( ( $ist_size ), , 0, 4 ), 757 | &format_number( $status->{'wsrep_cert_index_size'}, 0, 4 ), 758 | &format_number( $status->{'wsrep_cert_deps_distance'}, 0, 3 ), 759 | &format_number( $status->{'wsrep_apply_window'}, 0, 4 ), 760 | 761 | &format_number( $status->{'wsrep_commit_window'}, 0, 4 ), 762 | 763 | $variables->{version} =~ m/^5.5/ ? 764 | &format_number( $status->{'wsrep_flow_control_paused'}, 1, 4 ) : 765 | &format_number( &counter_diff( 'wsrep_flow_control_paused_ns' ) / 1000000, 0, 4 ), 766 | 767 | $variables->{version} =~ m/^5.5/ ? 768 | ( &format_number( $status->{'wsrep_flow_control_sent'}, 0, 3 ) > 0 ? '*' : '' ) : 769 | ( &format_number( &counter_diff( 'wsrep_flow_control_sent' ), 0, 3 ) > 0 ? '*' : '' ), 770 | ); 771 | 772 | # if ($status->{'wsrep_cluster_status'} eq 'Primary' and 773 | # $status->{'wsrep_local_state_comment'} !~ m/^Join/ ) { 774 | # # This helps keep things like wsrep_apply_window and others fresh for Galera polling 775 | # &mysql_call( "FLUSH STATUS" ); 776 | # } 777 | 778 | } elsif( $MODE eq 'wsrep_latency' ) { 779 | printf( "%s / %s (idx: %d) / %s %s\n", $variables->{'wsrep_cluster_name'}, $variables->{'wsrep_node_name'}, $status->{'wsrep_local_index'}, $status->{'wsrep_provider_name'}, $status->{'wsrep_provider_version'} 780 | ) if( $count % $lines_per_header == 0 ); 781 | printf( "%-8s %-8s %-4s %-11s %-9s\n", "Wsrep", "Cluster", "Node", "Ops", "Latencies", 782 | ) if( $count % $lines_per_header == 0 ); 783 | printf( "%8s %1s %3s %2s %-4.4s %5s %5s %4s %6s %6s %6s %6s\n", 784 | 'time', 'P', 'cnf', '#', 'State', 'Up', 'Dn', 'Size', 'Min', 'Avg', 'Max', 'Dev' 785 | ) if( $count % $lines_per_header == 0 ); 786 | 787 | $status->{'wsrep_local_state_comment'} =~ s/\s+\(.+\)$//; 788 | 789 | my ($min,$avg,$max,$dev,$size) = (0,0,0,0,0); 790 | if( $status->{'wsrep_evs_repl_latency'} =~ m|(\d+\.\d+)\/(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+)/(\d+)| ) { 791 | $min = $1 * 1000000; #(put to micro secs) 792 | $avg = $2 * 1000000; #(put to micro secs) 793 | $max = $3 * 1000000; #(put to micro secs) 794 | $dev = $4 * 1000000; #(put to micro secs) 795 | $size = $5; 796 | } 797 | 798 | printf( "%8s %1s %3s %2s %4.4s %5s %5s %4s %6s %6s %6s %6s", 799 | $pretty_time, 800 | ($status->{'wsrep_cluster_status'} eq 'Primary' ? 'P' : 'N'), 801 | substr( $status->{'wsrep_cluster_conf_id'}, -3, 3 ), 802 | &format_number( $status->{'wsrep_cluster_size'}, 0, 2 ), 803 | 804 | $status->{'wsrep_local_state_comment'}, 805 | 806 | &format_number( &counter_diff( 'wsrep_replicated' ), 0, 5 ), 807 | &format_number( &counter_diff( 'wsrep_received' ), 0, 5 ), 808 | 809 | &format_number( $size, 0, 4), 810 | 811 | &format_microseconds( $min, 0, 6 ), 812 | &format_microseconds( $avg, 0, 6 ), 813 | &format_microseconds( $max, 0, 6 ), 814 | &format_microseconds( $dev, 0, 6 ), 815 | ); 816 | } 817 | 818 | 819 | print "\n"; 820 | } 821 | 822 | # Main loop 823 | my $count = 0; 824 | 825 | $prev_status = undef; 826 | 827 | unless( defined( $FILENAME )) { 828 | while( 1 ) { 829 | my $pretty_time = `date '+\%H:\%M:\%S'`; 830 | chop $pretty_time; 831 | 832 | my $output = &mysql_call( "SHOW /*!50002 GLOBAL */ STATUS" ); 833 | 834 | $status = &parse_status( $output ); 835 | $variables = &parse_status( &mysql_call( "SHOW VARIABLES" )); 836 | 837 | &process_global_status( $count, $pretty_time ); 838 | 839 | $prev_status = $status; 840 | 841 | sleep $REPEAT_TIME; 842 | 843 | $count++; 844 | } 845 | } else { 846 | # Process the input file instead of doing live collection 847 | 848 | open( FILE, "< $FILENAME" ); 849 | 850 | my $first_uptime = undef; 851 | 852 | eval { 853 | $variables = &parse_status( &mysql_call( "SHOW VARIABLES" )); 854 | }; 855 | if( $@ ) { 856 | print "No variables could be loaded from local running mysql\n"; 857 | } 858 | 859 | my @output = (); 860 | my $skip = 0; 861 | while( my $line = ) { 862 | unless( $line =~ m/^\n/ ) { 863 | push( @output, $line ); 864 | } else { 865 | # Blank line 866 | if( $skip == 0 ) { 867 | $status = &parse_mysqladmin_status( \@output ); 868 | 869 | $first_uptime = $status->{'uptime'} unless defined $first_uptime; 870 | 871 | my $pretty_time = sprintf( "%8d", $status->{'uptime'} - $first_uptime ); 872 | 873 | &process_global_status( $count, $pretty_time ); 874 | 875 | $prev_status = $status; 876 | 877 | $skip = $REPEAT_TIME; # set skip to the number of seconds to repeat 878 | } else { 879 | $skip--; 880 | } 881 | 882 | @output = (); 883 | 884 | $count++; 885 | } 886 | } 887 | 888 | close( FILE ); 889 | } 890 | --------------------------------------------------------------------------------