-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathoptimize-tables.sh
More file actions
325 lines (275 loc) · 10.4 KB
/
optimize-tables.sh
File metadata and controls
325 lines (275 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
#!/bin/bash
###############################################################################
# Table Optimization Utility (optimize-tables.sh)
#
# Part of: MySQL Migration Tools
# Copyright (c) 2012-2026 utilmind
# https://github.com/utilmind/MySQL-migration-tools
#
# Description:
# Runs safe optimization / analyze operations for MySQL/MariaDB tables.
# - Reads DB connection settings from ".credentials.sh" or
# ".<configuration-name>.credentials.sh" (same directory as the script).
# - Supports explicit table list (2nd parameter) or prefix-based selection
# via dbTablePrefix array from credentials.
# - If dbTablePrefix is not defined OR defined but empty, all tables in the
# database are used (except *_backup_*).
# - MyISAM tables -> mysqlcheck --optimize
# - InnoDB tables -> mysqlcheck --analyze
#
# Usage:
# ./optimize-tables.sh [configuration-name] ["table1 table2 ..."]
#
# configuration-name (optional)
# Used to choose ".<configuration-name>.credentials.sh".
# If omitted or empty, ".credentials.sh" is used.
#
# explicit tables list (optional, second parameter)
# Quoted, space-separated list of table names.
# Example:
# ./optimize-tables.sh my-config "table1 table2 table_user logs"
#
# Behavior:
# - If explicit table list is provided:
# Only these tables are checked (engines are detected via
# INFORMATION_SCHEMA.TABLES).
# - Else, if dbTablePrefix is defined and non-empty:
# Only tables whose names start with any of the prefixes are used
# (excluding '*_backup_*').
# - Else (no prefixes):
# All tables in the database are used (excluding '*_backup_*').
#
# NOTE:
# - This script is intended to be called from db-dump.sh, but it can also
# be used standalone.
#
# License: MIT
###############################################################################
set -euo pipefail
# CONFIGURATION
# Optionally specify table prefixes to process for optimization/analyze.
# You may define dbTablePrefix here, but values from the `.[configuration.]credentials.sh` file take priority, if specified there.
#dbTablePrefix=('table_prefix1_' 'table_prefix2_' 'bot_' 'email_' 'user_' 'order_')
print_help() {
scriptName=$(basename "$0")
cat << EOF
Usage: $scriptName [configuration-name] ["table1 table2 table3"]
configuration-name (Optional)
Used to locate credentials file with name ".configuration-name.credentials.sh"
placed in the same directory as this script.
If not provided, then ".credentials.sh" will be used.
explicit tables list (Optional, second parameter)
Quoted space-separated list of tables to process.
If provided, dbTablePrefix is ignored and only these tables are optimized/analyzed.
If no explicit table list is given:
- and dbTablePrefix is defined (non-empty), only tables with these prefixes
are processed (excluding *_backup_*),
- otherwise, ALL tables in the database are processed (excluding *_backup_*).
Examples:
$scriptName
# use .credentials.sh, optimize/analyze tables (based on dbTablePrefix, if specified)
$scriptName my-config
# use .my-config.credentials.sh, optimize/analyze tables (based on dbTablePrefix, if specified)
$scriptName my-config "table1 table2 stats"
# use .my-config.credentials.sh, optimize/analyze only the listed tables
EOF
}
# ANSI colors (disabled if NO_COLOR is set or output is not a TTY)
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
COLOR_INFO="\033[1;34m"
COLOR_WARN="\033[1;33m"
COLOR_ERROR="\033[1;31m"
COLOR_OK="\033[1;32m"
COLOR_RESET="\033[0m"
else
COLOR_INFO=""
COLOR_WARN=""
COLOR_ERROR=""
COLOR_OK=""
COLOR_RESET=""
fi
log_info() { printf "%b[INFO]%b %s\n" "$COLOR_INFO" "$COLOR_RESET" "$*"; }
log_warn() { printf "%b[WARN]%b %s\n" "$COLOR_WARN" "$COLOR_RESET" "$*"; }
log_error() { printf "%b[ERROR]%b %s\n" "$COLOR_ERROR" "$COLOR_RESET" "$*"; }
log_ok() { printf "%b[OK]%b %s\n" "$COLOR_OK" "$COLOR_RESET" "$*"; }
# ---------------- PARAMETER PARSING ----------------
while [[ "${1-}" == -* ]] ; do
case "$1" in
-?|-h|-help|--help)
print_help
exit 0
;;
--)
shift
break
;;
*)
log_error "Invalid parameter: '$1'"
exit 1
;;
esac
done
dbConfigName="${1:-}" # configuration-name (may be empty)
tablesListRaw="${2:-}" # optional explicit table list (quoted)
# ---------------- BASIC PATHS ----------------
thisScript=$(readlink -f "$0")
scriptDir=$(dirname "$thisScript")
# Temporary directory for helper files.
tempDir="$scriptDir/_temp"
mkdir -p "$tempDir"
myisamTablesFilename="$tempDir/_${dbConfigName}-optimize_tables.txt"
innoDBTablesFilename="$tempDir/_${dbConfigName}-analyze_tables.txt"
# ---------------- LOAD CREDENTIALS ----------------
if [ -n "$dbConfigName" ]; then
credentialsFile="$scriptDir/.${dbConfigName}.credentials.sh"
else
credentialsFile="$scriptDir/.credentials.sh"
fi
if [ ! -r "$credentialsFile" ]; then
log_error "Credentials file '$credentialsFile' not found or not readable."
echo "Please create it with DB connection settings:"
echo " dbHost, dbPort, dbName, dbUser, [dbPass], [dbTablePrefix]"
exit 1
fi
# Expected variables:
# dbHost, dbPort, dbName, dbUser, optional dbPass, optional dbTablePrefix (array)
. "$credentialsFile"
# Apply defaults for host and port if not provided in credentials
dbHost="${dbHost:-localhost}"
dbPort="${dbPort:-3306}"
# If dbName is not defined, try to use configuration name as DB name.
if [ -z "${dbName:-}" ]; then
if [ -n "$dbConfigName" ]; then
dbName="$dbConfigName"
else
log_error "'dbName' is not defined in credentials file '$credentialsFile' and no configuration-name argument was provided."
exit 1
fi
fi
# dbUser must be defined in credentials
if [ -z "${dbUser:-}" ]; then
log_error "'dbUser' is not defined in credentials file '$credentialsFile'."
exit 1
fi
# Ask for password if missing
if [ -z "${dbPass:-}" ]; then
read -s -p "Enter password for MySQL user '$dbUser' (database '$dbName'): " dbPass
echo
fi
mysqlConnOpts=(
--host="$dbHost"
--port="$dbPort"
--user="$dbUser"
--password="$dbPass"
)
# Clean previous lists
: > "$myisamTablesFilename"
: > "$innoDBTablesFilename"
# ---------------- BUILD TABLE LISTS ----------------
if [ -n "$tablesListRaw" ]; then
# ---------- EXPLICIT TABLE LIST MODE ----------
log_info "Using explicit table list for optimization/analyze."
declare -a explicitTables=()
read -r -a explicitTables <<< "$tablesListRaw"
if [ ${#explicitTables[@]} -eq 0 ]; then
log_error "Explicit table list is empty after parsing."
exit 1
fi
# Build IN clause for INFORMATION_SCHEMA query
tablesInClause=""
for t in "${explicitTables[@]}"; do
esc=${t//\'/\'\'} # escape quotes
if [ -z "$tablesInClause" ]; then
tablesInClause="'$esc'"
else
tablesInClause="$tablesInClause, '$esc'"
fi
done
log_info "Detecting engines for explicitly listed tables..."
while IFS=$'\t' read -r tbl engine; do
case "$engine" in
MyISAM)
echo "$tbl" >> "$myisamTablesFilename"
;;
InnoDB)
echo "$tbl" >> "$innoDBTablesFilename"
;;
*)
log_info "Skipping table '$tbl' with unsupported engine '$engine'."
;;
esac
done < <(
# BASE TABLE's only, no VIEW's.
mysql "${mysqlConnOpts[@]}" -N \
-e "SELECT TABLE_NAME, ENGINE
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = '$dbName'
AND TABLE_TYPE = 'BASE TABLE'
AND TABLE_NAME IN (${tablesInClause})
ORDER BY TABLE_NAME;"
)
else
# ---------- PREFIX-BASED OR FULL-DB MODE ----------
# BASE TABLE's only, no VIEW's.
where_clause="TABLE_SCHEMA = '$dbName' AND TABLE_TYPE = 'BASE TABLE'"
# dbTablePrefix may be undefined or an empty array.
if [ -n "${dbTablePrefix+x}" ] && [ "${#dbTablePrefix[@]}" -gt 0 ]; then
log_info "Optimizing only tables matching prefixes: ${#dbTablePrefix[@]}."
like_clause=""
for p in "${dbTablePrefix[@]}"; do
esc=${p//\'/\'\'} # escape quotes
esc=${esc//_/\\_} # escape '_' for LIKE
if [ -z "$like_clause" ]; then
like_clause="(TABLE_NAME LIKE '${esc}%')"
else
like_clause="$like_clause OR (TABLE_NAME LIKE '${esc}%')"
fi
done
where_clause="$where_clause AND ($like_clause)"
else
# log_info "dbTablePrefix is not defined or empty; using ALL tables in '$dbName' (excluding *_backup_*)."
log_info "Using ALL tables in '$dbName' (excluding *_backup_*)."
fi
# Exclude backup tables always
where_clause="$where_clause AND TABLE_NAME NOT LIKE '%_backup_%'"
log_info "Detecting engines for selected tables from INFORMATION_SCHEMA..."
while IFS=$'\t' read -r tbl engine; do
case "$engine" in
MyISAM)
echo "$tbl" >> "$myisamTablesFilename"
;;
InnoDB)
echo "$tbl" >> "$innoDBTablesFilename"
;;
*)
log_info "Skipping table '$tbl' with unsupported engine '$engine'."
;;
esac
done < <(
mysql "${mysqlConnOpts[@]}" -N \
-e "SELECT TABLE_NAME, ENGINE
FROM INFORMATION_SCHEMA.TABLES
WHERE ${where_clause}
ORDER BY TABLE_NAME;"
)
fi
# ---------------- RUN MYSQLCHECK ----------------
if [ -s "$myisamTablesFilename" ]; then
log_info "Optimizing MyISAM tables via mysqlcheck --optimize ..."
mysqlcheck "${mysqlConnOpts[@]}" \
--optimize \
"$dbName" \
$(cat "$myisamTablesFilename")
else
log_info "No MyISAM to optimize."
fi
if [ -s "$innoDBTablesFilename" ]; then
log_info "Analyzing InnoDB tables via mysqlcheck --analyze ..."
mysqlcheck "${mysqlConnOpts[@]}" \
--analyze \
"$dbName" \
$(cat "$innoDBTablesFilename")
else
log_info "No InnoDB tables to analyze."
fi
log_ok "Table optimization/analyze completed."