報錯 ERROR '...' not found (errno: 23) 或 Can't open file: ... (errno: 24),本質是 **MySQL 進程打開文件描述符不足**。本文從內核限制、MySQL 參數、架構優化三個維度給出完整解決路徑。
錯誤碼含義
$ perror 23
Error code 23: File table overflow
$ perror 24
Error code 24: Too many open files
$ perror 11
Error code 11: Resource temporarily unavailable
根因分析
- MySQL 每張表打開需消耗 **3 個文件描述符**(.frm、.MYD、.MYI 或 .ibd)。
- 高併發下,
table_open_cache × 3 + max_connections × (線程棧 + 臨時文件) 易突破系統限制。
- 默認
ulimit -n 1024 極小,生產環境嚴重不足。
方案一:調大系統級文件描述符限制(根治)
臨時生效(當前 Shell)
ulimit -n 65535
永久生效(需 root)
# /etc/security/limits.conf
mysql soft nofile 65535
mysql hard nofile 65535
# /etc/systemd/system/mysqld.service.d/override.conf (systemd)
[Service]
LimitNOFILE=65535
驗證
cat /proc/$(pidof mysqld)/limits | grep "Max open files"
方案二:調優 MySQL 參數減少佔用
| 參數 | 建議值 | 影響 |
table_open_cache | 2000~10000 (視表數量) | 減少打開/關閉表頻率,但增加持有描述符數 |
table_definition_cache | 1400~4000 | 緩存 .frm 定義,減少元數據讀取 |
max_connections | 實際峰值 + 20% | 每連接消耗線程棧、網絡緩衝、臨時文件描述符 |
open_files_limit | 65535 (MySQL 內部提示) | 啟動時向 OS 申請,受 ulimit 硬限制約束 |
注意:open_files_limit 僅是 MySQL 向 OS 申請的目標值,**最終上限仍由系統 ulimit 決定**。
方案三:架構層面減少表數量
- 合併同結構表:如日誌表按月分表 → 改用分區表(
PARTITION BY RANGE),元數據僅 1 條。
- 避免過度分庫分表:中小應用單庫單表 + 索引優化往往足夠。
- 定期清理臨時表:
SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables'; 監控磁盤臨時表創建率。
排查清單
SHOW GLOBAL STATUS LIKE 'Open%'; → Open_tables、Opened_tables、Open_files。
SHOW VARIABLES LIKE 'open_files_limit'; 對比 ulimit -n。
cat /proc/$(pidof mysqld)/limits 確認進程實際限制。
- 若為容器環境(Docker/K8s),需在宿主機或 Pod `securityContext` 設定
ulimit。
總結
- **首選方案一**:系統級
ulimit -n 65535 + systemd LimitNOFILE,一勞永逸。
- **輔助方案二**:合理設定
table_open_cache、max_connections,避免過度緩存。
- **長期方案三**:分區表替代手動分表,從源頭減少文件數。
文件描述符耗盡是 MySQL 高併發下的「隱形殺手」,建議**新建實例即納入基線配置**,而非等報錯再救火。
- 上一篇文章: Java 操作 MySQL 中文亂碼終極解決:ISO-8859-1 轉碼陷阱與正確姿勢
- 下一篇文章: MySQL 徹底解決中文亂碼:從源碼編譯到 utf8mb4 全鏈路實戰
Copyright ©
程式碼之家 All Rights Reserved