首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

MySQL 讀寫分離架構實戰:MySQL Proxy、mysqlnd_ms 與 ProxySQL 方案對比

     編輯:MySQL 資料庫相關知識

前言

隨著業務增長,單一 MySQL 寫入壓力與讀取吞吐成為瓶頸。讀寫分離將 SELECT 導向從庫,INSERT/UPDATE/DELETE 保持在主庫,能線性擴展讀取能力。本文深度解析三種主流方案——MySQL Proxy(官方 Lua 腳本)、PHP mysqlnd_ms(驅動層)、ProxySQL(高效能中間件)的原理、配置、優缺點與生產選型建議。

一、核心原理對比

維度MySQL Proxymysqlnd_msProxySQL
層級網路層(TCP Proxy)PHP 驅動層(mysqlnd plugin)網路層(多執行緒 Proxy)
語言綁定無(通用)僅 PHP無(通用)
讀寫分離邏輯Lua 腳本 rw-splitting.luaJSON 規則 mysqlnd_ms_plugin.iniSQLite 規則庫 + Query Rules
連線池有(簡易)無(依賴 PHP-FPM)高效能多執行緒連線池
故障轉移弱(需外部腳本)無內建自動故障轉移、檢查
監控/統計極少無豐富(Web UI、Stats、Prometheus)
生產成熟度低(官方已停止維護)低(PHP 7.4+ 已移除 mysqlnd_ms)高(持續活躍、大廠廣泛使用)

二、方案一:MySQL Proxy(僅供教學參考)

2.1 安裝與配置

# 下載預編譯二進制(舊版 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

2.2 設定檔 /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

2.3 啟動與驗證

/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 mysqlnd_ms(已淘汰)

僅適用於 PHP 5.3~5.6 + mysqlnd 編譯環境。PHP 7.0 起移除 mysqlnd_ms,PHP 8 完全不支援。**僅維護舊專案時參考**。

3.1 安裝

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

3.2 規則檔 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 } }
  }
}

3.3 PHP 使用

$mysqli = new mysqli('myapp', 'user', 'pass', 'db');  // host 為 plugin 名稱
$mysqli->query('SELECT * FROM t');  // 自動路由到 slave
$mysqli->query('INSERT INTO t VALUES(1)');  // 自動路由到 master

四、方案三:ProxySQL(生產首選)

4.1 安裝

# 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

4.2 核心概念

  • Hostgroup:主庫 HG=10、從庫 HG=20、離線 HG=90。
  • Query Rules:依正則匹配 SQL 導向對應 Hostgroup。
  • Runtime vs Disk:LOAD MYSQL VARIABLES TO RUNTIME、SAVE MYSQL VARIABLES TO DISK。

4.3 管理介面配置(Admin Port 6032)

-- 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;

4.4 應用端連線

$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!');

4.5 進階功能

  • 鏡像流量:mirror_hostgroup 將流量複製到備用叢集做壓測。
  • 查詢重寫:mysql_query_rules.rewrite_pattern 自動優化惡意 SQL。
  • 限流/熔斷:max_connections、max_queue_size 保護後端。
  • Prometheus 指標: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 層無單點。

六、選型總結與遷移建議

  1. 新專案/重構:直接上 ProxySQL 2.5+,支援 MySQL 8.0、GTID、Prepared Statements、TLS。
  2. PHP 老專案:若仍在 PHP 5.6,可暫用 mysqlnd_ms,但規劃 6 個月內遷移至 ProxySQL(應用端零修改,只改連線 Port)。
  3. 多語言微服務:ProxySQL 為唯一通用方案,Java/Go/Python/Node.js 皆可透過標準 MySQL 協定接入。
  4. 雲原生:部署 ProxySQL Sidecar 或 DaemonSet,配合 Service Mesh (Istio) 做 mTLS 與可觀測性。

結語: MySQL Proxy 與 mysqlnd_ms 已成為歷史。ProxySQL 憑藉高效能、豐富規則、原生 HA、多語言支援,成為目前讀寫分離的事實標準。建議團隊投入精力掌握 ProxySQL 規則引擎與監控體系,而非維護過時元件。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved