mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-07 12:58:45 +00:00
388 lines
14 KiB
Bash
388 lines
14 KiB
Bash
# This program is copyright 2011-2012 Percona Inc.
|
|
# Feedback and improvements are welcome.
|
|
#
|
|
# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
|
|
# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
|
# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify it under
|
|
# the terms of the GNU General Public License as published by the Free Software
|
|
# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
|
|
# systems, you can issue `man perlgpl' or `man perlartistic' to read these
|
|
# licenses.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along with
|
|
# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
|
# Place, Suite 330, Boston, MA 02111-1307 USA.
|
|
# ###########################################################################
|
|
# collect_mysql_info package
|
|
# ###########################################################################
|
|
|
|
# Package: collect_mysql_info
|
|
# collect collects mysql information.
|
|
|
|
# XXX
|
|
# THIS LIB REQUIRES log_warn_die.sh, summary_common.sh, and alt_cmds.sh!
|
|
# XXX
|
|
|
|
CMD_MYSQL="${CMD_MYSQL:-""}"
|
|
CMD_MYSQLDUMP="${CMD_MYSQLDUMP:-""}"
|
|
|
|
# Simply looks for instances of mysqld in the outof of ps.
|
|
collect_mysqld_instances () {
|
|
local variables_file="$1"
|
|
|
|
local pids="$(_pidof mysqld)"
|
|
|
|
if [ -n "$pids" ]; then
|
|
|
|
for pid in $pids; do
|
|
local nice="$( get_nice_of_pid $pid )"
|
|
local oom="$( get_oom_of_pid $pid )"
|
|
echo "internal::nice_of_$pid $nice" >> "$variables_file"
|
|
echo "internal::oom_of_$pid $oom" >> "$variables_file"
|
|
done
|
|
|
|
pids="$(echo $pids | sed -e 's/ /,/g')"
|
|
ps ww -p "$pids" 2>/dev/null
|
|
else
|
|
echo "mysqld doesn't appear to be running"
|
|
fi
|
|
|
|
}
|
|
|
|
# Tries to find the my.cnf file by examining 'ps' output.
|
|
# You have to specify the port for the instance you are
|
|
# interested in, in case there are multiple instances.
|
|
find_my_cnf_file() {
|
|
local file="$1"
|
|
local port="${2:-""}"
|
|
local pid_file="${3:-""}"
|
|
|
|
local cnf_file=""
|
|
|
|
if [ "$pid_file" ]; then
|
|
local pid=$(cat "$pid_file")
|
|
cnf_file="$(grep --max-count 1 -E "^\s*$pid\s+" "$file" \
|
|
| awk 'BEGIN{RS=" "; FS="=";} $1 ~ /--defaults-file/ { print $2; }')"
|
|
|
|
if [ -n "$cnf_file" ]; then
|
|
echo "$cnf_file"
|
|
return
|
|
fi
|
|
fi
|
|
|
|
if [ "$port" ]; then
|
|
# Find the cnf file for the specific port.
|
|
cnf_file="$(grep --max-count 1 "/mysqld.*--port=$port" "$file" \
|
|
| awk 'BEGIN{RS=" "; FS="=";} $1 ~ /--defaults-file/ { print $2; }')"
|
|
|
|
if [ -n "$cnf_file" ]; then
|
|
echo "$cnf_file"
|
|
return
|
|
fi
|
|
else
|
|
cnf_file="$(grep --max-count 1 '/mysqld' "$file" \
|
|
| awk 'BEGIN{RS=" "; FS="=";} $1 ~ /--defaults-file/ { print $2; }')"
|
|
fi
|
|
|
|
if [ -z "$cnf_file" ]; then
|
|
# Cannot autodetect config file, try common locations.
|
|
if [ -e "/etc/my.cnf" ]; then
|
|
cnf_file="/etc/my.cnf"
|
|
elif [ -e "/etc/mysql/my.cnf" ]; then
|
|
cnf_file="/etc/mysql/my.cnf"
|
|
elif [ -e "/var/db/mysql/my.cnf" ]; then
|
|
cnf_file="/var/db/mysql/my.cnf";
|
|
fi
|
|
fi
|
|
|
|
echo "$cnf_file"
|
|
}
|
|
|
|
collect_mysql_variables () {
|
|
$CMD_MYSQL $EXT_ARGV -ss -e 'SHOW /*!40100 GLOBAL*/ VARIABLES'
|
|
}
|
|
|
|
collect_mysql_status () {
|
|
$CMD_MYSQL $EXT_ARGV -ss -e 'SHOW /*!50000 GLOBAL*/ STATUS'
|
|
}
|
|
|
|
collect_mysql_databases () {
|
|
$CMD_MYSQL $EXT_ARGV -ss -e 'SHOW DATABASES' 2>/dev/null
|
|
}
|
|
|
|
collect_mysql_plugins () {
|
|
$CMD_MYSQL $EXT_ARGV -ss -e 'SHOW PLUGINS' 2>/dev/null
|
|
}
|
|
|
|
collect_mysql_replica_status () {
|
|
local replica_name="$1"
|
|
|
|
$CMD_MYSQL $EXT_ARGV -ssE -e "SHOW ${replica_name} STATUS" 2>/dev/null
|
|
}
|
|
|
|
collect_mysql_innodb_status () {
|
|
$CMD_MYSQL $EXT_ARGV -ssE -e 'SHOW /*!50000 ENGINE*/ INNODB STATUS' 2>/dev/null
|
|
}
|
|
|
|
collect_mysql_ndb_status () {
|
|
$CMD_MYSQL $EXT_ARGV -ssE -e 'show /*!50000 ENGINE*/ NDB STATUS' 2>/dev/null
|
|
}
|
|
|
|
collect_mysql_processlist () {
|
|
$CMD_MYSQL $EXT_ARGV -ssE -e 'SHOW FULL PROCESSLIST' 2>/dev/null
|
|
}
|
|
|
|
collect_mysql_users () {
|
|
# The where clause has been added to skip listing MySQL 8+ roles as users.
|
|
# ROLES are locked accounts, without passwords and expired.
|
|
$CMD_MYSQL $EXT_ARGV -ss -e "SELECT COUNT(*), SUM(user=''), SUM(password=''), SUM(password NOT LIKE '*%') FROM mysql.user" 2>/dev/null
|
|
if [ "$?" -ne 0 ]; then
|
|
$CMD_MYSQL $EXT_ARGV -ss -e "SELECT COUNT(*), SUM(user=''), SUM(authentication_string=''), SUM(authentication_string NOT LIKE '*%') FROM mysql.user WHERE account_locked <> 'Y' AND password_expired <> 'Y' AND authentication_string <> ''" 2>/dev/null
|
|
fi
|
|
}
|
|
|
|
collect_mysql_roles () {
|
|
QUERY="SELECT DISTINCT User 'Role Name', if(from_user is NULL,0, 1) Active FROM mysql.user LEFT JOIN mysql.role_edges ON from_user=user WHERE account_locked='Y' AND password_expired='Y' AND authentication_string=''\G"
|
|
$CMD_MYSQL $EXT_ARGV -ss -e "$QUERY" 2>/dev/null
|
|
}
|
|
|
|
collect_mysql_show_replica_hosts () {
|
|
local version="$1"
|
|
|
|
local replicas='replicas'
|
|
if [ "$version" '<' "8.1" ]; then
|
|
replicas='slave hosts';
|
|
fi
|
|
|
|
$CMD_MYSQL $EXT_ARGV -ssE -e "SHOW ${replicas}" 2>/dev/null
|
|
}
|
|
|
|
collect_source_logs_status () {
|
|
local source_logs_file="$1"
|
|
local source_status_file="$2"
|
|
local version="$3"
|
|
|
|
local source_log='binary'
|
|
local source_status='binary log'
|
|
if [ "$version" '<' "8.1" ]; then
|
|
source_log='master';
|
|
source_status='master';
|
|
fi
|
|
$CMD_MYSQL $EXT_ARGV -ss -e "SHOW ${source_log} LOGS" > "$source_logs_file" 2>/dev/null
|
|
$CMD_MYSQL $EXT_ARGV -ss -e "SHOW ${source_status} STATUS" > "$source_status_file" 2>/dev/null
|
|
}
|
|
|
|
# Somewhat different from the others, this one joins the status we got earlier
|
|
collect_mysql_deferred_status () {
|
|
local status_file="$1"
|
|
collect_mysql_status > "$PT_TMPDIR/defer_gatherer"
|
|
join "$status_file" "$PT_TMPDIR/defer_gatherer"
|
|
}
|
|
|
|
collect_internal_vars () {
|
|
local mysqld_executables="${1:-""}"
|
|
|
|
local FNV_64=""
|
|
if $CMD_MYSQL $EXT_ARGV -e "SELECT FNV_64('a')" >/dev/null 2>&1; then
|
|
FNV_64="Enabled";
|
|
else
|
|
FNV_64="Unknown";
|
|
fi
|
|
|
|
local now="$($CMD_MYSQL $EXT_ARGV -ss -e 'SELECT NOW()')"
|
|
local user="$($CMD_MYSQL $EXT_ARGV -ss -e 'SELECT CURRENT_USER()')"
|
|
local trigger_count=$($CMD_MYSQL $EXT_ARGV -ss -e "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TRIGGERS" 2>/dev/null)
|
|
|
|
echo "pt-summary-internal-mysql_executable $CMD_MYSQL"
|
|
echo "pt-summary-internal-now $now"
|
|
echo "pt-summary-internal-user $user"
|
|
echo "pt-summary-internal-FNV_64 $FNV_64"
|
|
echo "pt-summary-internal-trigger_count $trigger_count"
|
|
|
|
if [ -e "$mysqld_executables" ]; then
|
|
local i=1
|
|
while read executable; do
|
|
echo "pt-summary-internal-mysqld_executable_${i} $(has_symbols "$executable")"
|
|
i=$(($i + 1))
|
|
done < "$mysqld_executables"
|
|
fi
|
|
|
|
# jemalloc info
|
|
local JEMALLOC_STATUS=''
|
|
local GENERAL_JEMALLOC_STATUS=0
|
|
local JEMALLOC_LOCATION=''
|
|
|
|
for pid in $(pidof mysqld); do
|
|
grep -qc jemalloc /proc/${pid}/environ || ldd $(which mysqld) 2>/dev/null | grep -qc jemalloc
|
|
jemalloc_status=$?
|
|
if [ $jemalloc_status = 1 ]; then
|
|
echo "pt-summary-internal-jemalloc_enabled_for_pid_${pid} 0"
|
|
else
|
|
echo "pt-summary-internal-jemalloc_enabled_for_pid_${pid} 1"
|
|
GENERAL_JEMALLOC_STATUS=1
|
|
fi
|
|
done
|
|
|
|
if [ $GENERAL_JEMALLOC_STATUS -eq 1 ]; then
|
|
JEMALLOC_LOCATION=$(find /usr/lib64/ /usr/lib/x86_64-linux-gnu /usr/lib -name "libjemalloc.*" 2>/dev/null | head -n 1)
|
|
echo "pt-summary-internal-jemalloc_location ${JEMALLOC_LOCATION}"
|
|
fi
|
|
}
|
|
|
|
collect_keyring_plugins() {
|
|
$CMD_MYSQL $EXT_ARGV --table -ss -e "SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE 'keyring%';"
|
|
}
|
|
|
|
collect_encrypted_tables() {
|
|
$CMD_MYSQL $EXT_ARGV --table -ss -e "SELECT TABLE_SCHEMA, TABLE_NAME, CREATE_OPTIONS FROM INFORMATION_SCHEMA.TABLES WHERE CREATE_OPTIONS LIKE '%ENCRYPTION=_Y_%';"
|
|
}
|
|
|
|
collect_encrypted_tablespaces() {
|
|
local version="$1"
|
|
# I_S.INNODB_[SYS_]TABLESPACES has a "flag" field. Encrypted tablespace has bit 14 set. You can check it with "flag & 8192".
|
|
# And seems like MySQL is capable of bitwise operations. https://dev.mysql.com/doc/refman/5.7/en/bit-functions.html
|
|
if [ "$version" '<' "8.0" ]; then
|
|
$CMD_MYSQL $EXT_ARGV --table -ss -e "SELECT SPACE, NAME, SPACE_TYPE from INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES where FLAG&8192 = 8192;"
|
|
else
|
|
$CMD_MYSQL $EXT_ARGV --table -ss -e "SELECT SPACE, NAME, SPACE_TYPE from INFORMATION_SCHEMA.INNODB_TABLESPACES where FLAG&8192 = 8192;"
|
|
fi
|
|
}
|
|
|
|
# Uses mysqldump and dumps the results to FILE.
|
|
# args and dbtodump are passed to mysqldump.
|
|
get_mysqldump_for () {
|
|
local args="$1"
|
|
local dbtodump="${2:-"--all-databases"}"
|
|
|
|
$CMD_MYSQLDUMP $EXT_ARGV --no-data --skip-comments \
|
|
--skip-add-locks --skip-add-drop-table --compact \
|
|
--skip-lock-all-tables --skip-lock-tables --skip-set-charset \
|
|
${args} --databases $(local IFS=,; echo ${dbtodump})
|
|
}
|
|
|
|
# Returns a string with arguments to pass to mysqldump.
|
|
# Takes one argument, which should be a
|
|
get_mysqldump_args () {
|
|
local file="$1"
|
|
local trg_arg=""
|
|
|
|
# If mysqldump supports triggers, then add options for routines.
|
|
if $CMD_MYSQLDUMP --help --verbose 2>&1 | grep triggers >/dev/null; then
|
|
# "mysqldump supports triggers"
|
|
trg_arg="--routines"
|
|
fi
|
|
|
|
if [ "${trg_arg}" ]; then
|
|
# Find out if there are any triggers. If there are none, we will skip
|
|
# that option to mysqldump, because when mysqldump checks for them, it
|
|
# can take a long time, one table at a time.
|
|
local triggers="--skip-triggers"
|
|
local trg=$(get_var "pt-summary-internal-trigger_count" "$file" )
|
|
if [ -n "${trg}" ] && [ "${trg}" -gt 0 ]; then
|
|
triggers="--triggers"
|
|
fi
|
|
trg_arg="${trg_arg} ${triggers}";
|
|
fi
|
|
echo "${trg_arg}"
|
|
}
|
|
|
|
collect_mysqld_executables () {
|
|
local mysqld_instances="$1"
|
|
|
|
local ps_opt="cmd="
|
|
if [ "$(uname -s)" = "Darwin" ]; then
|
|
ps_opt="command="
|
|
fi
|
|
|
|
for pid in $( grep '/mysqld' "$mysqld_instances" | awk '/^.*[0-9]/{print $1}' ); do
|
|
ps -o $ps_opt -p $pid | sed -e 's/^\(.*mysqld\) .*/\1/'
|
|
done | sort -u
|
|
}
|
|
|
|
collect_mysql_info () {
|
|
local dir="$1"
|
|
|
|
collect_mysql_variables > "$dir/mysql-variables"
|
|
collect_mysql_status > "$dir/mysql-status"
|
|
collect_mysql_databases > "$dir/mysql-databases"
|
|
collect_mysql_plugins > "$dir/mysql-plugins"
|
|
collect_mysql_innodb_status > "$dir/innodb-status"
|
|
collect_mysql_ndb_status > "$dir/ndb-status"
|
|
collect_mysql_processlist > "$dir/mysql-processlist"
|
|
collect_mysql_users > "$dir/mysql-users"
|
|
collect_mysql_roles > "$dir/mysql-roles"
|
|
collect_keyring_plugins > "$dir/keyring-plugins"
|
|
|
|
local mysql_version="$(get_var version "$dir/mysql-variables")"
|
|
|
|
local replicas='replicas'
|
|
local replica_name='replica'
|
|
if [ "$mysql_version" '<' "8.1" ]; then
|
|
replicas='slave-hosts'
|
|
replica_name='slave'
|
|
fi
|
|
|
|
collect_mysql_replica_status $replica_name > "$dir/mysql-${replica_name}"
|
|
collect_mysqld_instances "$dir/mysql-variables" > "$dir/mysqld-instances"
|
|
collect_mysqld_executables "$dir/mysqld-instances" > "$dir/mysqld-executables"
|
|
collect_mysql_show_replica_hosts "$mysql_version" > "$dir/mysql-${replicas}"
|
|
|
|
local binlog="$(get_var log_bin "$dir/mysql-variables")"
|
|
if [ "${binlog}" ]; then
|
|
# "Got a binlog, going to get BINARY LOGS and BINARY LOG STATUS"
|
|
local source_logs_file="mysql-binary-logs"
|
|
local source_status_file="mysql-binary-log-status"
|
|
|
|
if [ "$mysql_version" '<' "8.1" ]; then
|
|
source_logs_file='mysql-master-logs'
|
|
source_status_file='mysql-master-status'
|
|
fi
|
|
collect_source_logs_status "$dir/$source_logs_file" "$dir/$source_status_file" ${mysql_version}
|
|
fi
|
|
|
|
local uptime="$(get_var Uptime "$dir/mysql-status")"
|
|
local current_time="$($CMD_MYSQL $EXT_ARGV -ss -e \
|
|
"SELECT LEFT(NOW() - INTERVAL ${uptime} SECOND, 16)")"
|
|
|
|
local pid_file="$(get_var "pid_file" "$dir/mysql-variables")"
|
|
local pid_file_exists=""
|
|
[ -e "${pid_file}" ] && pid_file_exists=1
|
|
echo "pt-summary-internal-pid_file_exists $pid_file_exists" >> "$dir/mysql-variables"
|
|
|
|
local port="$(get_var port "$dir/mysql-variables")"
|
|
local cnf_file="$(find_my_cnf_file "$dir/mysqld-instances" ${port} ${pid_file})"
|
|
|
|
[ -e "$cnf_file" ] && cat "$cnf_file" > "$dir/mysql-config-file"
|
|
|
|
# TODO: Do these require a file of their own?
|
|
echo "pt-summary-internal-current_time $current_time" >> "$dir/mysql-variables"
|
|
echo "pt-summary-internal-Config_File_path $cnf_file" >> "$dir/mysql-variables"
|
|
collect_internal_vars "$dir/mysqld-executables" >> "$dir/mysql-variables"
|
|
|
|
# mysqldump schemas
|
|
if [ "$OPT_DATABASES" -o "$OPT_ALL_DATABASES" ]; then
|
|
local trg_arg="$(get_mysqldump_args "$dir/mysql-variables")"
|
|
local dbs="${OPT_DATABASES:-""}"
|
|
get_mysqldump_for "${trg_arg}" "$dbs" > "$dir/mysqldump"
|
|
fi
|
|
|
|
# encrypted tables and tablespaces
|
|
if [ "${OPT_LIST_ENCRYPTED_TABLES}" = 'yes' ]; then
|
|
collect_encrypted_tables > "$dir/encrypted-tables"
|
|
collect_encrypted_tablespaces ${mysql_version} > "$dir/encrypted-tablespaces"
|
|
fi
|
|
|
|
# TODO: gather this data in the same format as normal: TS line, stats
|
|
(
|
|
sleep $OPT_SLEEP
|
|
collect_mysql_deferred_status "$dir/mysql-status" > "$dir/mysql-status-defer"
|
|
) &
|
|
_d "Forked child is $!"
|
|
}
|
|
|
|
# ###########################################################################
|
|
# End collect_mysql_info package
|
|
# ###########################################################################
|