#!/usr/bin/perl # Copyright (C) 2001-2003, 2006 MySQL AB # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA use strict; use warnings; use DBI; use Getopt::Long; $Getopt::Long::ignorecase=0; my $Param={}; $Param->{host}=''; $Param->{user}=''; $Param->{password}=''; $Param->{PrintError}=0; $Param->{socket}=''; my $help; if (!GetOptions ( 'date|d:i' => \$Param->{ViewDate}, 'host|h:s' => \$Param->{host}, 'user|u:s' => \$Param->{user}, 'password|p:s' => \$Param->{password}, 'printerror|e:s' => \$Param->{PrintError}, 'socket|s:s' => \$Param->{socket}, 'help|h' => \$help, )) { ShowOptions(); exit(0); } if (defined ($help)) { ShowOptions(); exit(0); } print "explain_log provided by http://www.mobile.de\n"; print "=========== ================================\n"; $Param->{UpdateCount} = 0; $Param->{SelectCount} = 0; $Param->{IdxUseCount} = 0; $Param->{LineCount} = 0; $Param->{Init} = 0; $Param->{Field} = 0; $Param->{Refresh} = 0; $Param->{QueryCount} = 0; $Param->{Statistics} =0; $Param->{Query} = undef; $Param->{ALL} = undef ; $Param->{Comment} = undef ; @{$Param->{Rows}} = (qw|possible_keys key type|); if ($Param->{ViewDate}) { $Param->{View} = 0; } else { $Param->{View} = 1; } #print "Date=$Param->{ViewDate}, host=$Param->{host}, user=$Param->{user}, password=$Param->{password}\n"; $Param->{dbh}=DBI->connect("DBI:mysql:host=$Param->{host}".($Param->{socket}?";mysql_socket=$Param->{socket}":""),$Param->{user},$Param->{password},{PrintError=>0}); if (DBI::err()) { print "Error: " . DBI::errstr() . "\n"; } else { $Param->{Start} = time; while(<>) { $Param->{LineCount} ++ ; if ($Param->{ViewDate} ) { if (m/^(\d{6})\s+\d{1,2}:\d\d:\d\d\s.*$/) { # get date #print "# $1 #\n"; if ($1 == $Param->{ViewDate}) { $Param->{View} = 1; } else { $Param->{View} = 0; } } } if ($Param->{View} ) { #print "->>>$_"; if (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Connect.+\s+on\s+(.*)$/i) { # get connection ID($2) and database($3) #print "C-$1--$2--$3------\n"; RunQuery($Param); if (defined $3) { $Param->{CID}->{$2} = $3 ; #print "DB:$Param->{CID}->{$2} .. $2 .. $3 \n"; } } elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Connect.+$/i) { # get connection ID($2) and database($3) #print "\n <<<<<<<<<<<<<<<<<<----------------------------<<<<<<<<<<<<<<<< \n"; #print "Connect \n"; RunQuery($Param); } elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Change user .*\s+on\s+(.*)$/i) { # get connection ID($2) and database($3) #print "C-$1--$2--$3------\n"; RunQuery($Param); if (defined $3) { $Param->{CID}->{$2} = $3 ; #print "DB:$Param->{CID}->{$2} .. $2 .. $3 \n"; } } elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Quit\s+$/i) { # remove connection ID($2) and querystring #print "Q-$1--$2--------\n"; RunQuery($Param); delete $Param->{CID}->{$2} ; } elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Query\s+(select.+)$/i) { # get connection ID($2) and querystring #print "S1-$1--$2--$3------\n"; RunQuery($Param); unless ($Param->{CID}->{$2}) { #print "Error: No Database for Handle: $2 found\n"; } else { $Param->{DB}=$Param->{CID}->{$2}; my $s = "$3"; $s =~ s/from\s/from $Param->{DB}./i; $Param->{Query}="EXPLAIN $s"; #$s =~ m/from\s+(\w+[.]\w+)/i; #$Param->{tab} =$1; #print "-- $Param->{tab} -- $s --\n"; } } elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Query\s+(update.+)$/i) { # get connection ID($2) and querystring #print "S2--$1--$2--$3------\n"; RunQuery($Param); unless ($Param->{CID}->{$2}) { #print "Error: No Database for Handle: $2 found\n"; } else { $Param->{DB}=$Param->{CID}->{$2}; my $ud = $3; $ud =~ m/^update\s+(\w+).+(where.+)$/i; $Param->{Query} ="EXPLAIN SELECT * FROM $1 $2"; $Param->{Query} =~ s/from\s/from $Param->{DB}./i; #$Param->{Query} =~ m/from\s+(\w+[.]\w+)/i; #$Param->{tab} =$1; } } elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Statistics\s+(.*)$/i) { # get connection ID($2) and info? $Param->{Statistics} ++; #print "Statistics--$1--$2--$3------\n"; RunQuery($Param); } elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Query\s+(.+)$/i) { # get connection ID($2) $Param->{QueryCount} ++; #print "Query-NULL $3\n"; RunQuery($Param); } elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Refresh\s+(.+)$/i) { # get connection ID($2) $Param->{Refresh} ++; #print "Refresh\n"; RunQuery($Param); } elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Init\s+(.+)$/i) { # get connection ID($2) $Param->{Init} ++; #print "Init $3\n"; RunQuery($Param); } elsif (m/^(\d{6}\s+\d{1,2}:\d\d:\d\d\s+|\s+)(\d+)\s+Field\s+(.+)$/i) { # get connection ID($2) $Param->{Field} ++; #print "Field $3\n"; RunQuery($Param); } elsif (m/^\s+(.+)$/ ) { # command could be some lines ... #print "multi-lined ($1)\n"; my ($A)=$1; $A =~ s/\-\-.*//; $Param->{Query} .= " $A"; #print "multi-lined ($A)<<$Param->{Query}>>\n"; } } } $Param->{dbh}->disconnect(); if (1 == 0) { print "\nunclosed handles----------------------------------------\n"; my $count=0; foreach (sort keys %{$Param->{CID}}) { print "$count | $_ : $Param->{CID}->{$_} \n"; $count ++; } } print "\nIndex usage ------------------------------------\n"; foreach my $t (sort keys %{$Param->{Data}}) { print "\nTable\t$t: ---\n"; foreach my $k (sort keys %{$Param->{Data}->{$t}}) { print " count\t$k:\n"; my %h = %{$Param->{Data}->{$t}->{$k}}; foreach (sort {$h{$a} <=> $h{$b}} keys %h) { print " $Param->{Data}->{$t}->{$k}->{$_}\t$_\n"; } } } $Param->{AllCount}=0; print "\nQueries causing table scans -------------------\n\n"; foreach (@{$Param->{ALL}}) { $Param->{AllCount} ++; print "$_\n"; } print "Sum: $Param->{AllCount} table scans\n"; print "\nSummary ---------------------------------------\n\n"; print "Select: \t$Param->{SelectCount} queries\n"; print "Update: \t$Param->{UpdateCount} queries\n"; print "\n"; print "Init: \t$Param->{Init} times\n"; print "Field: \t$Param->{Field} times\n"; print "Refresh: \t$Param->{Refresh} times\n"; print "Query: \t$Param->{QueryCount} times\n"; print "Statistics:\t$Param->{Statistics} times\n"; print "\n"; print "Logfile: \t$Param->{LineCount} lines\n"; print "Started: \t".localtime($Param->{Start})."\n"; print "Finished: \t".localtime(time)."\n"; } ########################################################################### # # # sub RunQuery { my $Param = shift ; if (defined $Param->{Query}) { if (defined $Param->{DB} ) { $Param->{Query} =~ m/from\s+(\w+[.]\w+|\w+)/i; $Param->{tab} =$1; #print "||$Param->{tab} -- $Param->{Query}\n"; my $sth=$Param->{dbh}->prepare("USE $Param->{DB}"); if (DBI::err()) { if ($Param->{PrintError}) {print "Error: ".DBI::errstr()."\n";} } else { $sth->execute(); if (DBI::err()) { if ($Param->{PrintError}) {print "Error: ".DBI::errstr()."\n";} } else { $sth->finish(); $sth=$Param->{dbh}->prepare($Param->{Query}); if (DBI::err()) { if ($Param->{PrintError}) {print "Error: ".DBI::errstr()."\n";} } else { #print "$Param->{Query}\n"; $sth->execute(); if (DBI::err()) { if ($Param->{PrintError}) {print "[$Param->{LineCount}]<<$Param->{Query}>>\n";} if ($Param->{PrintError}) {print "Error: ".DBI::errstr()."\n";} } else { my $row = undef; while ($row = $sth->fetchrow_hashref()) { $Param->{SelectCount} ++; if (defined $row->{Comment}) { push (@{$Param->{Comment}}, "$row->{Comment}; $_; $Param->{DB}; $Param->{Query}"); } foreach (@{$Param->{Rows}}) { if (defined $row->{$_}) { #if (($_ eq 'type' ) and ($row->{$_} eq 'ALL')) { if ($row->{type} eq 'ALL') { push (@{$Param->{ALL}}, "$Param->{Query}"); #print ">> $row->{$_} $_ $Param->{DB} $Param->{Query}\n"; } $Param->{IdxUseCount} ++; $Param->{Data}->{$Param->{tab}}->{$_}->{$row->{$_}} ++; } } } } } } } $sth->finish(); } $Param->{Query} = undef ; } } ########################################################################### # # # sub ShowOptions { print <, http://www.willamowius.de Dennis Haney (Added socket support) =head1 SEE ALSO mysql documentation =cut