#!/usr/bin/env bash working_dir="$(dirname $(realpath $0))" conf_file="${working_dir}/conf/optimize_mysql_tables.conf" tmp_log_file="$(mktemp)" # ------------- # - Variable settings # ------------- DEFAULT_MYSQL_CREDENTIAL_ARGS="--login-path=local" DEFAULT_LOG_FILE="/var/log/optimize_mysql_tables.log" VERBOSE=false # ------------- # --- Some functions # ------------- usage() { [[ -n "$1" ]] && error "$1" [[ $terminal ]] && echo -e " \033[1mUsage:\033[m $(basename $0) [-h|-v] [DB-Name] \033[1mDescription\033[m Script checks (and reorganizes) all tables of all databases by executing MySQL command 'OPTIMIZE TABLE'. If a database is given at the command line, only tables of that database will be checked. If a check on a table fails, MySQL command 'REPAIR TABLE' will be executed on that table. \033[1mOptions\033[m -h Prints this help -v Verbose output if running on a terminal. \033[1mFiles\033[m $conf_file: Configuration file " clean_up 1 } clean_up() { # Perform program exit housekeeping rm -f $tmp_log_file exit $1 } echononl(){ if $terminal ; then echo X\\c > /tmp/shprompt$$ if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then echo -e -n "$*\\c" 1>&2 else echo -e -n "$*" 1>&2 fi rm /tmp/shprompt$$ fi } fatal(){ echo "" if $terminal ; then if [[ -n "$*" ]] ; then echo -e " [ \033[31m\033[1mFatal\033[m ]: $*" echo "" echo -e " \033[31m\033[1mScript will be interrupted.\033[m\033[m" else echo -e " \033[31m\033[1mFatal error\033[m: \033[1mScript will be interrupted.\033[m" fi else if [[ -n "$*" ]] ; then echo " [ Fatal ]: $*" echo "" echo " Script was terminated.." else echo " Fatal error: Script was terminated.." fi fi echo "" clean_up 1 } error(){ echo "" if $terminal ; then echo -e " [ \033[31m\033[1mError\033[m ]: $*" else echo " [ Error ]: $*" fi echo "" } warn (){ if $terminal ; then echo "" echo -e " [ \033[33m\033[1mWarning\033[m ]: $*" echo "" else echo " [ Warning ]: $*" fi } info (){ if $terminal ; then echo "" echo -e " [ \033[32m\033[1mInfo\033[m ]: $*" echo "" else echo " [ Info ]: $*" fi } echo_ok() { if $terminal ; then echo -e "\033[80G[ \033[32mok\033[m ]" fi } echo_failed(){ if $terminal ; then echo -e "\033[80G[ \033[1;31mfailed\033[m ]" fi } echo_skipped() { if $terminal ; then echo -e "\033[80G[ \033[37mskipped\033[m ]" fi } trim() { local var="$*" var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters echo -n "$var" } trap clean_up SIGHUP SIGINT SIGTERM # - Is this script running on terminal ? # - if [[ -t 1 ]] ; then terminal=true else terminal=false fi mysql=`which mysql` if [ -z "$mysql" ]; then if [ -x "/usr/local/mysql/bin/mysql" ]; then mysql=/usr/local/mysql/bin/mysql else fatal "No binary 'mysql' found!" fi fi # ------------- # - Read Commandline Parameters # ------------- while getopts hv opt ; do case $opt in v) VERBOSE=true ;; h) usage ;; *) usage esac done shift $(expr $OPTIND - 1) [[ "$#" -gt 1 ]] && usage "Wrong number or order of arguments given!" # - Print help? # - if [[ "$(trim $*)" =~ " -h" ]] || [[ "$(trim $*)" =~ " --help" ]] ; then usage fi # ------------- # - Load Settings from configuration file # ------------- if [[ -n "$1 " ]] ; then GIVEN_DATABASE="$1" else GIVEN_DATABASE="" fi if $terminal ; then echo "" fi echononl " Loading configuration settings from $(basename ${conf_file}).." if [[ -f "$conf_file" ]]; then source "$conf_file" > $tmp_log_file 2>&1 if [[ $? -eq 0 ]]; then echo_ok else echo_failed fatal "$(cat $tmp_log_file)" fi else echo_skipped if $terminal ;then warn "No Configuration File found. Loading defaults.." fi fi [[ -z "$mysql_credential_args" ]] && mysql_credential_args="$DEFAULT_MYSQL_CREDENTIAL_ARGS" [[ -z "$log_file" ]] && log_file="$DEFAULT_LOG_FILE" if [[ ${#mysql_credential_args_arr[@]} -eq 0 ]]; then [[ -z "$mysql_credential_args" ]] && mysql_credential_args="$DEFAULT_MYSQL_CREDENTIAL_ARGS" mysql_credential_args_arr="default:$mysql_credential_args" fi declare -i length_table_name declare -i number_blank_signd declare -i index_i for _val in ${mysql_credential_args_arr[@]} ; do _all_success=true IFS=':' read -a _val_arr <<< "${_val}" mysql_version="${_val_arr[0]}" mysql_credential_args="${_val_arr[1]}" DATABASES="$($mysql $mysql_credential_args -N -s -e "show databases")" found=false if [[ -n "$GIVEN_DATABASE" ]] ; then for db in $DATABASES ; do if [[ "$db" = "$GIVEN_DATABASE" ]]; then DATABASES="$GIVEN_DATABASE" if $terminal ; then echo "" echo -e "[ \033[37m\033[1mMySQL $mysql_version\033[m ]: optimize (and repair) tables of database '$GIVEN_DATABASE'." fi echo -e "[ MySQL $mysql_version ]: optimize (and repair) tables of database '$GIVEN_DATABASE'." > $log_file found=true fi done if ! $found ; then continue fi else if $terminal ; then echo "" echo -e "[ \033[37m\033[1mMySQL $mysql_version\033[m ]: optimize (and repair) tables of databases at host '$(hostname -f)'." fi echo -e "[ MySQL $mysql_version ]: optimize (and repair) tables of databases at host '$(hostname -f)'." > $log_file fi length_table_name=0 for db in $DATABASES ; do [ "$db" = "information_schema" ] && continue [ "$db" = "performance_schema" ] && continue [ "$db" = "mysql" ] && continue if $terminal ;then echo "" echo -e " [$(date)] Optimize tables in database '${db}'.." fi echo "" >> $log_file echo -e " [$(date)] Optimize tables in database '${db}'.." >> $log_file TABLES=`$mysql $mysql_credential_args $db -N -s -e "show tables"` for table in $TABLES ; do # - Ommit InnoDB tables # - _engine="$($mysql $mysql_credential_args -N -s -e "SELECT ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA = '$db' AND TABLE_NAME = '$table'")" if [[ "${_engine,,}" = 'innodb' ]] ; then if $VERBOSE ; then echo -e " [$(date)] Ommit table '$table' - storage engine is InnoDB" fi echo -e " [$(date)] Ommit table '$table' - storage engine is InnoDB" >> $log_file continue fi if $terminal ; then blank_signs="" if [[ $length_table_name -gt ${#table} ]]; then number_blank_sign=$(expr $length_table_name - ${#table}) index_i=0 while [[ $index_i -lt $number_blank_sign ]] ; do blank_signs="$blank_signs " (( index_i++ )) done echo -en "\033[1G" fi echo -en "\033[1G \033[32mOptimize table \033[1m$table\033[m$blank_signs\033[1G" fi echo -e " [$(date)] Optimize table '$table'" >> $log_file length_table_name=${#table} $mysql $mysql_credential_args $db -N -s -e "OPTIMIZE TABLE \`$table\`" > $tmp_log_file 2>&1 if [[ $? -ne 0 ]]; then warn "Optimizing table \"${table}\" of database \"$db\" failed. Trying to repair.." echo "" >> $log_file echo " [$(date)] warning: Optimizing table \"${table}\" of database \"$db\" failed." >> $log_file echo "" >> $log_file echo " [$(date)] Repair table '$table'" >> $log_file $mysql $mysql_credential_args $db -N -s -e "REPAIR TABLE \`$table\`" > $tmp_log_file 2>&1 if [[ $? -ne 0 ]]; then _all_success=false error "Repairing table '$table' failed.\n$(cat "$tmp_log_file")" error_messages_arr+=("MySQL $mysql_version: Error while repairing table '${table}' of database '$db'.") echo "" >> $log_file echo -e " [$(date)] error: Repairing table '$table' failed of database \"$db\" failed..\n$(cat "$tmp_log_file")" >> $log_file echo "" >> $log_file else echo -e " [$(date)] Optimize table '$table'" >> $log_file $mysql $mysql_credential_args $db -N -s -e "OPTIMIZE TABLE \`$table\`" > $tmp_log_file 2>&1 if [[ $? -ne 0 ]]; then error "Reoptimizing table \"${table}\" of database \"$db\" failed.\n$(cat "$tmp_log_file")" error_messages_arr+=("MySQL $mysql_version: Error while (re-)optimizing table '${table}' of database '$db'.") echo "" >> $log_file echo -e " [$(date)] error: Reoptimizing table \"${table}\" of database \"$db\" failed.\n$(cat "$tmp_log_file")" >> $log_file echo "" >> $log_file else info "Reoptimizing table \"${table}\" of database \"$db\" was successfully." fi fi if $terminal ;then echo "" fi fi done if $terminal ;then echo -en "\033[1G [$(date)] End optimize tables in database '${db}'.." echo fi echo -e " [$(date)] End optimize tables in database '${db}'" >> $log_file done if $_all_success ; then info_messages_arr+=("MySQL $mysql_version: The optimization of the MySQL tables of all databases were successful.") fi if [[ -n "$GIVEN_DATABASE" ]] ; then if $terminal ; then echo "" echo -e "[ \033[37m\033[1mMySQL $mysql_version\033[m ]: Finished optimizing MySQL database '$GIVEN_DATABASE'." echo "" fi echo "" >> $log_file echo "[ MySQL $mysql_version ]: Finished optimizing MySQL database '$GIVEN_DATABASE'." >> $log_file echo "" >> $log_file else if $terminal ; then echo "" echo -e "[ \033[37m\033[1mMySQL $mysql_version\033[m ]: Finished optimizing MySQL databases at host $(hostname -f)." echo "" fi echo "" >> $log_file echo "[ MySQL $mysql_version ]: Finished optimizing MySQL databases at host $(hostname -f)." >> $log_file echo "" >> $log_file fi done if [[ ${#info_messages_arr[@]} -gt 0 ]]; then for msg in "${info_messages_arr[@]}" ; do if $terminal ; then info "$msg" fi echo "[ Info ]: $msg" >> $log_file done fi if [[ ${#error_messages_arr[@]} -gt 0 ]]; then for msg in "${error_messages_arr[@]}" ; do if $terminal ; then error "$msg" fi echo "[ Error ]: $msg" >> $log_file done fi if $terminal ; then info "See logfile '$log_file' for more details." fi clean_up 0