A lot of new info about indexes and table structures
This commit is contained in:
parent
c1661f326e
commit
926c9a92ca
1 changed files with 154 additions and 14 deletions
168
mysqltuner.pl
168
mysqltuner.pl
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env perl
|
#!/usr/bin/env perl
|
||||||
# mysqltuner.pl - Version 1.8.5
|
# mysqltuner.pl - Version 1.8.7
|
||||||
# High Performance MySQL Tuning Script
|
# High Performance MySQL Tuning Script
|
||||||
# Copyright (C) 2006-2021 Major Hayden - major@mhtx.net
|
# Copyright (C) 2006-2021 Major Hayden - major@mhtx.net
|
||||||
#
|
#
|
||||||
|
@ -56,7 +56,7 @@ use Cwd 'abs_path';
|
||||||
#use Env;
|
#use Env;
|
||||||
|
|
||||||
# Set up a few variables for use in the script
|
# Set up a few variables for use in the script
|
||||||
my $tunerversion = "1.8.5";
|
my $tunerversion = "1.8.7";
|
||||||
my ( @adjvars, @generalrec );
|
my ( @adjvars, @generalrec );
|
||||||
|
|
||||||
# Set defaults
|
# Set defaults
|
||||||
|
@ -1064,6 +1064,51 @@ sub select_str_g {
|
||||||
return trim(@val);
|
return trim(@val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub select_user_dbs {
|
||||||
|
return select_array("SELECT DISTINCT TABLE_SCHEMA FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema', 'percona', 'sys')")
|
||||||
|
}
|
||||||
|
|
||||||
|
sub select_tables_db()
|
||||||
|
{
|
||||||
|
my $schema=shift;
|
||||||
|
return select_array("SELECT DISTINCT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA='$schema'")
|
||||||
|
}
|
||||||
|
sub select_indexes_db()
|
||||||
|
{
|
||||||
|
my $schema=shift;
|
||||||
|
return select_array("SELECT DISTINCT INDEX_NAME FROM information_schema.STATISTICS WHERE TABLE_SCHEMA='$schema'")
|
||||||
|
}
|
||||||
|
|
||||||
|
sub select_views_db
|
||||||
|
{
|
||||||
|
my $schema=shift;
|
||||||
|
return select_array("SELECT DISTINCT TABLE_NAME FROM information_schema.VIEWS WHERE TABLE_SCHEMA='$schema'")
|
||||||
|
}
|
||||||
|
sub select_triggers_db
|
||||||
|
{
|
||||||
|
my $schema=shift;
|
||||||
|
return select_array("SELECT DISTINCT TRIGGER_NAME FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA='$schema'")
|
||||||
|
}
|
||||||
|
|
||||||
|
sub select_routines_db
|
||||||
|
{
|
||||||
|
my $schema=shift;
|
||||||
|
return select_array("SELECT DISTINCT ROUTINE_NAME FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA='$schema'")
|
||||||
|
}
|
||||||
|
|
||||||
|
sub select_table_indexes_db
|
||||||
|
{
|
||||||
|
my $schema=shift;
|
||||||
|
my $tbname=shift;
|
||||||
|
return select_array("SELECT INDEX_NAME FROM information_schema.STATISTICS WHERE TABLE_SCHEMA='$schema' AND TABLE_NAME='$tbname'")
|
||||||
|
}
|
||||||
|
sub select_table_columns_db{
|
||||||
|
my $schema=shift;
|
||||||
|
my $table=shift;
|
||||||
|
return select_array("SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='$schema' AND TABLE_NAME='$table'")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sub get_tuning_info {
|
sub get_tuning_info {
|
||||||
my @infoconn = select_array "\\s";
|
my @infoconn = select_array "\\s";
|
||||||
my ( $tkey, $tval );
|
my ( $tkey, $tval );
|
||||||
|
@ -3521,7 +3566,7 @@ sub mysql_myisam {
|
||||||
. hr_bytes( $myvar{'key_buffer_size'} )
|
. hr_bytes( $myvar{'key_buffer_size'} )
|
||||||
. " cache)";
|
. " cache)";
|
||||||
|
|
||||||
#push(@adjvars,"key_buffer_size (\~ ".hr_num( $myvar{'key_buffer_size'} * $mycalc{'pct_key_buffer_used'} / 100).")");
|
push(@adjvars,"key_buffer_size (\~ ".hr_num( $myvar{'key_buffer_size'} * $mycalc{'pct_key_buffer_used'} / 100).")");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
goodprint "Key buffer used: $mycalc{'pct_key_buffer_used'}% ("
|
goodprint "Key buffer used: $mycalc{'pct_key_buffer_used'}% ("
|
||||||
|
@ -6120,12 +6165,21 @@ sub mysql_databases {
|
||||||
"SELECT DISTINCT(ENGINE) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$_'"
|
"SELECT DISTINCT(ENGINE) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$_'"
|
||||||
)
|
)
|
||||||
) . ")";
|
) . ")";
|
||||||
|
foreach my $eng(select_array(
|
||||||
|
"SELECT DISTINCT(ENGINE) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$_'"
|
||||||
|
)) {
|
||||||
|
infoprint " +-- ENGINE $eng : " .
|
||||||
|
select_one("SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$dbinfo[0]' AND ENGINE='$eng'") .
|
||||||
|
" TABLE(s)";
|
||||||
|
}
|
||||||
badprint "Index size is larger than data size for $dbinfo[0] \n"
|
badprint "Index size is larger than data size for $dbinfo[0] \n"
|
||||||
if ( $dbinfo[2] ne 'NULL' )
|
if ( $dbinfo[2] ne 'NULL' )
|
||||||
and ( $dbinfo[3] ne 'NULL' )
|
and ( $dbinfo[3] ne 'NULL' )
|
||||||
and ( $dbinfo[2] < $dbinfo[3] );
|
and ( $dbinfo[2] < $dbinfo[3] );
|
||||||
badprint "There are " . $dbinfo[5] . " storage engines. Be careful. \n"
|
unless ($dbinfo[5] == 1) {
|
||||||
if $dbinfo[5] > 1;
|
badprint "There are " . $dbinfo[5] . " storage engines. Be careful. \n";
|
||||||
|
push @generalrec, "Select one storage engine (InnoDB is a good choice) for all tables in $dbinfo[0] database ($dbinfo[5] engines detected)";
|
||||||
|
}
|
||||||
$result{'Databases'}{ $dbinfo[0] }{'Rows'} = $dbinfo[1];
|
$result{'Databases'}{ $dbinfo[0] }{'Rows'} = $dbinfo[1];
|
||||||
$result{'Databases'}{ $dbinfo[0] }{'Tables'} = $dbinfo[6];
|
$result{'Databases'}{ $dbinfo[0] }{'Tables'} = $dbinfo[6];
|
||||||
$result{'Databases'}{ $dbinfo[0] }{'Collations'} = $dbinfo[7];
|
$result{'Databases'}{ $dbinfo[0] }{'Collations'} = $dbinfo[7];
|
||||||
|
@ -6217,8 +6271,11 @@ sub mysql_tables {
|
||||||
if ( mysql_version_ge(8) and not mysql_version_eq(10) ) {
|
if ( mysql_version_ge(8) and not mysql_version_eq(10) ) {
|
||||||
infoprint
|
infoprint
|
||||||
"MySQL and Percona version 8 and greater have remove PROCEDURE ANALYSE feature";
|
"MySQL and Percona version 8 and greater have remove PROCEDURE ANALYSE feature";
|
||||||
|
$opt{colstat} = 0;
|
||||||
|
infoprint "Disabling colstat parameter";
|
||||||
|
|
||||||
}
|
}
|
||||||
foreach (@dblist) {
|
foreach (select_user_dbs()) {
|
||||||
my $dbname = $_;
|
my $dbname = $_;
|
||||||
next unless defined $_;
|
next unless defined $_;
|
||||||
infoprint "Database: " . $_ . "";
|
infoprint "Database: " . $_ . "";
|
||||||
|
@ -6228,6 +6285,28 @@ sub mysql_tables {
|
||||||
foreach (@dbtable) {
|
foreach (@dbtable) {
|
||||||
my $tbname = $_;
|
my $tbname = $_;
|
||||||
infoprint " +-- TABLE: $tbname";
|
infoprint " +-- TABLE: $tbname";
|
||||||
|
infoprint " +-- TYPE: ".select_one("SELECT ENGINE FROM information_schema.tables where TABLE_schema='$dbname' AND TABLE_NAME='$tbname'");
|
||||||
|
|
||||||
|
my $selIdxReq = <<"ENDSQL";
|
||||||
|
SELECT index_name AS idxname,
|
||||||
|
GROUP_CONCAT(column_name ORDER BY seq_in_index) AS cols,
|
||||||
|
INDEX_TYPE as type
|
||||||
|
FROM information_schema.statistics
|
||||||
|
WHERE INDEX_SCHEMA='$dbname'
|
||||||
|
AND TABLE_NAME='$tbname'
|
||||||
|
ENDSQL
|
||||||
|
my @tbidx=select_array($selIdxReq);
|
||||||
|
my $found=0;
|
||||||
|
foreach my $idx(@tbidx) {
|
||||||
|
my @info = split /\s/, $idx;
|
||||||
|
next if $info[0] eq 'NULL';
|
||||||
|
infoprint " +-- Index $info[0] - Cols: $info[1] - Type: $info[2]";
|
||||||
|
$found++;
|
||||||
|
}
|
||||||
|
if ($found == 0) {
|
||||||
|
badprint ("Table $dbname.$tbname has no index defined");
|
||||||
|
push @generalrec, "Add at least a primary key on table $dbname.$tbname";
|
||||||
|
}
|
||||||
my @tbcol = select_array(
|
my @tbcol = select_array(
|
||||||
"SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='$dbname' AND TABLE_NAME='$tbname'"
|
"SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='$dbname' AND TABLE_NAME='$tbname'"
|
||||||
);
|
);
|
||||||
|
@ -6239,11 +6318,10 @@ sub mysql_tables {
|
||||||
"SELECT IS_NULLABLE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='$dbname' AND TABLE_NAME='$tbname' AND COLUMN_NAME='$_' "
|
"SELECT IS_NULLABLE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='$dbname' AND TABLE_NAME='$tbname' AND COLUMN_NAME='$_' "
|
||||||
);
|
);
|
||||||
|
|
||||||
infoprint " +-- Column $tbname.$_:";
|
|
||||||
my $current_type =
|
my $current_type =
|
||||||
uc($ctype) . ( $isnull eq 'NO' ? " NOT NULL" : "" );
|
uc($ctype) . ( $isnull eq 'NO' ? " NOT NULL" : "NULL" );
|
||||||
my $optimal_type = '';
|
my $optimal_type = '';
|
||||||
|
infoprint " +-- Column $tbname.$_: $current_type";
|
||||||
if ( $opt{colstat} == 1 ) {
|
if ( $opt{colstat} == 1 ) {
|
||||||
$optimal_type = select_str_g( "Optimal_fieldtype",
|
$optimal_type = select_str_g( "Optimal_fieldtype",
|
||||||
"SELECT \\`$_\\` FROM \\`$dbname\\`.\\`$tbname\\` PROCEDURE ANALYSE(100000)"
|
"SELECT \\`$_\\` FROM \\`$dbname\\`.\\`$tbname\\` PROCEDURE ANALYSE(100000)"
|
||||||
|
@ -6252,7 +6330,7 @@ sub mysql_tables {
|
||||||
and not mysql_version_eq(10) );
|
and not mysql_version_eq(10) );
|
||||||
}
|
}
|
||||||
if ( $optimal_type eq '' ) {
|
if ( $optimal_type eq '' ) {
|
||||||
infoprint " Current Fieldtype: $current_type";
|
#infoprint " +-- Current Fieldtype: $current_type";
|
||||||
|
|
||||||
#infoprint " Optimal Fieldtype: Not available";
|
#infoprint " Optimal Fieldtype: Not available";
|
||||||
}
|
}
|
||||||
|
@ -6260,11 +6338,11 @@ sub mysql_tables {
|
||||||
and $current_type !~ /.*DATETIME.*/
|
and $current_type !~ /.*DATETIME.*/
|
||||||
and $current_type !~ /.*TIMESTAMP.*/ )
|
and $current_type !~ /.*TIMESTAMP.*/ )
|
||||||
{
|
{
|
||||||
infoprint " Current Fieldtype: $current_type";
|
infoprint " +-- Current Fieldtype: $current_type";
|
||||||
if ( $optimal_type =~ /.*ENUM\(.*/ ) {
|
if ( $optimal_type =~ /.*ENUM\(.*/ ) {
|
||||||
$optimal_type = "ENUM( ... )";
|
$optimal_type = "ENUM( ... )";
|
||||||
}
|
}
|
||||||
infoprint " Optimal Fieldtype: $optimal_type ";
|
infoprint " +-- Optimal Fieldtype: $optimal_type ";
|
||||||
if ( $optimal_type !~ /.*ENUM\(.*/ ) {
|
if ( $optimal_type !~ /.*ENUM\(.*/ ) {
|
||||||
badprint
|
badprint
|
||||||
"Consider changing type for column $_ in table $dbname.$tbname";
|
"Consider changing type for column $_ in table $dbname.$tbname";
|
||||||
|
@ -6360,7 +6438,34 @@ ENDSQL
|
||||||
badprint "$info[1] has a low selectivity";
|
badprint "$info[1] has a low selectivity";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
infoprint "Indexes per database:";
|
||||||
|
foreach my $dbname (select_user_dbs()) {
|
||||||
|
infoprint "Database: " . $dbname . "";
|
||||||
|
$selIdxReq = <<"ENDSQL";
|
||||||
|
SELECT concat(concat(table_name,'.'), index_name) AS idxname,
|
||||||
|
GROUP_CONCAT(column_name ORDER BY seq_in_index) AS cols,
|
||||||
|
CARDINALITY as card,
|
||||||
|
INDEX_TYPE as type,
|
||||||
|
COMMENT as comment
|
||||||
|
FROM information_schema.statistics
|
||||||
|
WHERE INDEX_SCHEMA='$dbname'
|
||||||
|
AND index_name IS NOT NULL
|
||||||
|
ENDSQL
|
||||||
|
my $found=0;
|
||||||
|
foreach my $idxinfo (select_array($selIdxReq))
|
||||||
|
{
|
||||||
|
my @info = split /\s/, $idxinfo;
|
||||||
|
next if $info[0] eq 'NULL';
|
||||||
|
infoprint " +-- INDEX : " . $info[0];
|
||||||
|
infoprint " +-- COLUMNS : " . $info[1];
|
||||||
|
infoprint " +-- CARDINALITY: " . $info[2];
|
||||||
|
infoprint " +-- TYPE : " . $info[4] if defined $info[4];
|
||||||
|
infoprint " +-- COMMENT : " . $info[5] if defined $info[5];
|
||||||
|
$found++;
|
||||||
|
}
|
||||||
|
badprint "No index found for $dbname database" if $found == 0;
|
||||||
|
push @generalrec, "Add indexes on tables from $dbname database" if $found == 0;
|
||||||
|
}
|
||||||
return
|
return
|
||||||
unless ( defined( $myvar{'performance_schema'} )
|
unless ( defined( $myvar{'performance_schema'} )
|
||||||
and $myvar{'performance_schema'} eq 'ON' );
|
and $myvar{'performance_schema'} eq 'ON' );
|
||||||
|
@ -6386,6 +6491,38 @@ ENDSQL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub mysql_views()
|
||||||
|
{
|
||||||
|
subheaderprint "Views Metrics";
|
||||||
|
unless ( mysql_version_ge( 5, 5 ) ) {
|
||||||
|
infoprint
|
||||||
|
"Skip Index metrics from information schema missing in this version";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sub mysql_routines()
|
||||||
|
{
|
||||||
|
subheaderprint "Routines Metrics";
|
||||||
|
unless ( mysql_version_ge( 5, 5 ) ) {
|
||||||
|
infoprint
|
||||||
|
"Skip Index metrics from information schema missing in this version";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sub mysql_triggers()
|
||||||
|
{
|
||||||
|
subheaderprint "Triggers Metrics";
|
||||||
|
unless ( mysql_version_ge( 5, 5 ) ) {
|
||||||
|
infoprint
|
||||||
|
"Skip Index metrics from information schema missing in this version";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
# Take the two recommendation arrays and display them at the end of the output
|
# Take the two recommendation arrays and display them at the end of the output
|
||||||
sub make_recommendations {
|
sub make_recommendations {
|
||||||
$result{'Recommendations'} = \@generalrec;
|
$result{'Recommendations'} = \@generalrec;
|
||||||
|
@ -6569,6 +6706,9 @@ mysql_databases; # Show informations about databases
|
||||||
mysql_tables; # Show informations about table column
|
mysql_tables; # Show informations about table column
|
||||||
|
|
||||||
mysql_indexes; # Show informations about indexes
|
mysql_indexes; # Show informations about indexes
|
||||||
|
mysql_views; # Show informations about views
|
||||||
|
mysql_triggers; # Show informations about triggers
|
||||||
|
mysql_routines; # Show informations about routines
|
||||||
security_recommendations; # Display some security recommendations
|
security_recommendations; # Display some security recommendations
|
||||||
cve_recommendations; # Display related CVE
|
cve_recommendations; # Display related CVE
|
||||||
calculations; # Calculate everything we need
|
calculations; # Calculate everything we need
|
||||||
|
@ -6603,7 +6743,7 @@ __END__
|
||||||
|
|
||||||
=head1 NAME
|
=head1 NAME
|
||||||
|
|
||||||
MySQLTuner 1.8.5 - MySQL High Performance Tuning Script
|
MySQLTuner 1.8.7 - MySQL High Performance Tuning Script
|
||||||
|
|
||||||
=head1 IMPORTANT USAGE GUIDELINES
|
=head1 IMPORTANT USAGE GUIDELINES
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue