#!/usr/bin/env bash # Phil Dufault (2009) # bumped to v1 (2011) # phil@dufault.info VERSION="1.0.2" mysqlCmd="mysql" echo "MySQL fragmentation finder (and fixer) v$VERSION" echo "Written by Phil Dufault (phil@dufault.info, http://www.dufault.info)" echo "" showHelp() { echo -e "\tThis script only repairs MyISAM and InnoDB tables" echo -e "\t--help -h\t\tthis menu" echo -e "\t--user username\t\tspecify mysql username to use, the script will prompt for a password during runtime, unless you supply a password" echo -e "\t--password \"yourpass\"" echo -e "\t--host hostname\t\tspecify mysql hostname to use, be it local (default) or remote" echo -e "\t--mysql command\t\tspecify mysql command name, default is mysql" echo -e "\t--database\t\tuse specified database as target\n\t\t\t\tif this option is not used, all databases are targeted" echo -e "\t--log\t\t\tset a custom log. Default value is $PWD/mysqlfragfinder.log" echo -e "\t--check\t\t\tonly shows fragmented tables, but do not optimize them" echo -e "\t--detail\t\tadditionally display fragmented tables" } # Parse arguments while [[ $1 == -* ]]; do case "$1" in --user) mysqlUser="$2"; shift 2;; --password) mysqlPass="$2"; shift 2;; --host) mysqlHost="$2"; shift 2;; --mysql) mysqlCmd="$2"; shift 2;; --database) mysqlDb="$2"; shift 2;; --log) log="$2"; shift 2;; --check) mysqlCheck="1"; shift;; --detail) mysqlDetail="1"; shift;; --help|-h) showHelp; exit 0;; --*) shift; break;; esac done # Set localhost if no host is set anywhere else if [[ ! $log ]]; then log="$PWD/mysqlfragfinder.log" fi # prevent overwriting the commandline args with the ones in .my.cnf, and check that .my.cnf exists if [[ ! $mysqlUser && -f "$HOME/.my.cnf" ]]; then if grep "user=" "$HOME/.my.cnf" >/dev/null 2>&1; then if grep "password=" "$HOME/.my.cnf" >/dev/null 2>&1; then mysqlUser=$(grep "user=" "$HOME/.my.cnf" | awk -F= 'NR==1{print $NF}'); mysqlPass=$(grep "password=" "$HOME/.my.cnf" | awk -F= 'NR==1{print $NF}'); if grep "host=" "$HOME/.my.cnf" >/dev/null 2>&1; then mysqlHost=$(grep "host=" "$HOME/.my.cnf" | awk -F= 'NR==1{print $NF}'); fi else echo "Found no pass line in your .my.cnf,, fix this or specify with --password" fi else echo "Found no user line in your .my.cnf, fix this or specify with --user" exit 1; fi fi mysqlCmd="$mysqlCmd -u$mysqlUser -p$mysqlPass" # If set, add -h parameter to mysqlHost if [[ $mysqlHost ]]; then mysqlCmd=$mysqlCmd" -h$mysqlHost" fi # Error out if no auth details are found for the user if [[ ! $mysqlUser ]]; then echo "Authentication information not found as arguments, nor in $HOME/.my.cnf" echo showHelp exit 1 fi if [[ ! $mysqlPass ]]; then echo -n "Enter your MySQL password: " read -s mysqlPass fi # Test connecting to the database: $mysqlCmd --skip-column-names --batch -e "show status" >/dev/null 2>"$log" if [[ $? -gt 0 ]]; then echo "An error occured, check $log for more information."; exit 1; fi # Retrieve the listing of databases: if [[ ! $mysqlDb ]]; then databases=($($mysqlCmd --skip-column-names --batch -e "show databases;" 2>"$log")); else databases=($mysqlDb); fi if [[ $? -gt 0 ]]; then echo "An error occured, check $log for more information." exit 1; fi echo -e "Found ${#databases[@]} databases"; for i in ${databases[@]}; do if [[ $i != 'information_schema' ]]; then # Get a list of all of the tables, grep for MyISAM or InnoDB, and then sort out the fragmented tables with awk fragmented=($($mysqlCmd --skip-column-names --batch -e "SHOW TABLE STATUS FROM \`$i\`;" 2>"$log" | awk '{print $1,$2,$10}' | egrep "MyISAM|InnoDB|Aria" | awk '$3 > 0' | awk '{print $1}')); if [[ $? -gt 0 ]]; then echo "An error occured, check $log for more information." exit 1; fi tput sc echo "" echo -n "Checking $i ... "; if [[ ${#fragmented[@]} -gt 0 ]]; then if [[ ${#fragmented[@]} -gt 0 ]]; then if [[ ${#fragmented[@]} -gt 1 ]]; then echo "found ${#fragmented[@]} fragmented tables." else echo "found ${#fragmented[@]} fragmented table." fi if [[ $mysqlDetail ]]; then for table in ${fragmented[@]}; do echo -ne "\t$table\n"; done fi fi # Only optimize tables if check option is disabled if [[ ! $mysqlCheck ]]; then for table in ${fragmented[@]}; do let fraggedTables=$fraggedTables+1; echo -ne "\tOptimizing $table ... "; $mysqlCmd -D "$i" --skip-column-names --batch -e "optimize table \`$table\`" 2>"$log" >/dev/null if [[ $? -gt 0 ]]; then echo "An error occured, check $log for more information." exit 1; fi echo done done fi else tput rc tput el fi unset fragmented fi done echo "" # Footer message if [[ $mysqlCheck ]]; then echo "Check option was enabled, so no optimizing was done."; elif [[ ! $fraggedTables -gt 0 ]]; then echo "No tables were fragmented, so no optimizing was done."; elif [[ $fraggedTables -gt 1 ]]; then echo "$fraggedTables tables were fragmented, and were optimized."; else echo "$fraggedTables table was fragmented, and was optimized."; fi if [[ ! -s $log ]]; then rm -f "$log" fi unset fraggedTables