551 lines
23 KiB
Perl
551 lines
23 KiB
Perl
#!/usr/bin/perl -w
|
|
use strict;
|
|
use warnings;
|
|
use diagnostics;
|
|
use Getopt::Long;
|
|
|
|
# Set defaults
|
|
my %opt = (
|
|
"nobad" => 0,
|
|
"nogood" => 0,
|
|
"noinfo" => 0,
|
|
"nocolor" => 0,
|
|
);
|
|
|
|
# Gather the options from the command line
|
|
GetOptions(\%opt,
|
|
'nobad',
|
|
'nogood',
|
|
'noinfo',
|
|
'nocolor',
|
|
'help',
|
|
);
|
|
|
|
if (defined $opt{'help'} && $opt{'help'} == 1) { usage(); }
|
|
|
|
sub usage {
|
|
print "\n".
|
|
" MySQL High Performance Tuning Script\n".
|
|
" Bug reports, feature requests, and downloads at http://mysqltuner.com/\n".
|
|
" Maintained by Major Hayden (major.hayden\@rackspace.com)\n\n".
|
|
" Important Usage Guidelines:\n".
|
|
" To run the script with the default options, run the script without arguments\n".
|
|
" Allow MySQL server to run for at least 24-48 hours before trusting suggestions\n".
|
|
" Some routines may require root level privileges (script will provide warnings)\n\n".
|
|
" Output Options:\n".
|
|
" --nogood Remove OK responses\n".
|
|
" --nobad Remove negative/suggestion responses\n".
|
|
" --noinfo Remove informational responses\n".
|
|
" --nocolor Don't print output in color\n".
|
|
"\n";
|
|
exit;
|
|
}
|
|
|
|
# CONFIGURATION ITEMS
|
|
my ($good,$bad,$info);
|
|
if ($opt{nocolor} == 0) {
|
|
$good = "[\e[00;32mOK\e[00m]";
|
|
$bad = "[\e[00;31m!!\e[00m]";
|
|
$info = "[\e[00;34m--\e[00m]";
|
|
} else {
|
|
$good = "[OK]";
|
|
$bad = "[!!]";
|
|
$info = "[--]";
|
|
}
|
|
|
|
sub goodprint {
|
|
if ($opt{nogood} == 1) { return 0; }
|
|
my $text = shift;
|
|
print $good." ".$text;
|
|
}
|
|
|
|
sub infoprint {
|
|
if ($opt{noinfo} == 1) { return 0; }
|
|
my $text = shift;
|
|
print $info." ".$text;
|
|
}
|
|
|
|
sub badprint {
|
|
if ($opt{nobad} == 1) { return 0; }
|
|
my $text = shift;
|
|
print $bad." ".$text;
|
|
}
|
|
|
|
my ($physical_memory,$swap_memory,$duflags);
|
|
sub os_setup {
|
|
my $os = `uname`;
|
|
$duflags = '';
|
|
if ($os =~ /Linux/) {
|
|
$physical_memory = `free -b | grep Mem | awk '{print \$2}'`;
|
|
$swap_memory = `free -b | grep Swap | awk '{print \$2}'`;
|
|
$duflags = '-b';
|
|
} elsif ($os =~ /Darwin/) {
|
|
$physical_memory = `sysctl -n hw.memsize`;
|
|
$swap_memory = `sysctl -n vm.swapusage | awk '{print \$3}' | sed 's/\..*\$//'`;
|
|
} elsif ($os =~ /BSD/) {
|
|
$physical_memory = `sysctl -n hw.realmem`;
|
|
$swap_memory = `swapinfo | grep '^/' | awk '{ s+= \$2 } END { print s }'`;
|
|
}
|
|
chomp($physical_memory);
|
|
}
|
|
|
|
my $mysqllogin;
|
|
sub mysql_setup {
|
|
my $command = `which mysqladmin`;
|
|
chomp($command);
|
|
if (! -e $command) {
|
|
badprint "Unable to find mysqladmin in your \$PATH. Is MySQL installed?\n";
|
|
exit;
|
|
}
|
|
if ( -r "/etc/psa/.psa.shadow" ) {
|
|
# It's a Plesk box, use the available credentials
|
|
$mysqllogin = "-u admin -p`cat /etc/psa/.psa.shadow`";
|
|
my $loginstatus = `mysqladmin ping $mysqllogin 2>&1`;
|
|
if ($loginstatus =~ /mysqld is alive/) {
|
|
# Login was successful, but we won't say anything to save space
|
|
return 1;
|
|
} else {
|
|
badprint "Attempted to use login credentials from Plesk, but they failed.\n";
|
|
exit 0;
|
|
}
|
|
} else {
|
|
# It's not Plesk, we should try a login
|
|
my $loginstatus = `mysqladmin ping 2>&1`;
|
|
if ($loginstatus =~ /mysqld is alive/) {
|
|
# Login went just fine
|
|
$mysqllogin = "";
|
|
# Did this go well because of a .my.cnf file or is there no password set?
|
|
my $userpath = `ls -d ~`;
|
|
chomp($userpath);
|
|
if ( -e "$userpath/.my.cnf" ) {
|
|
# Login was successful, but we won't say anything to save space
|
|
} else {
|
|
badprint "Successfully authenticated with no password - SECURITY RISK!\n";
|
|
}
|
|
return 1;
|
|
} else {
|
|
print "Please enter your MySQL login: ";
|
|
my $name = <>;
|
|
print "Please enter your MySQL password: ";
|
|
system("stty -echo"); #don't show the password
|
|
my $password = <>;
|
|
system("stty echo"); #plz give echo back
|
|
chomp($password);
|
|
chomp($name);
|
|
$mysqllogin = "-u $name -p'$password'";
|
|
my $loginstatus = `mysqladmin ping $mysqllogin 2>&1`;
|
|
if ($loginstatus =~ /mysqld is alive/) {
|
|
# Login was successful, but we won't say anything to save space
|
|
print "\n";
|
|
return 1;
|
|
} else {
|
|
print "\n".$bad." Attempted to use login credentials, but they were invalid.\n";
|
|
exit 0;
|
|
}
|
|
exit 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
my (%mystat,%myvar,$dummyselect);
|
|
sub get_all_vars {
|
|
$dummyselect = `mysql $mysqllogin -Bse "SELECT VERSION();"`;
|
|
my @mysqlvarlist = `mysql $mysqllogin -Bse "SHOW /*!50000 GLOBAL */ VARIABLES;"`;
|
|
foreach my $line (@mysqlvarlist) {
|
|
$line =~ /([a-zA-Z_]*)\s*(.*)/;
|
|
$myvar{$1} = $2;
|
|
}
|
|
my @mysqlstatlist = `mysql $mysqllogin -Bse "SHOW /*!50000 GLOBAL */ STATUS;"`;
|
|
foreach my $line (@mysqlstatlist) {
|
|
$line =~ /([a-zA-Z_]*)\s*(.*)/;
|
|
$mystat{$1} = $2;
|
|
}
|
|
}
|
|
|
|
my ($mysqlvermajor,$mysqlverminor);
|
|
sub validate_mysql_version {
|
|
($mysqlvermajor,$mysqlverminor) = $myvar{'version'} =~ /(\d)\.(\d)/;
|
|
if ($mysqlvermajor < 5) {
|
|
badprint "Your MySQL version ".$myvar{'version'}." is EOL software! Upgrade soon!\n";
|
|
} elsif ($mysqlvermajor == 5 && $mysqlverminor == 1) {
|
|
badprint "Currently running supported MySQL version ".$myvar{'version'}." (BETA - USE CAUTION)\n";
|
|
} else {
|
|
goodprint "Currently running supported MySQL version ".$myvar{'version'}."\n";
|
|
}
|
|
}
|
|
|
|
sub pretty_uptime {
|
|
my $uptime = shift;
|
|
my $seconds = $uptime % 60;
|
|
my $minutes = int(($uptime % 3600) / 60);
|
|
my $hours = int(($uptime % 86400) / (3600));
|
|
my $days = int($uptime / (86400));
|
|
my $uptimestring;
|
|
if ($days > 0) {
|
|
$uptimestring = "${days}d ${hours}h ${minutes}m ${seconds}s";
|
|
} elsif ($hours > 0) {
|
|
$uptimestring = "${hours}h ${minutes}m ${seconds}s";
|
|
} elsif ($minutes > 0) {
|
|
$uptimestring = "${minutes}m ${seconds}s";
|
|
} else {
|
|
$uptimestring = "${seconds}s";
|
|
}
|
|
return $uptimestring;
|
|
}
|
|
|
|
sub hr_bytes_rnd {
|
|
my $num = shift;
|
|
if ($num >= (1024**3)) { #GB
|
|
return int(($num/(1024**3)))."G";
|
|
} elsif ($num >= (1024**2)) { #MB
|
|
return int(($num/(1024**2)))."M";
|
|
} elsif ($num >= 1024) { #KB
|
|
return int(($num/1024))."K";
|
|
} else {
|
|
return $num;
|
|
}
|
|
}
|
|
|
|
sub hr_bytes {
|
|
my $num = shift;
|
|
if ($num >= (1024**3)) { #GB
|
|
return sprintf("%.1f",($num/(1024**3)))."G";
|
|
} elsif ($num >= (1024**2)) { #MB
|
|
return sprintf("%.1f",($num/(1024**2)))."M";
|
|
} elsif ($num >= 1024) { #KB
|
|
return sprintf("%.1f",($num/1024))."K";
|
|
} else {
|
|
return $num;
|
|
}
|
|
}
|
|
|
|
sub hr_num {
|
|
my $num = shift;
|
|
if ($num >= (1000**3)) { #GB
|
|
return int(($num/(1000**3)))."G";
|
|
} elsif ($num >= (1000**2)) { #MB
|
|
return int(($num/(1000**2)))."M";
|
|
} elsif ($num >= 1000) { #KB
|
|
return int(($num/1000))."K";
|
|
} else {
|
|
return $num;
|
|
}
|
|
}
|
|
|
|
my %mycalc;
|
|
sub calculations {
|
|
if ($mystat{'Questions'} < 1) {
|
|
badprint "Your server has not answered any queries - cannot continue...";
|
|
exit 0;
|
|
}
|
|
# Per-thread memory
|
|
if ($mysqlvermajor > 3) {
|
|
$mycalc{'per_thread_buffers'} = $myvar{'read_buffer_size'} + $myvar{'read_rnd_buffer_size'} + $myvar{'sort_buffer_size'} + $myvar{'thread_stack'} + $myvar{'join_buffer_size'};
|
|
} else {
|
|
$mycalc{'per_thread_buffers'} = $myvar{'record_buffer'} + $myvar{'record_rnd_buffer'} + $myvar{'sort_buffer'} + $myvar{'thread_stack'} + $myvar{'join_buffer_size'};
|
|
}
|
|
$mycalc{'total_per_thread_buffers'} = $mycalc{'per_thread_buffers'} * $myvar{'max_connections'};
|
|
$mycalc{'max_total_per_thread_buffers'} = $mycalc{'per_thread_buffers'} * $mystat{'Max_used_connections'};
|
|
|
|
# Server-wide memory
|
|
$mycalc{'max_tmp_table_size'} = ($myvar{'tmp_table_size'} > $myvar{'max_heap_table_size'}) ? $myvar{'max_heap_table_size'} : $myvar{'tmp_table_size'} ;
|
|
$mycalc{'server_buffers'} = $myvar{'key_buffer_size'} + $mycalc{'max_tmp_table_size'};
|
|
$mycalc{'server_buffers'} += (defined $myvar{'innodb_buffer_pool_size'}) ? $myvar{'innodb_buffer_pool_size'} : 0 ;
|
|
$mycalc{'server_buffers'} += (defined $myvar{'innodb_additional_mem_pool_size'}) ? $myvar{'innodb_additional_mem_pool_size'} : 0 ;
|
|
$mycalc{'server_buffers'} += (defined $myvar{'innodb_log_buffer_size'}) ? $myvar{'innodb_log_buffer_size'} : 0 ;
|
|
$mycalc{'server_buffers'} += (defined $myvar{'query_cache_size'}) ? $myvar{'query_cache_size'} : 0 ;
|
|
|
|
# Global memory
|
|
$mycalc{'max_used_memory'} = $mycalc{'server_buffers'} + $mycalc{"max_total_per_thread_buffers"};
|
|
$mycalc{'total_possible_used_memory'} = $mycalc{'server_buffers'} + $mycalc{'total_per_thread_buffers'};
|
|
$mycalc{'pct_physical_memory'} = int(($mycalc{'total_possible_used_memory'} * 100) / $physical_memory);
|
|
|
|
# Slow queries
|
|
$mycalc{'pct_slow_queries'} = int(($mystat{'Slow_queries'}/$mystat{'Questions'}) * 100);
|
|
|
|
# Connections
|
|
$mycalc{'pct_connections_used'} = int(($mystat{'Max_used_connections'}/$myvar{'max_connections'}) * 100);
|
|
$mycalc{'pct_connections_used'} = ($mycalc{'pct_connections_used'} > 100) ? 100 : $mycalc{'pct_connections_used'} ;
|
|
|
|
# Key buffers
|
|
if ($mysqlvermajor > 3) {
|
|
$mycalc{'pct_key_buffer_used'} = sprintf("%.1f",(1 - (($mystat{'Key_blocks_unused'} * $myvar{'key_cache_block_size'}) / $myvar{'key_buffer_size'})) * 100);
|
|
}
|
|
if ($mystat{'Key_read_requests'} > 0) {
|
|
$mycalc{'pct_keys_from_mem'} = sprintf("%.1f",(100 - (($mystat{'Key_reads'} / $mystat{'Key_read_requests'}) * 100)));
|
|
}
|
|
if ($mysqlvermajor < 5) {
|
|
$mycalc{'total_myisam_indexes'} = `find $myvar{'datadir'} -name '*.MYI' 2>&1 | xargs du $duflags '{}' 2>&1 | awk '{ s += \$1 } END { print s }'`;
|
|
if ($mycalc{'total_myisam_indexes'} =~ /^0\n$/) { $mycalc{'total_myisam_indexes'} = "fail"; }
|
|
} else {
|
|
$mycalc{'total_myisam_indexes'} = `mysql $mysqllogin -Bse "/*!50000 SELECT SUM(INDEX_LENGTH) from information_schema.TABLES where ENGINE='MyISAM' */"`;
|
|
}
|
|
chomp($mycalc{'total_myisam_indexes'});
|
|
|
|
# Query cache
|
|
if ($mysqlvermajor > 3) {
|
|
$mycalc{'query_cache_efficiency'} = sprintf("%.1f",($mystat{'Qcache_hits'} / ($mystat{'Com_select'} + $mystat{'Qcache_hits'})) * 100);
|
|
if ($myvar{'query_cache_size'}) {
|
|
$mycalc{'pct_query_cache_used'} = sprintf("%.1f",100 - ($mystat{'Qcache_free_memory'} / $myvar{'query_cache_size'}) * 100);
|
|
}
|
|
if ($mystat{'Qcache_lowmem_prunes'} == 0) {
|
|
$mycalc{'query_cache_prunes_per_day'} = 0;
|
|
} else {
|
|
$mycalc{'query_cache_prunes_per_day'} = int($mystat{'Qcache_lowmem_prunes'} / ($mystat{'Uptime'}/86400));
|
|
}
|
|
}
|
|
|
|
# Sorting
|
|
$mycalc{'total_sorts'} = $mystat{'Sort_scan'} + $mystat{'Sort_range'};
|
|
if ($mycalc{'total_sorts'} > 0) {
|
|
$mycalc{'pct_temp_sort_table'} = int(($mystat{'Sort_merge_passes'} / $mycalc{'total_sorts'}) * 100);
|
|
}
|
|
|
|
# Joins
|
|
$mycalc{'joins_without_indexes'} = $mystat{'Select_range_check'} + $mystat{'Select_full_join'};
|
|
|
|
# Temporary tables
|
|
if ($mystat{'Created_tmp_tables'} > 0) {
|
|
if ($mystat{'Created_tmp_disk_tables'} > 0) {
|
|
$mycalc{'pct_temp_disk'} = int(($mystat{'Created_tmp_disk_tables'} / $mystat{'Created_tmp_tables'}) * 100);
|
|
} else {
|
|
$mycalc{'pct_temp_disk'} = 0;
|
|
}
|
|
}
|
|
|
|
# Table cache
|
|
if ($mystat{'Opened_tables'} > 0) {
|
|
$mycalc{'table_cache_hit_rate'} = int($mystat{'Open_tables'}*100/$mystat{'Opened_tables'});
|
|
} else {
|
|
$mycalc{'table_cache_hit_rate'} = 100;
|
|
}
|
|
|
|
# Open files
|
|
if ($mystat{'Open_files'} > 0 && $myvar{'open_files_limit'} > 0) {
|
|
$mycalc{'pct_files_open'} = int($mystat{'Open_files'}*100/$myvar{'open_files_limit'});
|
|
}
|
|
|
|
# Table locks
|
|
if ($mystat{'Table_locks_immediate'} > 0) {
|
|
if ($mystat{'Table_locks_waited'} == 0) {
|
|
$mycalc{'pct_table_locks_immediate'} = 100;
|
|
} else {
|
|
$mycalc{'pct_table_locks_immediate'} = int($mystat{'Table_locks_immediate'}*100/($mystat{'Table_locks_waited'} + $mystat{'Table_locks_immediate'}));
|
|
}
|
|
}
|
|
|
|
# Thread cache
|
|
$mycalc{'thread_cache_hit_rate'} = int(100 - (($mystat{'Threads_created'} / $mystat{'Connections'}) * 100));
|
|
|
|
#foreach my $key (sort keys %mycalc) { print "$key\t\t-> \t".$mycalc{$key}."\n"; }
|
|
}
|
|
|
|
my (@decvars, @incvars, @generalrec);
|
|
sub mysql_stats {
|
|
print "-------- General Statistics --------------------------------------------------\n";
|
|
|
|
# Show uptime, queries per second, connections, traffic stats
|
|
my $qps;
|
|
if ($mystat{'Uptime'} > 0) { $qps = sprintf("%.3f",$mystat{'Questions'}/$mystat{'Uptime'}); }
|
|
if ($mystat{'Uptime'} < 86400) { push(@generalrec,"MySQL started within last 24 hours - recommendations may be inaccurate"); }
|
|
infoprint "Up for: ".pretty_uptime($mystat{'Uptime'})." (".hr_num($mystat{'Questions'}).
|
|
" q [".hr_num($qps)." qps], ".hr_num($mystat{'Connections'})." conn,".
|
|
" TX: ".hr_num($mystat{'Bytes_sent'}).", RX: ".hr_num($mystat{'Bytes_received'}).")\n";
|
|
|
|
# Memory usage
|
|
if ($mycalc{'pct_physical_memory'} > 85) {
|
|
badprint "Maximum possible memory usage: ".hr_bytes($mycalc{'total_possible_used_memory'})." ($mycalc{'pct_physical_memory'}% of installed RAM)\n";
|
|
push(@generalrec,"Reduce your overall MySQL memory footprint for system stability");
|
|
} else {
|
|
goodprint "Maximum possible memory usage: ".hr_bytes($mycalc{'total_possible_used_memory'})." ($mycalc{'pct_physical_memory'}% of installed RAM)\n";
|
|
}
|
|
|
|
# Slow queries
|
|
if ($mycalc{'pct_slow_queries'} > 5) {
|
|
badprint "Slow queries: $mycalc{'pct_slow_queries'}%\n";
|
|
} else {
|
|
goodprint "Slow queries: $mycalc{'pct_slow_queries'}%\n";
|
|
}
|
|
if ($myvar{'long_query_time'} > 5) { push(@decvars,"long_query_time (<= 5)"); }
|
|
|
|
# Connections
|
|
if ($mycalc{'pct_connections_used'} > 85) {
|
|
badprint "Highest connection usage: $mycalc{'pct_connections_used'}%\n";
|
|
push(@incvars,"max_connections (> ".$myvar{'max_connections'}.")");
|
|
push(@decvars,"wait_timeout (< ".$myvar{'wait_timeout'}.")","interactive_timeout (< ".$myvar{'interactive_timeout'}.")");
|
|
push(@generalrec,"Reduce or eliminate persistent connections to reduce connection usage")
|
|
} else {
|
|
goodprint "Highest usage of available connections: $mycalc{'pct_connections_used'}%\n";
|
|
}
|
|
|
|
# Key buffer
|
|
if ($mycalc{'total_myisam_indexes'} =~ /^fail$/) {
|
|
badprint "Cannot calculate MyISAM index size - re-run script as root user\n";
|
|
} elsif ($mycalc{'total_myisam_indexes'} == "0") {
|
|
badprint "None of your MyISAM tables are indexed - add indexes immediately\n";
|
|
} else {
|
|
if ($myvar{'key_buffer_size'} < $mycalc{'total_myisam_indexes'}) {
|
|
badprint "Key buffer size / total MyISAM indexes: ".hr_bytes($myvar{'key_buffer_size'})."/".hr_bytes($mycalc{'total_myisam_indexes'})."\n";
|
|
push(@incvars,"key_buffer_size (> ".hr_bytes($mycalc{'total_myisam_indexes'}).")");
|
|
} else {
|
|
goodprint "Key buffer size / total MyISAM indexes: ".hr_bytes($myvar{'key_buffer_size'})."/".hr_bytes($mycalc{'total_myisam_indexes'})."\n";
|
|
}
|
|
if ($mystat{'Key_read_requests'} > 0) {
|
|
if ($mycalc{'pct_keys_from_mem'} < 95) {
|
|
badprint "Key buffer hit rate: $mycalc{'pct_keys_from_mem'}%\n";
|
|
} else {
|
|
goodprint "Key buffer hit rate: $mycalc{'pct_keys_from_mem'}%\n";
|
|
}
|
|
} else {
|
|
# For the sake of space, we will be quiet here
|
|
# No queries have run that would use keys
|
|
}
|
|
}
|
|
if ($mysqlvermajor > 3 && $myvar{'max_seeks_for_key'} > 100) { push(@decvars,"max_seeks_for_key (<= 100)"); }
|
|
|
|
# Query cache
|
|
if ($mysqlvermajor < 4) {
|
|
# For the sake of space, we will be quiet here
|
|
# MySQL versions < 4.01 don't support query caching
|
|
push(@generalrec,"Upgrade MySQL to version 4+ to utilize query caching");
|
|
} elsif ($myvar{'query_cache_size'} < 1) {
|
|
badprint "Query cache is disabled\n";
|
|
push(@incvars,"query_cache_size (>= 8M)");
|
|
} elsif ($mystat{'Com_select'} == 0) {
|
|
badprint "Query cache cannot be analyzed - no SELECT statements executed\n";
|
|
} else {
|
|
if ($mycalc{'query_cache_efficiency'} < 20) {
|
|
badprint "Query cache efficiency: $mycalc{'query_cache_efficiency'}%\n";
|
|
push(@incvars,"query_cache_limit (> 1M, or use smaller result sets)");
|
|
} else {
|
|
goodprint "Query cache efficiency: $mycalc{'query_cache_efficiency'}%\n";
|
|
}
|
|
if ($mycalc{'query_cache_prunes_per_day'} > 98) {
|
|
badprint "Query cache prunes per day: $mycalc{'query_cache_prunes_per_day'}\n";
|
|
push(@incvars,"query_cache_size (> ".hr_bytes_rnd($myvar{'query_cache_size'}).")")
|
|
} else {
|
|
goodprint "Query cache prunes per day: $mycalc{'query_cache_prunes_per_day'}\n";
|
|
}
|
|
}
|
|
|
|
# Sorting
|
|
if ($mycalc{'total_sorts'} == 0) {
|
|
# For the sake of space, we will be quiet here
|
|
# No sorts have run yet
|
|
} elsif ($mycalc{'pct_temp_sort_table'} > 10) {
|
|
badprint "Sorts requiring temporary tables: $mycalc{'pct_temp_sort_table'}%\n";
|
|
push(@incvars,"sort_buffer_size (> ".hr_bytes_rnd($myvar{'sort_buffer_size'}).")");
|
|
push(@incvars,"read_rnd_buffer_size (> ".hr_bytes_rnd($myvar{'read_rnd_buffer_size'}).")");
|
|
} else {
|
|
goodprint "Sorts requiring temporary tables: $mycalc{'pct_temp_sort_table'}%\n";
|
|
}
|
|
|
|
# Joins
|
|
if ($mycalc{'joins_without_indexes'} > 0) {
|
|
badprint "Joins performed without indexes: $mycalc{'joins_without_indexes'}\n";
|
|
push(@incvars,"join_buffer_size (> ".hr_bytes($myvar{'join_buffer_size'}).", or always use indexes with joins)");
|
|
push(@generalrec,"Adjust your join queries to always utilize indexes");
|
|
} else {
|
|
# For the sake of space, we will be quiet here
|
|
# No joins have run without indexes
|
|
}
|
|
|
|
# Temporary tables
|
|
if ($mystat{'Created_tmp_tables'} > 0) {
|
|
if ($mycalc{'pct_temp_disk'} > 25) {
|
|
badprint "Temporary tables created on disk: $mycalc{'pct_temp_disk'}%\n";
|
|
push(@incvars,"tmp_table_size (> ".hr_bytes_rnd($myvar{'tmp_table_size'}).")");
|
|
push(@incvars,"max_heap_table_size (> ".hr_bytes_rnd($myvar{'max_heap_table_size'}).")");
|
|
push(@generalrec,"Be sure that tmp_table_size/max_heap_table_size are equal");
|
|
} else {
|
|
goodprint "Temporary tables created on disk: $mycalc{'pct_temp_disk'}%\n";
|
|
}
|
|
} else {
|
|
# For the sake of space, we will be quiet here
|
|
# No temporary tables have been created
|
|
}
|
|
|
|
# Thread cache
|
|
if ($mycalc{'thread_cache_hit_rate'} <= 50) {
|
|
badprint "Thread cache hit rate: $mycalc{'thread_cache_hit_rate'}%\n";
|
|
} else {
|
|
goodprint "Thread cache hit rate: $mycalc{'thread_cache_hit_rate'}%\n";
|
|
}
|
|
|
|
# Table cache
|
|
if ($mystat{'Open_tables'} > 0) {
|
|
if ($mycalc{'table_cache_hit_rate'} < 20) {
|
|
badprint "Table cache hit rate: $mycalc{'table_cache_hit_rate'}%\n";
|
|
push(@incvars,"table_cache (> ".$myvar{'table_cache'}.")");
|
|
push(@generalrec,"Increase table_cache gradually to avoid file descriptor limits");
|
|
} else {
|
|
goodprint "Table cache hit rate: $mycalc{'table_cache_hit_rate'}%\n";
|
|
}
|
|
}
|
|
|
|
# Open files
|
|
if ($myvar{'open_files_limit'} > 0) {
|
|
if ($mycalc{'pct_files_open'} > 85) {
|
|
badprint "Open file limit used: $mycalc{'pct_files_open'}%\n";
|
|
push(@incvars,"open_files_limit (> ".$myvar{'open_files_limit'}.")");
|
|
} else {
|
|
goodprint "Open file limit used: $mycalc{'pct_files_open'}%\n";
|
|
}
|
|
}
|
|
|
|
# Table locks
|
|
if (defined $mycalc{'pct_table_locks_immediate'}) {
|
|
if ($mycalc{'pct_table_locks_immediate'} < 95) {
|
|
badprint "Table locks acquired immediately: $mycalc{'pct_table_locks_immediate'}%\n";
|
|
push(@generalrec,"Optimize queries and/or use InnoDB to reduce lock wait");
|
|
} else {
|
|
goodprint "Table locks acquired immediately: $mycalc{'pct_table_locks_immediate'}%\n";
|
|
}
|
|
}
|
|
|
|
# Performance options
|
|
if ($mysqlvermajor == 3 || ($mysqlvermajor == 4 && $mysqlverminor == 0)) {
|
|
push(@generalrec,"Upgrade to MySQL 4.1+ to use concurrent MyISAM inserts");
|
|
} elsif ($myvar{'concurrent_insert'} eq "OFF") {
|
|
push(@generalrec,"Enable concurrent_insert by setting it to 'ON'");
|
|
} elsif ($myvar{'concurrent_insert'} eq 0) {
|
|
push(@generalrec,"Enable concurrent_insert by setting it to 1");
|
|
}
|
|
}
|
|
|
|
sub make_recommendations {
|
|
print "-------- Recommendations -----------------------------------------------------\n";
|
|
if (@generalrec > 0) {
|
|
print "General recommendations:\n";
|
|
foreach (@generalrec) { print " ".$_."\n"; }
|
|
}
|
|
if (@incvars > 0) {
|
|
print "Variables to increase:\n";
|
|
if ($mycalc{'pct_physical_memory'} > 85) {
|
|
print " *** MySQL's maximum memory usage exceeds your installed memory ***\n".
|
|
" *** Add more RAM before increasing any MySQL buffer variables ***\n";
|
|
}
|
|
foreach (@incvars) { print " ".$_."\n"; }
|
|
}
|
|
if (@decvars > 0) {
|
|
print "Variables to decrease:\n";
|
|
foreach (@decvars) { print " ".$_."\n"; }
|
|
}
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# BEGIN 'MAIN'
|
|
# ---------------------------------------------------------------------------
|
|
print " MySQL High-Performance Tuner - Major Hayden <major.hayden\@rackspace.com>\n".
|
|
" Bug reports, feature requests, and downloads at mysqltuner.com\n".
|
|
" Run with '--help' for additional options and output filtering\n";
|
|
os_setup; # Set up some OS variables
|
|
mysql_setup; # Gotta login first
|
|
get_all_vars; # Toss variables/status into hashes
|
|
validate_mysql_version; # Check current MySQL version
|
|
calculations; # Calculate everything we need
|
|
mysql_stats; # Print the server stats
|
|
make_recommendations; # Make recommendations based on stats
|
|
# ---------------------------------------------------------------------------
|
|
# END 'MAIN'
|
|
# ---------------------------------------------------------------------------
|