Commit e8012350 authored by monty@hundin.mysql.fi's avatar monty@hundin.mysql.fi

New script: explain_log.sh

Portability fixes.
Handle cleanup when we get an error before the message file is read.
Fix bug in multi-table delete
parent f0ae1dbb
......@@ -384,8 +384,8 @@ int STDCALL mysql_drop_db(MYSQL *mysql, const char *DB);
*/
int simple_command(MYSQL *mysql,enum enum_server_command command,
const char *arg, ulong length, my_bool skipp_check);
ulong net_safe_read(MYSQL* mysql);
const char *arg, unsigned long length, my_bool skipp_check);
unsigned long net_safe_read(MYSQL* mysql);
#ifdef __cplusplus
}
......
......@@ -236,13 +236,13 @@ done
MYRUN_DIR=$MYSQL_TEST_DIR/var/run
MASTER_MYDDIR="$MYSQL_TEST_DIR/var/master-data"
MASTER_MYSOCK="$MYSQL_TMP_DIR/mysql-master.sock"
MASTER_MYSOCK="$MYSQL_TMP_DIR/master.sock"
MASTER_MYPID="$MYRUN_DIR/mysqld.pid"
MASTER_MYLOG="$MYSQL_TEST_DIR/var/log/mysqld.log"
MASTER_MYERR="$MYSQL_TEST_DIR/var/log/mysqld.err"
SLAVE_MYDDIR="$MYSQL_TEST_DIR/var/slave-data"
SLAVE_MYSOCK="$MYSQL_TMP_DIR/mysql-slave.sock"
SLAVE_MYSOCK="$MYSQL_TMP_DIR/slave.sock"
SLAVE_MYPID="$MYRUN_DIR/mysqld-slave.pid"
SLAVE_MYLOG="$MYSQL_TEST_DIR/var/log/mysqld-slave.log"
SLAVE_MYERR="$MYSQL_TEST_DIR/var/log/mysqld-slave.err"
......
#!@PERL@ -w
use strict;
use DBI;
use Getopt::Long;
$Getopt::Long::ignorecase=0;
print "explain_log provided by http://www.mobile.de\n";
print "=========== ================================\n";
my $Param={};
$Param->{host}='';
$Param->{user}='';
$Param->{password}='';
$Param->{PrintError}=0;
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},
)) {
ShowOptions();
}
else {
$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->{user},$Param->{password},{PrintError=>0});
if (DBI::err()) {
print "Error: " . DBI::errstr() . "\n";
}
else {
$Param->{Start} = time;
while(<STDIN>) {
$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;
chomp $A;
$Param->{Query} .= " $1";
#print "multi-lined ($1)<<$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}}, "$row->{$_} $_ $Param->{DB} $Param->{Query}");
#print ">> $row->{$_} $_ $Param->{DB} $Param->{Query}\n";
}
$Param->{IdxUseCount} ++;
$Param->{Data}->{$Param->{tab}}->{$_}->{$row->{$_}} ++;
}
}
}
}
}
}
}
$sth->finish();
}
$Param->{Query} = undef ;
}
}
###########################################################################
#
#
#
sub ShowOptions {
print <<EOF;
Usage: $0 [OPTIONS] < LOGFILE
--date=YYMMDD select only entrys of date
-d=YYMMDD
--host=HOSTNAME db-host to ask
-h=HOSTNAME
--user=USERNAME db-user
-u=USERNAME
--password=PASSWORD password of db-user
-p=PASSWORD
Read logfile from STDIN an try to EXPLAIN all SELECT statements. All UPDATE statements are rewritten to an EXPLAIN SELECT statement. The results of the EXPLAIN statement are collected and counted. All results with type=ALL are collected in an separete list. Results are printed to STDOUT.
EOF
}
1;
__END__
=pod
=head1 NAME
explain_log.pl
Feed a mysqld general logfile (created with mysqld --log) back into mysql
and collect statistics about index usage with EXPLAIN.
=head1 DISCUSSION
To optimize your indices, you have to know which ones are actually
used and what kind of queries are causing table scans. Especially
if you are generating your queries dynamically and you have a huge
amount of queries going on, this isn't easy.
Use this tool to take a look at the effects of your real life queries.
Then add indices to avoid table scans and remove those which aren't used.
=head1 USAGE
explain_log.pl [--date=YYMMDD] --host=dbhost] [--user=dbuser] [--password=dbpw] < logfile
--date=YYMMDD select only entrys of date
-d=YYMMDD
--host=HOSTNAME db-host to ask
-h=HOSTNAME
--user=USERNAME db-user
-u=USERNAME
--password=PASSWORD password of db-user
-p=PASSWORD
=head1 EXAMPLE
explain_log.pl --host=localhost --user=foo --password=bar < /var/lib/mysql/mobile.log
=head1 AUTHOR
Stefan Nitz
Jan Willamowius <jan@mobile.de>, http://www.mobile.de
=head1 RECRUITING
If you are looking for a MySQL or Perl job, take a look at http://www.mobile.de
and send me an email with your resume (you must be speaking German!).
=head1 SEE ALSO
mysql documentation
=cut
......@@ -51,6 +51,7 @@ static void read_texts(const char *file_name,const char ***point,
uchar head[32],*pos;
DBUG_ENTER("read_texts");
*point=0; // If something goes wrong
LINT_INIT(buff);
funktpos=0;
if ((file=my_open(fn_format(name,file_name,language,"",4),
......
......@@ -741,7 +741,7 @@ void clean_up(bool print_message)
if (!opt_bootstrap)
(void) my_delete(pidfile_name,MYF(0)); // This may not always exist
#endif
if (print_message)
if (print_message && errmesg)
sql_print_error(ER(ER_SHUTDOWN_COMPLETE),my_progname);
x_free((gptr) my_errmsg[ERRMAPP]); /* Free messages */
......@@ -3688,7 +3688,7 @@ static void get_options(int argc,char **argv)
opt_specialflag|= SPECIAL_NO_HOST_CACHE;
break;
case (int) OPT_ENABLE_LOCK:
my_disable_locking=0;
my_disable_locking=myisam_single_user=0;
break;
case (int) OPT_USE_LOCKING:
my_disable_locking=0;
......
......@@ -274,6 +274,8 @@ multi_delete::initialize_tables(JOIN *join)
/* We are going to delete from this table */
walk->table=tab->table;
walk=walk->next;
if (tab == join->join_tab)
tab->table->no_keyread=1;
}
}
}
......@@ -353,6 +355,10 @@ void multi_delete::send_error(uint errcode,const char *err)
{
/* First send error what ever it is ... */
::send_error(&thd->net,errcode,err);
/* reset used flags */
delete_tables->table->no_keyread=0;
/* If nothing deleted return */
if (!deleted)
return;
......@@ -451,6 +457,9 @@ bool multi_delete::send_eof()
thd->proc_info="deleting from reference tables";
int error = do_deletes(false);
/* reset used flags */
delete_tables->table->no_keyread=0;
thd->proc_info="end";
if (error && error != -1)
{
......
......@@ -2489,7 +2489,8 @@ make_join_readinfo(JOIN *join,uint options)
table->file->index_init(tab->ref.key);
tab->read_first_record= join_read_key;
tab->read_record.read_record= join_no_more_records;
if (table->used_keys & ((key_map) 1 << tab->ref.key))
if (table->used_keys & ((key_map) 1 << tab->ref.key) &&
!table->no_keyread)
{
table->key_read=1;
table->file->extra(HA_EXTRA_KEYREAD);
......@@ -2507,7 +2508,8 @@ make_join_readinfo(JOIN *join,uint options)
table->file->index_init(tab->ref.key);
tab->read_first_record= join_read_always_key;
tab->read_record.read_record= join_read_next;
if (table->used_keys & ((key_map) 1 << tab->ref.key))
if (table->used_keys & ((key_map) 1 << tab->ref.key) &&
!table->no_keyread)
{
table->key_read=1;
table->file->extra(HA_EXTRA_KEYREAD);
......@@ -2568,18 +2570,21 @@ make_join_readinfo(JOIN *join,uint options)
statistic_increment(select_full_join_count, &LOCK_status);
}
}
if (tab->select && tab->select->quick &&
table->used_keys & ((key_map) 1 << tab->select->quick->index))
if (!table->no_keyread)
{
table->key_read=1;
table->file->extra(HA_EXTRA_KEYREAD);
}
else if (table->used_keys && ! (tab->select && tab->select->quick))
{ // Only read index tree
tab->index=find_shortest_key(table, table->used_keys);
tab->table->file->index_init(tab->index);
tab->read_first_record= join_init_read_first_with_key;
tab->type=JT_NEXT; // Read with index_first / index_next
if (tab->select && tab->select->quick &&
table->used_keys & ((key_map) 1 << tab->select->quick->index))
{
table->key_read=1;
table->file->extra(HA_EXTRA_KEYREAD);
}
else if (table->used_keys && ! (tab->select && tab->select->quick))
{ // Only read index tree
tab->index=find_shortest_key(table, table->used_keys);
tab->table->file->index_init(tab->index);
tab->read_first_record= join_init_read_first_with_key;
tab->type=JT_NEXT; // Read with index_first / index_next
}
}
}
break;
......@@ -4556,7 +4561,8 @@ join_init_read_first_with_key(JOIN_TAB *tab)
{
int error;
TABLE *table=tab->table;
if (!table->key_read && (table->used_keys & ((key_map) 1 << tab->index)))
if (!table->key_read && (table->used_keys & ((key_map) 1 << tab->index)) &&
!table->no_keyread)
{
table->key_read=1;
table->file->extra(HA_EXTRA_KEYREAD);
......
......@@ -97,6 +97,7 @@ struct st_table {
my_bool locked_by_name;
my_bool crashed;
my_bool is_view;
my_bool no_keyread;
Field *next_number_field, /* Set if next_number is activated */
*found_next_number_field, /* Set on open */
*rowid_field;
......
......@@ -39,8 +39,7 @@
#include <errno.h>
#define MANAGER_VERSION "1.0"
#define MANAGER_GREETING "MySQL Server Management Daemon v." ## \
MANAGER_VERSION
#define MANAGER_GREETING "MySQL Server Management Daemon v. 1.0"
#define LOG_ERR 1
#define LOG_WARN 2
......@@ -218,23 +217,23 @@ struct manager_exec
int num_args;
};
#define HANDLE_DECL(com) static int handle_ ## com (struct manager_thd* thd,\
#define HANDLE_DECL(com) static int com (struct manager_thd* thd,\
char* args_start,char* args_end)
#define HANDLE_NOARG_DECL(com) static int handle_ ## com \
#define HANDLE_NOARG_DECL(com) static int com \
(struct manager_thd* thd, char* __attribute__((unused)) args_start,\
char* __attribute__((unused)) args_end)
HANDLE_NOARG_DECL(ping);
HANDLE_NOARG_DECL(quit);
HANDLE_NOARG_DECL(help);
HANDLE_NOARG_DECL(shutdown);
HANDLE_DECL(def_exec);
HANDLE_DECL(start_exec);
HANDLE_DECL(stop_exec);
HANDLE_DECL(set_exec_con);
HANDLE_NOARG_DECL(show_exec);
HANDLE_NOARG_DECL(handle_ping);
HANDLE_NOARG_DECL(handle_quit);
HANDLE_NOARG_DECL(handle_help);
HANDLE_NOARG_DECL(handle_shutdown);
HANDLE_DECL(handle_def_exec);
HANDLE_DECL(handle_start_exec);
HANDLE_DECL(handle_stop_exec);
HANDLE_DECL(handle_set_exec_con);
HANDLE_NOARG_DECL(handle_show_exec);
struct manager_cmd commands[] =
{
......@@ -375,20 +374,20 @@ static struct manager_cmd* lookup_cmd(char* s,int len)
return 0;
}
HANDLE_NOARG_DECL(ping)
HANDLE_NOARG_DECL(handle_ping)
{
client_msg(thd->vio,MSG_OK,"Server management daemon is alive");
return 0;
}
HANDLE_NOARG_DECL(quit)
HANDLE_NOARG_DECL(handle_quit)
{
client_msg(thd->vio,MSG_OK,"Goodbye");
thd->finished=1;
return 0;
}
HANDLE_NOARG_DECL(help)
HANDLE_NOARG_DECL(handle_help)
{
struct manager_cmd* cmd = commands;
Vio* vio = thd->vio;
......@@ -401,7 +400,7 @@ HANDLE_NOARG_DECL(help)
return 0;
}
HANDLE_NOARG_DECL(shutdown)
HANDLE_NOARG_DECL(handle_shutdown)
{
client_msg(thd->vio,MSG_OK,"Shutdown started, goodbye");
thd->finished=1;
......@@ -414,7 +413,7 @@ HANDLE_NOARG_DECL(shutdown)
return 0;
}
HANDLE_DECL(set_exec_con)
HANDLE_DECL(handle_set_exec_con)
{
int num_args;
const char* error=0;
......@@ -461,7 +460,7 @@ HANDLE_DECL(set_exec_con)
return 1;
}
HANDLE_DECL(start_exec)
HANDLE_DECL(handle_start_exec)
{
int num_args;
struct manager_exec* e;
......@@ -509,7 +508,7 @@ HANDLE_DECL(start_exec)
return 1;
}
HANDLE_DECL(stop_exec)
HANDLE_DECL(handle_stop_exec)
{
int num_args;
struct timespec abstime;
......@@ -564,7 +563,7 @@ HANDLE_DECL(stop_exec)
return 1;
}
HANDLE_DECL(def_exec)
HANDLE_DECL(handle_def_exec)
{
struct manager_exec* e=0,*old_e;
const char* error=0;
......@@ -597,7 +596,7 @@ HANDLE_DECL(def_exec)
return 1;
}
HANDLE_NOARG_DECL(show_exec)
HANDLE_NOARG_DECL(handle_show_exec)
{
uint i;
client_msg_pre(thd->vio,MSG_INFO,"Exec_def\tPid\tExit_status\tCon_info\
......@@ -809,19 +808,19 @@ static void log_msg(const char* fmt, int msg_type, va_list args)
pthread_mutex_unlock(&lock_log);
}
#define LOG_MSG_FUNC(type,TYPE) inline static void log_ ## type \
#define LOG_MSG_FUNC(type,TYPE) inline static void type \
(const char* fmt,...) { \
va_list args; \
va_start(args,fmt); \
log_msg(fmt,LOG_ ## TYPE,args);\
log_msg(fmt,TYPE,args);\
}
LOG_MSG_FUNC(err,ERR)
LOG_MSG_FUNC(warn,WARN)
LOG_MSG_FUNC(info,INFO)
LOG_MSG_FUNC(log_err,LOG_ERR)
LOG_MSG_FUNC(log_warn,LOG_WARN)
LOG_MSG_FUNC(log_info,LOG_INFO)
#ifndef DBUG_OFF
LOG_MSG_FUNC(debug,DEBUG)
LOG_MSG_FUNC(log_debug,LOG_DEBUG)
#else
void log_debug(char* __attribute__((unused)) fmt,...) {}
#endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment