ProxySQL 部署实施方案 - MySQL读写分离
·
目录
ProxySQL 部署实施方案 - MySQL读写分离
一、项目概述
1.1 目标
部署 ProxySQL 实现 MySQL 数据库的读写分离,提高数据库性能和可用性。
1.2 环境规划
| 角色 | 主机名 | IP地址 | 端口 | 软件版本 |
|---|---|---|---|---|
| ProxySQL 节点 | proxysql-01 | 192.168.1.100 | 6032/6033 | ProxySQL 2.5.x |
| MySQL 主库 | mysql-master | 192.168.1.101 | 3306 | MySQL 8.0 |
| MySQL 从库1 | mysql-slave1 | 192.168.1.102 | 3306 | MySQL 8.0 |
| MySQL 从库2 | mysql-slave2 | 192.168.1.103 | 3306 | MySQL 8.0 |
| 应用服务器 | app-server | 192.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 回滚步骤
- 修改应用配置,直连 MySQL 主库
- 停止 ProxySQL 服务
- 验证应用功能正常
- 分析问题原因
部署完成标志:
- 所有测试用例通过
- 性能测试结果符合预期
- 监控系统正常运行
- 文档完整交付
负责人签字:____________
完成日期:____年__月__日
更多推荐



所有评论(0)