Skip to content

Commit 190186f

Browse files
feat: Improve innodb_redo_log_capacity recommendation
This commit implements a more intelligent recommendation for the `innodb_redo_log_capacity` variable for MySQL versions 8.0.30 and newer. The previous recommendation was based on a simple percentage of the `innodb_buffer_pool_size`, which was often inaccurate. The new logic calculates the hourly InnoDB log write rate based on the `Innodb_os_log_written` status variable and the server's uptime. It then suggests a value for `innodb_redo_log_capacity` that can hold at least one hour's worth of writes, rounded up to a practical power-of-2 GB value. A sanity check is also included to prevent recommending an excessively large redo log compared to the total size of the InnoDB data. For older MySQL and MariaDB versions, the existing logic for `innodb_log_file_size` and `innodb_log_files_in_group` is preserved. A new helper function `hr_bytes_practical_rnd` has been added to handle the rounding of the suggested size. Fixes #784
1 parent 5d14c82 commit 190186f

File tree

1 file changed

+119
-68
lines changed

1 file changed

+119
-68
lines changed

mysqltuner.pl

Lines changed: 119 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,20 @@ sub hr_bytes {
441441
}
442442
}
443443

444+
# Calculates the parameter passed in bytes, then rounds it to a practical power-of-2 value in GB.
445+
sub hr_bytes_practical_rnd {
446+
my $num = shift;
447+
return "0B" unless defined($num) and $num > 0;
448+
449+
my $gbs = $num / (1024**3); # convert to GB
450+
my $power_of_2_gb = 1;
451+
while ($power_of_2_gb < $gbs) {
452+
$power_of_2_gb *= 2;
453+
}
454+
455+
return $power_of_2_gb . "G";
456+
}
457+
444458
sub hr_raw {
445459
my $num = shift;
446460
return "0" unless defined($num);
@@ -6580,79 +6594,116 @@ sub mysql_innodb {
65806594
}
65816595
}
65826596
}
6583-
if ( $mycalc{'innodb_log_size_pct'} < 20
6584-
or $mycalc{'innodb_log_size_pct'} > 30 )
6585-
{
6586-
if ( defined $myvar{'innodb_redo_log_capacity'} ) {
6587-
badprint
6588-
"Ratio InnoDB redo log capacity / InnoDB Buffer pool size ("
6589-
. $mycalc{'innodb_log_size_pct'} . "%): "
6590-
. hr_bytes( $myvar{'innodb_redo_log_capacity'} ) . " / "
6591-
. hr_bytes( $myvar{'innodb_buffer_pool_size'} )
6592-
. " should be equal to 25%";
6593-
push( @adjvars,
6594-
"innodb_redo_log_capacity should be (="
6595-
. hr_bytes_rnd( $myvar{'innodb_buffer_pool_size'} / 4 )
6596-
. ") if possible, so InnoDB Redo log Capacity equals 25% of buffer pool size."
6597-
);
6598-
push( @generalrec,
6599-
"Be careful, increasing innodb_redo_log_capacity means higher crash recovery mean time"
6600-
);
6601-
}
6602-
else {
6603-
badprint "Ratio InnoDB log file size / InnoDB Buffer pool size ("
6604-
. $mycalc{'innodb_log_size_pct'} . "%): "
6605-
. hr_bytes( $myvar{'innodb_log_file_size'} ) . " * "
6606-
. $myvar{'innodb_log_files_in_group'} . " / "
6607-
. hr_bytes( $myvar{'innodb_buffer_pool_size'} )
6608-
. " should be equal to 25%";
6609-
push(
6610-
@adjvars,
6611-
"innodb_log_file_size should be (="
6612-
. hr_bytes_rnd(
6613-
(
6614-
defined $myvar{'innodb_buffer_pool_size'}
6615-
&& $myvar{'innodb_buffer_pool_size'} ne ''
6616-
? $myvar{'innodb_buffer_pool_size'}
6617-
: 0
6618-
) / (
6619-
defined $myvar{'innodb_log_files_in_group'}
6620-
&& $myvar{'innodb_log_files_in_group'} ne ''
6621-
&& $myvar{'innodb_log_files_in_group'} != 0
6622-
? $myvar{'innodb_log_files_in_group'}
6623-
: 1
6624-
) / 4
6625-
)
6626-
. ") if possible, so InnoDB total log file size equals 25% of buffer pool size."
6627-
);
6628-
push( @generalrec,
6629-
"Be careful, increasing innodb_log_file_size / innodb_log_files_in_group means higher crash recovery mean time"
6630-
);
6631-
}
6632-
if ( mysql_version_le( 5, 6, 2 ) ) {
6633-
push( @generalrec,
6634-
"For MySQL 5.6.2 and lower, total innodb_log_file_size should have a ceiling of (4096MB / log files in group) - 1MB."
6635-
);
6636-
}
6597+
# InnoDB Log File Size / InnoDB Redo Log Capacity Recommendations
6598+
# For MySQL < 8.0.30, the recommendation is based on innodb_log_file_size and innodb_log_files_in_group.
6599+
# For MySQL >= 8.0.30, innodb_redo_log_capacity replaces the old system.
6600+
if ( mysql_version_ge( 8, 0, 30 ) ) {
6601+
# New recommendation logic for MySQL >= 8.0.30
6602+
infoprint "InnoDB Redo Log Capacity is set to " . hr_bytes($myvar{'innodb_redo_log_capacity'});
6603+
6604+
my $innodb_os_log_written = $mystat{'Innodb_os_log_written'} || 0;
6605+
my $uptime = $mystat{'Uptime'} || 1;
6606+
6607+
if ($uptime > 3600) { # Only make a recommendation if server has been up for at least an hour
6608+
my $hourly_rate = $innodb_os_log_written / ( $uptime / 3600 );
6609+
my $suggested_redo_log_capacity_str = hr_bytes_practical_rnd($hourly_rate);
6610+
my $suggested_redo_log_capacity_bytes = hr_raw($suggested_redo_log_capacity_str);
6611+
6612+
infoprint "Hourly InnoDB log write rate: " . hr_bytes_rnd($hourly_rate) . "/hour";
66376613

6614+
if (hr_raw($myvar{'innodb_redo_log_capacity'}) < $hourly_rate) {
6615+
badprint "Your innodb_redo_log_capacity is not large enough to hold at least 1 hour of writes.";
6616+
push( @adjvars, "innodb_redo_log_capacity (>= " . $suggested_redo_log_capacity_str . ")" );
6617+
} else {
6618+
goodprint "Your innodb_redo_log_capacity is sized to handle more than 1 hour of writes.";
6619+
}
6620+
6621+
# Sanity check against total InnoDB data size
6622+
if ( defined $enginestats{'InnoDB'} and $enginestats{'InnoDB'} > 0 ) {
6623+
my $total_innodb_size = $enginestats{'InnoDB'};
6624+
if ( $suggested_redo_log_capacity_bytes > $total_innodb_size * 0.25 ) {
6625+
infoprint "The suggested innodb_redo_log_capacity (" . $suggested_redo_log_capacity_str . ") is more than 25% of your total InnoDB data size. This might be unnecessarily large.";
6626+
}
6627+
}
6628+
} else {
6629+
infoprint "Server uptime is less than 1 hour. Cannot make a reliable recommendation for innodb_redo_log_capacity.";
6630+
}
66386631
}
66396632
else {
6640-
if ( defined $myvar{'innodb_redo_log_capacity'} ) {
6641-
goodprint
6642-
"Ratio InnoDB Redo Log Capacity / InnoDB Buffer pool size: "
6643-
. hr_bytes( $myvar{'innodb_redo_log_capacity'} ) . "/"
6644-
. hr_bytes( $myvar{'innodb_buffer_pool_size'} )
6645-
. " should be equal to 25%";
6633+
# Keep existing logic for older versions
6634+
if ( $mycalc{'innodb_log_size_pct'} < 20
6635+
or $mycalc{'innodb_log_size_pct'} > 30 )
6636+
{
6637+
if ( defined $myvar{'innodb_redo_log_capacity'} ) {
6638+
badprint
6639+
"Ratio InnoDB redo log capacity / InnoDB Buffer pool size ("
6640+
. $mycalc{'innodb_log_size_pct'} . "%): "
6641+
. hr_bytes( $myvar{'innodb_redo_log_capacity'} ) . " / "
6642+
. hr_bytes( $myvar{'innodb_buffer_pool_size'} )
6643+
. " should be equal to 25%";
6644+
push( @adjvars,
6645+
"innodb_redo_log_capacity should be (="
6646+
. hr_bytes_rnd( $myvar{'innodb_buffer_pool_size'} / 4 )
6647+
. ") if possible, so InnoDB Redo log Capacity equals 25% of buffer pool size."
6648+
);
6649+
push( @generalrec,
6650+
"Be careful, increasing innodb_redo_log_capacity means higher crash recovery mean time"
6651+
);
6652+
}
6653+
else {
6654+
badprint "Ratio InnoDB log file size / InnoDB Buffer pool size ("
6655+
. $mycalc{'innodb_log_size_pct'} . "%): "
6656+
. hr_bytes( $myvar{'innodb_log_file_size'} ) . " * "
6657+
. $myvar{'innodb_log_files_in_group'} . " / "
6658+
. hr_bytes( $myvar{'innodb_buffer_pool_size'} )
6659+
. " should be equal to 25%";
6660+
push(
6661+
@adjvars,
6662+
"innodb_log_file_size should be (="
6663+
. hr_bytes_rnd(
6664+
(
6665+
defined $myvar{'innodb_buffer_pool_size'}
6666+
&& $myvar{'innodb_buffer_pool_size'} ne ''
6667+
? $myvar{'innodb_buffer_pool_size'}
6668+
: 0
6669+
) / (
6670+
defined $myvar{'innodb_log_files_in_group'}
6671+
&& $myvar{'innodb_log_files_in_group'} ne ''
6672+
&& $myvar{'innodb_log_files_in_group'} != 0
6673+
? $myvar{'innodb_log_files_in_group'}
6674+
: 1
6675+
) / 4
6676+
)
6677+
. ") if possible, so InnoDB total log file size equals 25% of buffer pool size."
6678+
);
6679+
push( @generalrec,
6680+
"Be careful, increasing innodb_log_file_size / innodb_log_files_in_group means higher crash recovery mean time"
6681+
);
6682+
}
6683+
if ( mysql_version_le( 5, 6, 2 ) ) {
6684+
push( @generalrec,
6685+
"For MySQL 5.6.2 and lower, total innodb_log_file_size should have a ceiling of (4096MB / log files in group) - 1MB."
6686+
);
6687+
}
66466688
}
66476689
else {
6648-
push( @generalrec,
6649-
"Before changing innodb_log_file_size and/or innodb_log_files_in_group read this: https://bit.ly/2TcGgtU"
6650-
);
6651-
goodprint "Ratio InnoDB log file size / InnoDB Buffer pool size: "
6652-
. hr_bytes( $myvar{'innodb_log_file_size'} ) . " * "
6653-
. $myvar{'innodb_log_files_in_group'} . "/"
6654-
. hr_bytes( $myvar{'innodb_buffer_pool_size'} )
6655-
. " should be equal to 25%";
6690+
if ( defined $myvar{'innodb_redo_log_capacity'} ) {
6691+
goodprint
6692+
"Ratio InnoDB Redo Log Capacity / InnoDB Buffer pool size: "
6693+
. hr_bytes( $myvar{'innodb_redo_log_capacity'} ) . "/"
6694+
. hr_bytes( $myvar{'innodb_buffer_pool_size'} )
6695+
. " should be equal to 25%";
6696+
}
6697+
else {
6698+
push( @generalrec,
6699+
"Before changing innodb_log_file_size and/or innodb_log_files_in_group read this: https://bit.ly/2TcGgtU"
6700+
);
6701+
goodprint "Ratio InnoDB log file size / InnoDB Buffer pool size: "
6702+
. hr_bytes( $myvar{'innodb_log_file_size'} ) . " * "
6703+
. $myvar{'innodb_log_files_in_group'} . "/"
6704+
. hr_bytes( $myvar{'innodb_buffer_pool_size'} )
6705+
. " should be equal to 25%";
6706+
}
66566707
}
66576708
}
66586709

0 commit comments

Comments
 (0)