mysqldump -u zom -p'XXXXXXXX' zzm > /volume1/NAS/BACKUP/backup_$(date +%Y%m%d%H%M).sql

這是我的排程備份DB指令

我還想再新增一個每天 優化資料表的指令

 

 


 

 

這是一個很棒的備份習慣!在 Linux 或 NAS 環境中,透過指令自動化維護資料庫能省下不少心力。

要「優化」MySQL 資料表,最標準的指令是使用 mysqlcheck。這個工具可以針對資料庫進行分析(Analyze)、檢查(Check)以及最重要的優化(Optimize)。

推薦的優化指令
你可以將以下指令加入你的排程中:

 

mysqlcheck -u zom -p'XXXXXXXX' --optimize zzm


指令細節解析
--optimize: 這是核心參數。它會回收未使用的空間並整理資料檔的碎片(重組索引)。這對於頻繁刪除或更新資料的資料表特別有效。

 

效能注意: 執行 OPTIMIZE TABLE 時,MySQL 通常會鎖定資料表。如果你的資料表非常大(數 GB 以上),執行期間可能會導致網站或服務暫時無法寫入。建議排程在凌晨等離峰時段。

自動修復: 如果你希望更全面一點,可以使用 --auto-repair,它會在發現資料表損壞時嘗試自動修復: mysqlcheck -u [使用者] -p'[密碼]' --auto-repair --optimize [資料庫名]

如何整合進原本的排程?
如果你希望備份完後緊接著進行優化,可以利用 && 將兩個指令串接。這樣只有在備份成功的情況下,才會執行優化:

 

mysqldump -u zom -p'XXXXXXXX' zzm > /volume1/NAS/BACKUP/backup_$(date +%Y%m%d%H%M).sql && mysqlcheck -u zom -p'XXXXXXXX' --optimize zzm



小撇步:安全性建議
你的密碼包含特殊字元(如 %, [),在 Bash 環境下有時會引起解析問題。雖然你已經用了單引號包裹,但長期來說,建議建立一個 .my.cnf 設定檔來存放帳密,這樣指令列就不會出現明文密碼,也更安全。

 

需要我幫你寫一個包含自動刪除舊備份(例如只保留 7 天)的完整 Shell Script 嗎?

 

 


 

錯誤:

/bin/bash: mysqlcheck: command not found

 

 


 

看來你的系統環境(可能是 Synology NAS 或特定的 Docker 環境)路徑中沒有直接包含 mysqlcheck。

在很多 NAS 環境中,MariaDB/MySQL 的工具通常放在 /usr/local/mariadb10/bin/ 或 /usr/bin/ 下。既然你的 mysqldump 可以跑,我們可以直接用 mysql 指令來執行 SQL 語法達成同樣的效果。

請試試看改用這個指令:

方案 A:使用 mysql 指令執行(最通用)
這會直接對該資料庫內的所有資料表下達 OPTIMIZE 指令:

 

mysql -u zom -p'XXXXXXXX' -e "SET @tables = NULL; SELECT GROUP_CONCAT('\`', table_name, '\`') INTO @tables FROM information_schema.tables WHERE table_schema = 'zzm'; SET @s = CONCAT('OPTIMIZE TABLE ', @tables); PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;"



方案 B:找出 mysqlcheck 的完整路徑
如果你偏好原生的 mysqlcheck 工具,你可以先用以下指令找出它的藏身之處:

 

執行 which mysqldump

假設回傳:/usr/local/mariadb10/bin/mysqldump

那麼你的 mysqlcheck 通常就在旁邊:/usr/local/mariadb10/bin/mysqlcheck

找到路徑後,指令改成這樣(以 Synology 為例):

 

/usr/local/mariadb10/bin/mysqlcheck -u zom -p'XXXXXXXX' --optimize zzm


為什麼會出現 command not found?
環境變數問題:系統的 $PATH 沒有包含資料庫工具的資料夾。

 

套件不完整:有些精簡版環境只安裝了 mysqldump 跟 mysql 客戶端,沒附帶 mysqlcheck。

建議嘗試方案 A,因為只要 mysqldump 能跑,方案 A 的 mysql 指令幾乎 100% 也能跑。

需要我幫你把這個優化指令與你原本的備份指令整合成一個腳本檔案,並加上「執行成功與否」的紀錄嗎?

 

 


 

錯誤:

--------------

PREPARE stmt FROM @s

--------------

ERROR 1046 (3D000) at line 1: No database selected

 

 


 

 

這是一個常見的錯誤!原因在於 mysql 指令執行時,雖然你進入了資料庫,但後續的 PREPARE 語句需要明確指定一個「預設資料庫」,否則它會不知道在哪個空間執行指令。

我們可以透過在指令中加入 database_name 參數,或是在 SQL 語法最前面加上 USE 來修正。

修正後的指令
請嘗試以下版本(我在密碼後方直接加上了資料庫名稱 zzm):

 

mysql -u zom -p'XXXXXXXX' zzm -e "SET @tables = NULL; SELECT GROUP_CONCAT('\`', table_name, '\`') INTO @tables FROM information_schema.tables WHERE table_schema = 'zzm'; SET @s = CONCAT('OPTIMIZE TABLE ', @tables); PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;"



為什麼會報錯?(小知識)
ERROR 1046 (3D000):代表「沒有選擇資料庫」。

 

當你執行 SELECT ... FROM information_schema.tables 時,系統知道去哪裡找資料表的清單。

但當你執行 PREPARE stmt FROM @s(也就是執行 OPTIMIZE TABLE ...)時,MySQL 需要確認這些表是在哪個資料庫環境下操作,這時如果你沒先 USE 某個資料庫,它就會報錯。

如果上述指令太長,還有一個「簡單暴力」法
如果你不想寫複雜的 SQL,也可以利用 Linux 的 xargs 搭配 mysql 達成同樣效果:

 

mysql -u zom -p'XXXXXXXX' -N -s -e "SHOW TABLES" zzm | xargs -I {} mysql -u zom -p'XXXXXXXX' zzm -e "OPTIMIZE TABLE \`{}\`"


這個做法是先列出所有資料表,再一個一個餵給 OPTIMIZE 指令。

 

建議先用第一個修正版的指令試跑看看,成功的話直接放進排程即可!需要我幫你把這串指令包進原本的備份 crontab 嗎?

 

 


 

執行結果:

任務:mysql_optimize

開始時間:Fri, 23 Jan 2026 09:36:05 +0800

停止時間:Fri, 23 Jan 2026 09:37:31 +0800

目前狀態:0 (正常)

標準輸出 / 錯誤:

Table Op Msg_type Msg_text

zzm.zoe_assets optimize status Table is already up to date

zzm.zoe_content optimize status OK

 


 


太棒了!從這個輸出結果來看,你的優化指令已經成功執行。

看到 OK 或是 Table is already up to date 都代表該資料表已經處理完畢。針對輸出的內容,有幾個點稍微說明一下,讓你更安心:

輸出結果解讀
OK: 代表 MySQL 實際上重建了資料表(多半是因為這些表常有資料變動),釋放了零散的空間。

Table is already up to date: 代表該資料表目前非常健康,沒有空間碎片,所以 MySQL 覺得不需要浪費資源去重組它。

The storage engine for the table doesn't support optimize:

這出現在 zoe_finder_tokens 等表。這是因為這些表可能使用的是 MEMORY 引擎或是特定的非事務型引擎,本質上不支持 OPTIMIZE。

結論: 這不是報錯,可以忽略它,對系統沒有負面影響。

下一步:自動清理老舊備份(選配)
既然你的備份檔名帶有日期時間(backup_$(date +%Y%m%d%H%M).sql),日積月累會佔用不少 NAS 空間。

如果你想在每次優化完後,自動刪除超過 30 天的舊備份,你可以在排程指令最後面再加上這串:

 

&& find /volume1/NAS/BACKUP/ -name "backup_*.sql" -mtime +30 -delete



最終完美的排程指令建議
將備份、優化、清理結合在一起:

 

 

mysqldump -u zom -p'XXXXXXXX' zzm > /volume1/NAS/BACKUP/backup_$(date +%Y%m%d%H%M).sql && mysql -u zom -p'XXXXXXXX' zzm -e "SET @tables = NULL; SELECT GROUP_CONCAT('\`', table_name, '\`') INTO @tables FROM information_schema.tables WHERE table_schema = 'zzm'; SET @s = CONCAT('OPTIMIZE TABLE ', @tables); PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;" && find /volume1/NAS/BACKUP/ -name "backup_*.sql" -mtime +30 -delete



目前資料庫運作看起來非常完美!還有其他關於這台 NAS 資料庫維護的問題想調整嗎?

 

 


 

後記:

 

使用 mysql -u zom -p'XXXXXXXX' zzm -e "SET @tables = NULL; SELECT GROUP_CONCAT('\`', table_name, '\`') INTO @tables FROM information_schema.tables WHERE table_schema = 'zzm'; SET @s = CONCAT('OPTIMIZE TABLE ', @tables); PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;"

這個優化DB指令實測OK!