隨著業務增長,單一 MySQL 寫入壓力與讀取吞吐成為瓶頸。讀寫分離將 SELECT 導向從庫,INSERT/UPDATE/DELETE 保持在主庫,能線性擴展讀取能力。本文深度解析三種主流方案——MySQL Proxy(官方 Lua 腳本)、PHP mysqlnd_ms(驅動層)、ProxySQL(高效能中間件)的原理、配置、優缺點與生產選型建議。
| 維度 | MySQL Proxy | mysqlnd_ms | ProxySQL |
|---|---|---|---|
| 層級 | 網路層(TCP Proxy) | PHP 驅動層(mysqlnd plugin) | 網路層(多執行緒 Proxy) |
| 語言綁定 | 無(通用) | 僅 PHP | 無(通用) |
| 讀寫分離邏輯 | Lua 腳本 rw-splitting.lua | JSON 規則 mysqlnd_ms_plugin.ini | SQLite 規則庫 + Query Rules |
| 連線池 | 有(簡易) | 無(依賴 PHP-FPM) | 高效能多執行緒連線池 |
| 故障轉移 | 弱(需外部腳本) | 無 | 內建自動故障轉移、檢查 |
| 監控/統計 | 極少 | 無 | 豐富(Web UI、Stats、Prometheus) |
| 生產成熟度 | 低(官方已停止維護) | 低(PHP 7.4+ 已移除 mysqlnd_ms) | 高(持續活躍、大廠廣泛使用) |
# 下載預編譯二進制(舊版 0.8.5) wget https://downloads.mysql.com/archives/get/file/mysql-proxy-0.8.5-linux-glibc2.12-x86-64bit.tar.gz tar xzf mysql-proxy-*.tar.gz mv mysql-proxy-* /usr/local/mysql-proxy
/usr/local/mysql-proxy/mysql-proxy.cnf[mysql-proxy] daemon=true keepalive=true log-level=info log-file=/var/log/mysql-proxy.log proxy-address=0.0.0.0:4040 proxy-backend-addresses=192.168.1.10:3306 # Master proxy-read-only-backend-addresses=192.168.1.11:3306 # Slave proxy-lua-script=/usr/local/mysql-proxy/share/doc/mysql-proxy/rw-splitting.lua admin-address=0.0.0.0:4041 admin-username=admin admin-password=AdminPassw0rd! admin-lua-script=/usr/local/mysql-proxy/share/doc/mysql-proxy/admin.lua
/usr/local/mysql-proxy/bin/mysql-proxy --defaults-file=/usr/local/mysql-proxy/mysql-proxy.cnf # 管理介面驗證後端狀態 mysql -uadmin -pAdminPassw0rd! -h127.0.0.1 -P4041 -e "SELECT * FROM backends;"
缺點: 單執行緒、Lua 腳本效能差、無原生健康檢查、官方 2016 年後停更,**不建議生產使用**。
僅適用於 PHP 5.3~5.6 + mysqlnd 編譯環境。PHP 7.0 起移除 mysqlnd_ms,PHP 8 完全不支援。**僅維護舊專案時參考**。
pecl install mysqlnd_ms-1.6.0 # 需 phpize、mysqlnd headers # php.ini extension=mysqlnd_ms.so mysqlnd_ms.enable=1 mysqlnd_ms.config_file=/etc/php/mysqlnd_ms_plugin.ini
mysqlnd_ms_plugin.ini{
"myapp": {
"master": { "master_0": { "host": "192.168.1.10", "port": 3306 } },
"slave": { "slave_0": { "host": "192.168.1.11", "port": 3306 } },
"filters": { "random": { "sticky": true } }
}
}
$mysqli = new mysqli('myapp', 'user', 'pass', 'db'); // host 為 plugin 名稱
$mysqli->query('SELECT * FROM t'); // 自動路由到 slave
$mysqli->query('INSERT INTO t VALUES(1)'); // 自動路由到 master
# CentOS/RHEL yum install -y https://repo.proxysql.com/proxysql-2.5.x/centos/7/proxysql-2.5.5-1.el7.x86_64.rpm # 或 Docker docker run -d --name proxysql -p 6033:6033 -p 6032:6032 proxysql/proxysql:2.5.5
LOAD MYSQL VARIABLES TO RUNTIME、SAVE MYSQL VARIABLES TO DISK。-- 1. 設定後端伺服器
INSERT INTO mysql_servers (hostgroup_id, hostname, port, weight, max_connections, comment)
VALUES (10, '192.168.1.10', 3306, 100, 1000, 'Master'),
(20, '192.168.1.11', 3306, 100, 1000, 'Slave1'),
(20, '192.168.1.12', 3306, 100, 1000, 'Slave2');
-- 2. 設定使用者(連線池認證)
INSERT INTO mysql_users (username, password, default_hostgroup, active)
VALUES ('app_user', 'AppPassw0rd!', 10, 1);
-- 3. 讀寫分離規則:SELECT 走 HG=20,其他走 HG=10
INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup, apply)
VALUES (1, 1, '^SELECT.*FOR UPDATE$', 10, 1), -- SELECT ... FOR UPDATE 必須走主
(2, 1, '^SELECT', 20, 1), -- 一般 SELECT 走從
(3, 1, '^INSERT|^UPDATE|^DELETE|^REPLACE', 10, 1);
-- 4. 啟用健康檢查
UPDATE global_variables SET variable_value='2000' WHERE variable_name='mysql-monitor_connect_interval';
UPDATE global_variables SET variable_value='1000' WHERE variable_name='mysql-monitor_ping_interval';
UPDATE global_variables SET variable_value='true' WHERE variable_name='mysql-monitor_read_only_interval';
-- 5. 落地生效
LOAD MYSQL SERVERS TO RUNTIME; SAVE MYSQL SERVERS TO DISK;
LOAD MYSQL USERS TO RUNTIME; SAVE MYSQL USERS TO DISK;
LOAD MYSQL QUERY RULES TO RUNTIME; SAVE MYSQL QUERY RULES TO DISK;
LOAD MYSQL VARIABLES TO RUNTIME; SAVE MYSQL VARIABLES TO DISK;
$mysqli = new mysqli('127.0.0.1', 'app_user', 'AppPassw0rd!', 'db', 6033);
// 或 PDO
$pdo = new PDO('mysql:host=127.0.0.1;port=6033;dbname=db', 'app_user', 'AppPassw0rd!');
mirror_hostgroup 將流量複製到備用叢集做壓測。mysql_query_rules.rewrite_pattern 自動優化惡意 SQL。max_connections、max_queue_size 保護後端。https://proxysql:6070/metrics 整合 Grafana。 +------------------+
| Keepalived/VIP |
+--------+---------+
|
+-----------+-----------+
| |
ProxySQL 1 (Active) ProxySQL 2 (Standby)
6033/6032 6033/6032
| |
+-----------+-----------+
|
+----------+----------+
| |
MySQL Master MySQL Slave(s)
(HG=10) (HG=20)
| |
Binlog (GTID) Relay Log
+----------+----------+
|
Orchestrator / MHA
(自動故障轉移)
ProxySQL 叢集模式(proxysql_cluster)可同步多節點配置,搭配 Keepalived VIP 實現 Proxy 層無單點。
結語: MySQL Proxy 與 mysqlnd_ms 已成為歷史。ProxySQL 憑藉高效能、豐富規則、原生 HA、多語言支援,成為目前讀寫分離的事實標準。建議團隊投入精力掌握 ProxySQL 規則引擎與監控體系,而非維護過時元件。