案例背景
- 單日 PV 近 1 億,MySQL 單點寫入崩潰,統計系統全天零資料。
- 團隊資源有限,排除叢集、分庫分表 (成本/技術門檻高)。
演進軌跡與失敗教訓
| 方案 | 做法 | 結果 | 教訓 |
| 叢集 | MySQL Cluster / Galera | 否決 | 成本高、技術債重 |
| 分庫分表 | 按流量切 10 台低配機 | 暫時緩解 | 統計聚合痛苦、非長久 |
| 改寫寫入邏輯 | 先查重再插 → 直接插入 | 資料保住 | 索引維護拖垮、後續處理仍死 |
| 移除索引 | 寫入快、查詢極慢 | 得不償失 | 索引是查詢命脈,不可捨 |
最終制勝方案:寫入檔案落地 + 異步批次入庫
核心架構
請求 → PHP error_log() 寫入本地檔案 (按小時+IP分片)
↓
離線處理腳本 (每小時/每日)
1. 讀取檔案 → 去重/聚合 (同 IP 合併 PV)
2. LOAD DATA INFILE 批次入庫 (秒級百萬行)
↓
統計報表正常跑
關鍵技術細節
- 檔案命名:
stats_2024011514_221.log (YYYYMMDDHH_IP前三段)。
- 寫入函數:
error_log($line, 3, $file) — 系統級追加寫入、自動鎖定、效能極高。
- 去重邏輯: 同 IP 同 URL 合併計數,減少 90%+ 入庫行數。
- 入庫指令:
LOAD DATA INFILE '/path/stats_2024011514_221.log'
INTO TABLE stats
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
(`access_time`, `url_md5`, `ip`, `ua`);
效能對比
| 指標 | 直接 INSERT | 檔案落地 + LOAD DATA |
| 寫入 QPS | ~500 (鎖爭用) | ~50,000+ (檔案追加) |
| 入庫耗時 (百萬行) | 數小時 | < 30 秒 |
| 資料完整性 | 高併發丟失 | 零丟失 (檔案可重跑) |
架構原則總結
- 寫入路徑極簡化: 不做查重、不做邏輯、只落地。
- 計算下沉離線: 複雜聚合移至批次腳本,不阻塞線上。
- 利用資料庫批次工具:
LOAD DATA INFILE 是 MySQL 最高效入庫方式。
- 冪等設計: 檔案可重複處理,主鍵/唯一鍵保證冪等。
適用場景擴展
- 日誌收集、點擊流、IoT 遙測、廣告曝光/點擊統計。
- 任何「寫多讀少、允許秒級延遲」的高併發寫入場景。
解決資料庫瓶頸不一定要加硬體、換引擎、上叢集;**換個思路,把寫入變成順序追加檔案**,往往能以 1/10 成本獲得 100 倍吞吐。
- 上一篇文章: MySQL 雙向複制 (Master-Master) 完整部署指南:雙主同步架構實戰
- 下一篇文章: MySQL 全文搜索布林模式完整語法與操作符詳解
Copyright ©
程式碼之家 All Rights Reserved