嘗試在 MySQL 觸發器中透過 mysql-udf-http 呼叫外部 HTTP 服務同步 Memcached,聽起來很美好——資料庫層自動完成緩存更新,應用層零侵入。但實測數據揭示了殘酷真相:單次 HTTP 呼叫延遲 9ms 起跳,千次寫入耗時 9 秒,效能不可接受。本文完整記錄測試環境、程式碼、數據分析與替代架構建議。
mysql-udf-http (提供 http_get(), http_post(), http_put())| 操作 | 方式 | 1000 次耗時 | 單次平均 |
|---|---|---|---|
| MySQL INSERT | PHP 直連 mysql_query | 0.9 秒 | 0.9 ms |
| Memcached SET | PHP 直連 Memcache::set | 0.8 秒 | 0.8 ms |
| MySQL INSERT + 觸發器 HTTP PUT | 觸發器呼叫 http_put() | 9 秒 | 9 ms |
| 觸發器純 SQL INSERT (同庫另一表) | 觸發器 INSERT INTO tmp... | 0.7 秒 | 0.7 ms |
CREATE TRIGGER `mytable_insert` AFTER INSERT ON `mytable`
FOR EACH ROW
BEGIN
SET @tt_resu = http_put(
CONCAT('https://192.168.0.134/mem_ss.php?type=post&id=', NEW.id, '&data=', NEW.addtime),
11 -- timeout 秒
);
END;
$mem = new Memcache();
$mem->connect('192.168.0.134', 11211);
$mem->set($_GET['id'], $_GET['data'], 0, 3600);
$mem->close();
http_put() 時,MySQL 執行緒完全阻塞等待 HTTP 回應,無法並行。new Memcache()->connect() + close(),TCP 三次握手 + Memcached 協議交換 ≈ 2-3ms。CONCAT() 組裝長 URL、PHP 端 $_GET 解析。| 優化手段 | 預期效果 | 實測結果 |
|---|---|---|
PHP 端改用持久連線 pconnect | 省去 TCP 握手 | 無顯著改善(CGI 模式進程隔離,持久連線無效) |
| 改用 FastCGI (PHP-FPM) | 復用進程、支援持久連線 | 單次降至 ~4ms,仍偏高 |
UDF 改用 libcurl 非同步 + 批次 | 非阻塞、批次發送 | 需重寫 UDF,屬架構級重構 |
改用 MySQL 8.0 HTTP() UDF (企業版) | 原生支援、連線池 | 需商業授權 |
// 應用程式碼
db.execute("INSERT INTO mytable ...");
memcache.set(key, value); // 同步或非同步佇列
優點:延遲可控、易除錯、可重試。缺點:應用層耦合、需事務性保證時複雜。
[MySQL Binlog] → [Canal / Debezium / Maxwell] → [Kafka / RocketMQ] → [Consumer] → [Memcached/Redis]
INVOKER + 元件 (企業版)支援在觸發器/預存程序中安全呼叫外部 REST/gRPC,內建連線池、超時、重試、熔斷。
CREATE TABLE sync_queue (id BIGINT AUTO_INCREMENT, payload JSON, status TINYINT, created_at TIMESTAMP);
-- 觸發器只寫入隊列(極快)
CREATE TRIGGER ... AFTER INSERT ON mytable FOR EACH ROW
INSERT INTO sync_queue (payload) VALUES (JSON_OBJECT('id', NEW.id, 'data', NEW.addtime));
-- 獨立 Worker 輪詢處理
優點:資料庫內完成、無外部依賴、可重試。缺點:需額外 Worker 進程、延遲取決於輪詢間隔。
mysql-udf-http 適合極低頻、非核心路徑的管理類操作(如發送告警、觸發重建索引任務),不適合高頻寫入路徑。技術選型的本質是權衡。看似優雅的「資料庫自動同步緩存」,若犧牲了寫入效能與穩定性,便得不償失。下一篇我們將探討 MySQL 慢查詢日誌分析與優化實戰,敬請期待。