MySQL Proxy 透過 Lua 腳本實現讀寫分離,但 0.6→0.7 版本 API 破壞性變更導致舊腳本失效,且 PHP PDO Prepare 模式與 Proxy 兼容性差。本文記錄遷坑全過程與生產級部署方案。
應用 → MySQL Proxy (Lua 腳本路由) → Master (寫) / Slaves (讀)
錯誤日誌**:
(critical) proxy-plugin.c:1367: .address is deprecated. Use .src.name or .dst.name instead
(critical) (read_query) .address is deprecated.
I have no server backend, closing connection
原因**:0.7 版本廢棄 proxy.connection.client.address,改為 .src.name/.dst.name。官方附帶的 rw-splitting.lua 未同步更新。
解決**:
https://launchpad.net/mysql-proxy-lua-scripts.address → .src.name (客戶端) / .dst.name (後端)connect_server()、read_query() 內的後端選擇邏輯必須更新現象**:單獨連 MySQL 正常,經 Proxy 查詢隨機無結果、無報錯。
根因**:Proxy 對 COM_STMT_PREPARE/COM_STMT_EXECUTE 二進制協議處理有缺陷。
繞過方案**:強制 PDO 使用模擬準備(客戶端模擬,非 Server 端):
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_EMULATE_PREPARES => true, // 關鍵!
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
副作用**:失去 Server 端準備語句緩存優勢,但換取穩定性。實測模擬模式效能不差 (甚至略好,省去協議交互)。
Proxy 自身會 Crash,需外部守護:
# 啟動參數加入 keepalive
mysql-proxy --defaults-file=/etc/mysql-proxy.cnf --keepalive --daemon
生產級高可用方案**:
mysqladmin ping[Service]
Restart=always
RestartSec=5
ExecStart=/usr/local/mysql-proxy/bin/mysql-proxy --defaults-file=/etc/mysql-proxy.cnf --keepaliveproxy_up、proxy_backend_up、proxy_query_latency# /etc/mysql-proxy.cnf
[mysql-proxy]
daemon = true
keepalive = true
log_file = /var/log/mysql-proxy.log
log_level = info
proxy_address = 0.0.0.0:4040
proxy_read_only_backend_addresses = 10.0.1.10:3306,10.0.1.11:3306
proxy_backend_addresses = 10.0.1.9:3306
# Lua 腳本路徑 (使用更新版 rw-splitting.lua)
proxy_lua_script = /usr/local/mysql-proxy/share/lua/rw-splitting.lua
# 連接池/超時
proxy_pool_no_change_user = true
proxy_fix_bug_25371 = 1
proxy_connect_timeout = 5
proxy_read_timeout = 30
proxy_write_timeout = 30
function read_query(packet)
if packet:byte() == proxy.COM_QUERY then
local query = packet:sub(2)
-- SELECT / SHOW / BEGIN 走從庫
if query:match("^%s*SELECT") or query:match("^%s*SHOW") or query:match("^%s*BEGIN") then
proxy.queries:append(1, string.char(proxy.COM_QUERY) .. query)
return proxy.PROXY_SEND_QUERY
end
end
return nil -- 其他 (INSERT/UPDATE/DELETE/DDL) 走主庫
end
function connect_server(proxy)
-- 後端連接建立時的初始化
-- 可在這裡 SET NAMES utf8mb4、SET SESSION sql_mode...
end
| 方案 | 優點 | 缺點 | 推薦場景 |
|---|---|---|---|
| MySQL Proxy + Lua | 輕量、透明、可編程路由 | 單點、Lua 維護成本高、Prepare 兼容差 | 舊系統改造、簡單讀寫分離 |
| ProxySQL | 高效能、連接池、查詢緩存、熔斷、集群管理、Web UI | 學習曲線、資源佔用較大 | **生產環境首選** |
| MaxScale | MariaDB 官方、Binlog 過濾、安全防火牆 | 商業功能收費、較重 | MariaDB 生態、合規審計 |
| 應用層路由 | 零中間件、靈活控制 | 代碼侵入、連接池分散 | 微服務、多語言 |
MySQL Proxy 讀寫分離可用,但**必須使用更新版 Lua 腳本**、**強制 PDO 模擬 Prepare**、**部署 Keepalive/雙機高可用**。新架構強烈建議直接上 **ProxySQL**,功能完備、社群活躍、生產驗證充分。