ProxySQL 部署实施方案 - MySQL读写分离

一、项目概述

1.1 目标

部署 ProxySQL 实现 MySQL 数据库的读写分离,提高数据库性能和可用性。

1.2 环境规划

角色主机名IP地址端口软件版本
ProxySQL 节点proxysql-01192.168.1.1006032/6033ProxySQL 2.5.x
MySQL 主库mysql-master192.168.1.1013306MySQL 8.0
MySQL 从库1mysql-slave1192.168.1.1023306MySQL 8.0
MySQL 从库2mysql-slave2192.168.1.1033306MySQL 8.0
应用服务器app-server192.168.1.200-业务应用

1.3 网络拓扑

应用程序
    │
    ├─ 写请求 ──┐
    │          ▼
    │    ProxySQL (6033) ────┬─── MySQL 主库 (写)
    │          ▲             │
    └─ 读请求 ──┘             ├─── MySQL 从库1 (读)
                              │
                              └─── MySQL 从库2 (读)

二、准备工作

2.1 系统要求检查

# 在所有节点执行
# 1. 检查系统版本
cat /etc/redhat-release
# 预期: Rocky Linux 8/9 或 CentOS 7/8

# 2. 检查防火墙
systemctl status firewalld
# 如开启,配置防火墙规则

# 3. 检查SELinux
getenforce
# 建议设置为 Permissive 或 Disabled
sudo setenforce 0
sudo sed -i 's/^SELINUX=.*/SELINUX=permissive/' /etc/selinux/config

2.2 时间同步配置

# 所有节点配置NTP
sudo dnf install chrony -y
sudo systemctl enable chronyd
sudo systemctl start chronyd
sudo chronyc sources -v

2.3 主机名解析

# 在所有节点的 /etc/hosts 添加
sudo tee -a /etc/hosts << EOF
192.168.1.100 proxysql-01
192.168.1.101 mysql-master
192.168.1.102 mysql-slave1
192.168.1.103 mysql-slave2
192.168.1.200 app-server
EOF

三、MySQL 主从复制配置

3.1 MySQL 安装配置(所有MySQL节点)

# 1. 安装 MySQL 8.0
sudo dnf install -y mysql-server mysql-shell

# 2. 启动服务
sudo systemctl enable mysqld
sudo systemctl start mysqld

# 3. 获取初始密码
sudo grep 'temporary password' /var/log/mysqld.log

# 4. 安全配置
sudo mysql_secure_installation

3.2 主库配置 (mysql-master)

# 编辑配置文件
sudo tee /etc/my.cnf.d/master.cnf << 'EOF'
[mysqld]
# 服务器ID,主库设为1
server_id=1

# 启用二进制日志
log_bin=mysql-bin
binlog_format=ROW

# 需要复制的数据库(根据实际情况修改)
binlog_do_db=mydb

# 不需要复制的数据库
# binlog_ignore_db=mysql

# 从库更新时也写二进制日志
log_slave_updates=1

# 自动清理过期日志
expire_logs_days=7
max_binlog_size=100M

# 复制配置
gtid_mode=ON
enforce_gtid_consistency=ON

# 从库相关配置
relay_log=mysql-relay-bin
relay_log_index=mysql-relay-bin.index

# 性能优化
innodb_flush_log_at_trx_commit=1
sync_binlog=1

# 连接配置
max_connections=1000
wait_timeout=600
interactive_timeout=600
EOF

# 重启MySQL
sudo systemctl restart mysqld

3.3 从库配置 (mysql-slave1, mysql-slave2)

# 编辑配置文件(mysql-slave1)
sudo tee /etc/my.cnf.d/slave.cnf << 'EOF'
[mysqld]
# 服务器ID,从库需要不同
server_id=2  # slave1 用 2,slave2 用 3

# 启用二进制日志(可选,用于级联复制)
log_bin=mysql-bin
binlog_format=ROW

# 只读模式(从库推荐设置)
read_only=1
super_read_only=1

# 中继日志配置
relay_log=mysql-relay-bin
relay_log_index=mysql-relay-bin.index

# GTID配置
gtid_mode=ON
enforce_gtid_consistency=ON

# 复制过滤(可选)
# replicate_do_db=mydb
# replicate_ignore_db=mysql

# 性能优化
innodb_flush_log_at_trx_commit=2
sync_binlog=0

# 连接配置
max_connections=1000
wait_timeout=600
interactive_timeout=600
EOF

# 重启MySQL
sudo systemctl restart mysqld

3.4 配置主从复制

在主库执行:
-- 登录MySQL
mysql -uroot -p

-- 创建复制用户
CREATE USER 'repl'@'192.168.1.%' IDENTIFIED BY 'Repl@2024';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.1.%';
FLUSH PRIVILEGES;

-- 查看主库状态,记录 File 和 Position
SHOW MASTER STATUS\G
-- 输出示例:
-- File: mysql-bin.000001
-- Position: 154
在从库执行:
-- 登录MySQL
mysql -uroot -p

-- 配置复制
CHANGE MASTER TO
MASTER_HOST='192.168.1.101',
MASTER_USER='repl',
MASTER_PASSWORD='Repl@2024',
MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000001',  -- 替换为实际的File
MASTER_LOG_POS=154,                  -- 替换为实际的Position
MASTER_CONNECT_RETRY=10,
MASTER_RETRY_COUNT=86400;

-- 启动复制
START SLAVE;

-- 检查复制状态
SHOW SLAVE STATUS\G
-- 确保 Slave_IO_Running 和 Slave_SQL_Running 都是 Yes

3.5 验证主从复制

-- 在主库创建测试数据
CREATE DATABASE IF NOT EXISTS mydb;
USE mydb;
CREATE TABLE IF NOT EXISTS test_table (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO test_table (name) VALUES ('test_from_master');

-- 在从库查询验证
SELECT * FROM mydb.test_table;

四、ProxySQL 部署配置

4.1 安装 ProxySQL

# 在 proxysql-01 节点执行

# 1. 添加 ProxySQL 仓库
sudo tee /etc/yum.repos.d/proxysql.repo << 'EOF'
[proxysql_repo]
name=ProxySQL Repository
baseurl=https://repo.proxysql.com/ProxySQL/proxysql-2.5.x/centos/\$releasever
gpgcheck=1
gpgkey=https://repo.proxysql.com/ProxySQL/proxysql-2.5.x/repo_pub_key
EOF

# 2. 安装 ProxySQL
sudo dnf install proxysql -y

# 3. 检查版本
proxysql --version

# 4. 启动服务
sudo systemctl enable proxysql
sudo systemctl start proxysql
sudo systemctl status proxysql

4.2 初始配置

# 登录 ProxySQL 管理界面
mysql -u admin -padmin -h 127.0.0.1 -P 6032 --prompt='ProxySQL> '

# 修改管理员密码(安全建议)
UPDATE global_variables SET variable_value='admin:admin_password' 
WHERE variable_name='admin-admin_credentials';
-- 注意:修改后需要重启或重载配置

4.3 配置监控用户

在 MySQL 主库上创建监控用户:

-- 在主库执行
CREATE USER 'monitor'@'192.168.1.%' IDENTIFIED BY 'Monitor@2024';
GRANT USAGE, REPLICATION CLIENT ON *.* TO 'monitor'@'192.168.1.%';
FLUSH PRIVILEGES;

在 ProxySQL 中配置监控:

-- 在 ProxySQL 管理界面执行
-- 设置监控用户
UPDATE global_variables SET variable_value='monitor' 
WHERE variable_name='mysql-monitor_username';
UPDATE global_variables SET variable_value='Monitor@2024' 
WHERE variable_name='mysql-monitor_password';

-- 设置监控间隔
UPDATE global_variables SET variable_value='2000' 
WHERE variable_name='mysql-monitor_connect_interval';
UPDATE global_variables SET variable_value='2000' 
WHERE variable_name='mysql-monitor_ping_interval';
UPDATE global_variables SET variable_value='10000' 
WHERE variable_name='mysql-monitor_read_only_interval';

-- 加载到运行时
LOAD MYSQL VARIABLES TO RUNTIME;
SAVE MYSQL VARIABLES TO DISK;

4.4 配置后端 MySQL 服务器

-- 在 ProxySQL 管理界面执行

-- 清空现有服务器配置
DELETE FROM mysql_servers;

-- 添加主库(写组 hostgroup_id=0)
INSERT INTO mysql_servers (hostgroup_id, hostname, port, weight, max_connections) 
VALUES 
(0, '192.168.1.101', 3306, 1000, 1000);

-- 添加从库(读组 hostgroup_id=1)
INSERT INTO mysql_servers (hostgroup_id, hostname, port, weight, max_connections) 
VALUES 
(1, '192.168.1.102', 3306, 100, 1000),
(1, '192.168.1.103', 3306, 100, 1000);

-- 查看配置
SELECT * FROM mysql_servers;

-- 加载配置
LOAD MYSQL SERVERS TO RUNTIME;
SAVE MYSQL SERVERS TO DISK;

4.5 配置复制主机组

-- 配置读写分离的主机组映射
INSERT INTO mysql_replication_hostgroups 
(writer_hostgroup, reader_hostgroup, comment) 
VALUES (0, 1, 'MySQL Cluster RW Separation');

-- 查看配置
SELECT * FROM mysql_replication_hostgroups;

-- 加载配置
LOAD MYSQL SERVERS TO RUNTIME;
SAVE MYSQL SERVERS TO DISK;

4.6 配置应用用户

在 MySQL 主库上创建应用用户:

-- 在主库执行
CREATE USER 'app_user'@'192.168.1.%' IDENTIFIED BY 'App@2024';
GRANT ALL PRIVILEGES ON mydb.* TO 'app_user'@'192.168.1.%';
FLUSH PRIVILEGES;

在 ProxySQL 中配置用户:

-- 在 ProxySQL 管理界面执行

-- 清空现有用户
DELETE FROM mysql_users;

-- 添加应用用户
INSERT INTO mysql_users (username, password, default_hostgroup, default_schema, transaction_persistent, fast_forward) 
VALUES 
('app_user', 'App@2024', 0, 'mydb', 1, 0);

-- 查看用户配置
SELECT * FROM mysql_users;

-- 加载配置
LOAD MYSQL USERS TO RUNTIME;
SAVE MYSQL USERS TO DISK;

4.7 配置查询规则

-- 在 ProxySQL 管理界面执行

-- 清空现有规则
DELETE FROM mysql_query_rules;

-- 规则1:SELECT...FOR UPDATE 路由到主库
INSERT INTO mysql_query_rules 
(rule_id, active, match_pattern, destination_hostgroup, apply) 
VALUES 
(1, 1, '^SELECT.*FOR UPDATE', 0, 1);

-- 规则2:所有写操作路由到主库
INSERT INTO mysql_query_rules 
(rule_id, active, match_pattern, destination_hostgroup, apply) 
VALUES 
(2, 1, '^INSERT', 0, 1),
(3, 1, '^UPDATE', 0, 1),
(4, 1, '^DELETE', 0, 1),
(5, 1, '^REPLACE', 0, 1),
(6, 1, '^CREATE', 0, 1),
(7, 1, '^ALTER', 0, 1),
(8, 1, '^DROP', 0, 1),
(9, 1, '^TRUNCATE', 0, 1),
(10, 1, '^RENAME', 0, 1),
(11, 1, '^LOCK', 0, 1),
(12, 1, '^UNLOCK', 0, 1),
(13, 1, '^CALL.*', 0, 1);

-- 规则3:特定表的读操作(可根据需要调整)
INSERT INTO mysql_query_rules 
(rule_id, active, match_pattern, destination_hostgroup, apply) 
VALUES 
(14, 1, '^SELECT.*FROM users.*', 1, 0),
(15, 1, '^SELECT.*FROM orders.*', 1, 0),
(16, 1, '^SELECT.*FROM products.*', 1, 0);

-- 规则4:其他所有SELECT路由到从库
INSERT INTO mysql_query_rules 
(rule_id, active, match_pattern, destination_hostgroup, apply) 
VALUES 
(17, 1, '^SELECT', 1, 1);

-- 规则5:其他所有查询路由到主库(默认规则)
INSERT INTO mysql_query_rules 
(rule_id, active, match_pattern, destination_hostgroup, apply) 
VALUES 
(100, 1, '^.*', 0, 1);

-- 查看规则
SELECT rule_id, active, match_pattern, destination_hostgroup, apply 
FROM mysql_query_rules ORDER BY rule_id;

-- 加载配置
LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;

4.8 配置负载均衡算法

-- 配置读负载均衡算法
UPDATE mysql_servers SET weight=100 WHERE hostgroup_id=1 AND hostname='192.168.1.102';
UPDATE mysql_servers SET weight=150 WHERE hostgroup_id=1 AND hostname='192.168.1.103';  -- 如果slave2性能更好

-- 或者使用随机权重
UPDATE global_variables SET variable_value='random' 
WHERE variable_name='mysql-query_digests';

五、防火墙配置

# 在 ProxySQL 节点配置防火墙
sudo firewall-cmd --permanent --add-port=6032/tcp  # 管理端口
sudo firewall-cmd --permanent --add-port=6033/tcp  # 应用连接端口
sudo firewall-cmd --reload

# 在 MySQL 节点配置防火墙
sudo firewall-cmd --permanent --add-port=3306/tcp
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="3306" protocol="tcp" accept'
sudo firewall-cmd --reload

六、验证部署

6.1 测试连接

# 使用应用用户连接 ProxySQL
mysql -u app_user -pApp@2024 -h 192.168.1.100 -P 6033 mydb

# 执行测试查询
-- 写操作测试(应该路由到主库)
INSERT INTO test_table (name) VALUES ('test_from_proxysql');
SELECT @@hostname;

-- 读操作测试(应该路由到从库)
SELECT * FROM test_table;
SELECT @@hostname;

6.2 监控 ProxySQL 状态

-- 登录管理界面查看
mysql -u admin -padmin -h 127.0.0.1 -P 6032

-- 查看后端服务器状态
SELECT * FROM monitor.mysql_server_ping_log ORDER BY time_start_us DESC LIMIT 5;
SELECT * FROM monitor.mysql_server_read_only_log ORDER BY time_start_us DESC LIMIT 5;

-- 查看查询统计
SELECT hostgroup, count_star, sum_time, sum_rows_affected 
FROM stats_mysql_query_digest 
ORDER BY sum_time DESC LIMIT 10;

-- 查看连接统计
SELECT * FROM stats_mysql_connections;

-- 查看查询规则匹配情况
SELECT * FROM stats_mysql_query_rules;

6.3 验证读写分离

# 创建测试脚本 test_rw_separation.sh
cat > test_rw_separation.sh << 'EOF'
#!/bin/bash

DB_HOST="192.168.1.100"
DB_PORT="6033"
DB_USER="app_user"
DB_PASS="App@2024"
DB_NAME="mydb"

echo "=== 测试读写分离 ==="
echo "1. 执行写操作 (INSERT)..."
mysql -u${DB_USER} -p${DB_PASS} -h${DB_HOST} -P${DB_PORT} ${DB_NAME} -e "
INSERT INTO test_table (name) VALUES ('test_write_$(date +%s)');
SELECT '写操作执行在: ', @@hostname;" 2>/dev/null

echo -e "\n2. 执行读操作 (SELECT)..."
mysql -u${DB_USER} -p${DB_PASS} -h${DB_HOST} -P${DB_PORT} ${DB_NAME} -e "
SELECT '读操作执行在: ', @@hostname;
SELECT id, name, created_at FROM test_table ORDER BY id DESC LIMIT 3;" 2>/dev/null

echo -e "\n3. 检查查询路由..."
mysql -u admin -padmin -h 127.0.0.1 -P 6032 -e "
SELECT digest_text, hostgroup, count_star 
FROM stats_mysql_query_digest 
WHERE digest_text LIKE '%test_table%' 
ORDER BY time_last DESC LIMIT 5;" 2>/dev/null
EOF

chmod +x test_rw_separation.sh
./test_rw_separation.sh

七、性能优化配置

7.1 连接池优化

-- 调整连接池大小
UPDATE global_variables SET variable_value='100' 
WHERE variable_name='mysql-connection_max_age_ms';
UPDATE global_variables SET variable_value='3' 
WHERE variable_name='mysql-connection_delay_multiplex_ms';
UPDATE global_variables SET variable_value='10000' 
WHERE variable_name='mysql-connect_retries_on_failure';

7.2 查询缓存配置

-- 启用查询缓存(可选)
UPDATE mysql_query_rules SET cache_ttl=30000 
WHERE match_pattern LIKE '^SELECT.*FROM products%' 
AND destination_hostgroup=1;

-- 配置缓存大小
UPDATE global_variables SET variable_value='256' 
WHERE variable_name='mysql-query_cache_size_MB';

7.3 监控告警配置

# 创建监控脚本 /usr/local/bin/check_proxysql.sh
sudo tee /usr/local/bin/check_proxysql.sh << 'EOF'
#!/bin/bash

# 检查 ProxySQL 服务状态
if ! systemctl is-active --quiet proxysql; then
    echo "CRITICAL: ProxySQL service is down!"
    systemctl restart proxysql
    exit 1
fi

# 检查后端 MySQL 状态
DOWN_SERVERS=$(mysql -u admin -padmin -h 127.0.0.1 -P 6032 -Nse \
"SELECT COUNT(*) FROM mysql_servers WHERE status != 'ONLINE';")

if [ "$DOWN_SERVERS" -gt 0 ]; then
    echo "WARNING: $DOWN_SERVERS MySQL servers are not ONLINE"
    mysql -u admin -padmin -h 127.0.0.1 -P 6032 -e \
    "SELECT hostname, port, status, error FROM mysql_servers WHERE status != 'ONLINE';"
fi

# 检查连接数
CONN_COUNT=$(mysql -u admin -padmin -h 127.0.0.1 -P 6032 -Nse \
"SELECT SUM(ConnFree+ConnUsed) FROM stats_mysql_connection_pool;")

if [ "$CONN_COUNT" -gt 500 ]; then
    echo "WARNING: High connection count: $CONN_COUNT"
fi

echo "OK: ProxySQL status normal"
EOF

sudo chmod +x /usr/local/bin/check_proxysql.sh

# 添加到 crontab
echo "*/5 * * * * /usr/local/bin/check_proxysql.sh >> /var/log/proxysql_monitor.log 2>&1" | sudo crontab -

八、高可用方案(可选)

8.1 配置 ProxySQL 集群

-- 在主 ProxySQL 节点执行
UPDATE global_variables SET variable_value=2 
WHERE variable_name='admin-cluster_username';
UPDATE global_variables SET variable_value='cluster_password' 
WHERE variable_name='admin-cluster_password';

-- 添加集群节点(如果有多个ProxySQL实例)
INSERT INTO proxysql_servers (hostname, port, weight, comment) 
VALUES 
('192.168.1.100', 6032, 100, 'Primary ProxySQL'),
('192.168.1.110', 6032, 10, 'Secondary ProxySQL');  -- 如果有备用节点

LOAD ADMIN VARIABLES TO RUNTIME;
SAVE ADMIN VARIABLES TO DISK;

8.2 使用 Keepalived 实现 VIP 漂移

# 安装 Keepalived
sudo dnf install keepalived -y

# 配置 Keepalived
sudo tee /etc/keepalived/keepalived.conf << 'EOF'
vrrp_script chk_proxysql {
    script "/usr/local/bin/check_proxysql.sh"
    interval 2
    weight 2
}

vrrp_instance VI_1 {
    state MASTER  # 主节点设为MASTER,备节点设为BACKUP
    interface eth0
    virtual_router_id 51
    priority 100  # 主节点100,备节点90
    
    virtual_ipaddress {
        192.168.1.99/24 dev eth0
    }
    
    track_script {
        chk_proxysql
    }
}
EOF

sudo systemctl enable keepalived
sudo systemctl start keepalived

九、应用端配置

9.1 应用连接配置示例

Java 应用 (application.yml):
spring:
  datasource:
    url: jdbc:mysql://192.168.1.100:6033/mydb?useSSL=false&serverTimezone=Asia/Shanghai
    username: app_user
    password: App@2024
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      connection-timeout: 30000
PHP 应用 (config.php):
<?php
$db_config = [
    'host' => '192.168.1.100',
    'port' => 6033,
    'username' => 'app_user',
    'password' => 'App@2024',
    'database' => 'mydb',
    'charset' => 'utf8mb4'
];
?>

十、部署检查清单

10.1 部署前检查

  • 网络连通性测试
  • 主机名解析配置
  • 防火墙端口开放
  • SELinux 状态检查
  • 时间同步配置

10.2 部署后验证

  • MySQL 主从复制状态正常
  • ProxySQL 服务运行正常
  • 监控用户连接正常
  • 应用用户连接正常
  • 读写分离功能验证
  • 性能测试通过

10.3 性能测试

# 使用 sysbench 进行压力测试
sysbench oltp_read_write \
--mysql-host=192.168.1.100 \
--mysql-port=6033 \
--mysql-user=app_user \
--mysql-password=App@2024 \
--mysql-db=mydb \
--tables=10 \
--table-size=10000 \
--time=300 \
--threads=20 \
--report-interval=10 \
prepare

sysbench oltp_read_write \
--mysql-host=192.168.1.100 \
--mysql-port=6033 \
--mysql-user=app_user \
--mysql-password=App@2024 \
--mysql-db=mydb \
--tables=10 \
--table-size=10000 \
--time=300 \
--threads=20 \
--report-interval=10 \
run

十一、故障处理指南

11.1 常见问题

问题1:连接失败
# 检查网络
ping 192.168.1.100
telnet 192.168.1.100 6033

# 检查服务状态
systemctl status proxysql
mysql -u admin -padmin -h 127.0.0.1 -P 6032 -e "SELECT 1"
问题2:读写分离不生效
-- 检查查询规则匹配
SELECT * FROM stats_mysql_query_rules;
-- 检查查询统计
SELECT hostgroup, digest_text 
FROM stats_mysql_query_digest 
WHERE digest_text LIKE '%SELECT%';
问题3:后端 MySQL 故障
-- 手动标记服务器离线
UPDATE mysql_servers SET status='OFFLINE_HARD' WHERE hostname='192.168.1.102';
LOAD MYSQL SERVERS TO RUNTIME;

十二、维护计划

12.1 日常维护

  • 每日检查 ProxySQL 和 MySQL 日志
  • 每周清理旧日志文件
  • 每月备份 ProxySQL 配置

12.2 配置备份

# 备份 ProxySQL 配置
mysqldump -u admin -padmin -h 127.0.0.1 -P 6032 --databases main stats \
--no-create-info --skip-triggers --skip-lock-tables > proxysql_backup_$(date +%Y%m%d).sql

# 备份到 crontab
echo "0 2 * * * mysqldump -u admin -padmin -h 127.0.0.1 -P 6032 --databases main stats --no-create-info --skip-triggers --skip-lock-tables > /backup/proxysql_$(date +\%Y\%m\%d).sql" | sudo crontab -

十三、回滚计划

13.1 回滚条件

  • ProxySQL 性能不如预期
  • 应用出现大量连接错误
  • 数据一致性出现问题

13.2 回滚步骤

  1. 修改应用配置,直连 MySQL 主库
  2. 停止 ProxySQL 服务
  3. 验证应用功能正常
  4. 分析问题原因

部署完成标志:

  1. 所有测试用例通过
  2. 性能测试结果符合预期
  3. 监控系统正常运行
  4. 文档完整交付

负责人签字:____________
完成日期:____年__月__日

更多推荐