MySQL 读写分离详解与 MyCat 实战部署
MySQL 读写分离详解与 MyCat 实战部署
一、读写分离核心概念
1.1 什么是读写分离?
读写分离是基于主从复制的数据库架构优化方案,核心逻辑是:
- 主数据库(Master):专门处理写操作(INSERT、UPDATE、DELETE 等事务性操作)
- 从数据库(Slave):专门处理读操作(SELECT 查询)
- 主从数据同步:通过主从复制机制,将主库的写操作变更同步到从库,保证数据一致性
1.2 为什么需要读写分离?
数据库性能瓶颈往往出现在写操作(如批量插入、复杂更新),而读操作通常更轻量。例如:
- 写 10000 条数据可能需要 3 分钟(涉及锁表、日志写入等)
- 读 10000 条数据可能仅需 5 秒(仅需查询数据)
读写分离通过 “读写拆分” 解决:写操作的耗时不影响读操作的响应速度,从而提升整体并发能力
1.3 适用场景
并非所有系统都需要读写分离,适合的场景包括:
- 读操作远多于写操作(如电商商品详情页、新闻网站)
- 单库并发查询压力大(QPS 超过 1000)
- 对查询响应时间敏感(如秒杀系统、实时数据分析)
二、读写分离实现方式对比
实现方式 | 原理 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
程序代码内部实现 | 在代码中根据 SQL 类型(SELECT/INSERT)路由到主库或从库 | 性能好(无中间层开销)、灵活 | 需开发介入,代码侵入性强,维护成本高 | 小型应用、开发团队可控的项目 |
中间代理层实现 | 代理层(如 MyCat)接收请求,自动路由到主库(写)或从库(读) | 对应用透明(无需改代码)、集中管理 | 增加中间层开销,需维护代理服务 | 大型应用、多语言项目、运维主导场景 |
主流代理工具特性对比
代理工具 | 开发语言 | 支持事务 | 支持存储过程 | 特点 |
---|---|---|---|---|
MySQL-Proxy | C/Lua | 支持 | 支持 | 官方工具,需编写 Lua 脚本,学习成本高 |
Atlas | C | 支持 | 支持 | 360 开源,基于 MySQL-Proxy 优化,稳定性好 |
Amoeba | Java | 不支持 | 不支持 | 阿里开源,轻量易用,适合简单场景 |
MyCat | Java | 支持 | 支持 | 功能全面(分库分表 + 读写分离),社区活跃 |
推荐选择:MyCat(功能全面,适合生产环境,支持分库分表扩展)
三、MyCat 实现读写分离实战
3.1 环境说明
主机名 | IP 地址 | 角色 | 系统版本 | 预装软件 |
---|---|---|---|---|
master | 192.168.100.10 | 主数据库 | CentOS 7 | MySQL 5.7(已配置主库) |
slave | 192.168.100.20 | 从数据库 | CentOS 7 | MySQL 5.7(已配置从库) |
mycat | 192.168.100.30 | 代理服务器 | CentOS 7 | 待安装 Java、MyCat |
前置条件:
- 主从复制已配置完成(主库写操作可同步到从库)
- 三台主机已关闭防火墙(或开放 3306、8066、9066 端口)
- 关闭 SELinux(
setenforce 0
) - 时间同步(
chronyd
服务) /etc/hosts
已配置主机名解析
3.2 部署步骤
配置/etc/hosts文件:
[root@syf ~]# hostname
syf.example.com
[root@syf ~]# vim /etc/hosts
192.168.100.10 syf.example.com master
192.168.100.20 syf2.example.com slave
192.168.100.30 syf3.example.com mycat
~
[root@syf ~]# scp /etc/hosts root@slave:/etc/hosts
The authenticity of host 'slave (192.168.100.20)' can't be established.
ECDSA key fingerprint is SHA256:UN0UZbtBfFQeLR3836aFd9k4cm9na95JOPqBnPk05VU.
ECDSA key fingerprint is MD5:20:05:39:25:84:f6:1b:bb:8b:b3:ed:b9:bf:96:99:ba.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'slave,192.168.100.20' (ECDSA) to the list of known hosts.
root@slave's password:
hosts 100% 272 148.7KB/s 00:00
[root@syf ~]# scp /etc/hosts root@slave:/root/
root@slave's password:
hosts 100% 272 104.8KB/s 00:00
[root@syf ~]# scp /etc/hosts root@mycat:/etc/hosts
The authenticity of host 'mycat (192.168.100.30)' can't be established.
ECDSA key fingerprint is SHA256:UN0UZbtBfFQeLR3836aFd9k4cm9na95JOPqBnPk05VU.
ECDSA key fingerprint is MD5:20:05:39:25:84:f6:1b:bb:8b:b3:ed:b9:bf:96:99:ba.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'mycat,192.168.100.30' (ECDSA) to the list of known hosts.
root@mycat's password:
hosts 100% 272 146.9KB/s 00:00
开启时钟同步:
[root@syf ~]# vim /etc/chrony.conf
...
# Allow NTP client access from local network.
#allow 192.168.0.0/16
allow 192.168.100.0/24# Serve time even if not synchronized to a time source.
local stratum 10
...
[root@syf ~]# systemctl restart chronyd
[root@syf ~]# systemctl enable chronyd
Created symlink from /etc/systemd/system/multi-user.target.wants/chronyd.service to /usr/lib/systemd/system/chronyd.service.
[root@syf ~]# hwclock -w
[root@syf2 ~]# vim /etc/chrony.conf #server 0.centos.pool.ntp.org iburst
#server 1.centos.pool.ntp.org iburst
#server 2.centos.pool.ntp.org iburst
#server 3.centos.pool.ntp.org iburst
server master iburst[root@syf2 ~]# systemctl restart chronyd
[root@syf2 ~]# systemctl enable chronyd
Created symlink from /etc/systemd/system/multi-user.target.wants/chronyd.service to /usr/lib/systemd/system/chronyd.service.
[root@syf2 ~]# hwclock -w
[root@syf3 ~]# vim /etc/chrony.conf #server 0.centos.pool.ntp.org iburst
#server 1.centos.pool.ntp.org iburst
#server 2.centos.pool.ntp.org iburst
#server 3.centos.pool.ntp.org iburst
server master iburst[root@syf3 ~]# systemctl restart chronyd
[root@syf3 ~]# systemctl enable chronyd
Created symlink from /etc/systemd/system/multi-user.target.wants/chronyd.service to /usr/lib/systemd/system/chronyd.service.
[root@syf3 ~]# hwclock -w
[root@syf2 ~]# chronyc sources
210 Number of sources = 1
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^* syf.example.com 3 6 177 21 +519us[+6701us] +/- 42ms
[root@syf3 ~]# chronyc sources
210 Number of sources = 1
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^* syf.example.com 3 6 37 28 +170ns[+2031us] +/- 43ms
在mycat节点安装java环境------mycat是基于java的
配置yum仓库略
[root@syf3 ~]# yum -y install epel-release
[root@syf3 ~]# yum -y install java java-devel
....
查看java版本
[root@syf3 ~]# java -version
openjdk version "1.8.0_412"
OpenJDK Runtime Environment (build 1.8.0_412-b08)
OpenJDK 64-Bit Server VM (build 25.412-b08, mixed mode)
部署mycat服务,将Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz上传到mycat的/root目录下,并解压到/usr/local
[root@syf3 ~]# rz -E
rz waiting to receive.
[root@syf3 ~]# ls
anaconda-ks.cfg initial-setup-ks.cfg Public
Desktop Music Templates
Documents Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz Videos
Downloads Pictures
[root@syf3 ~]# tar -zxvf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz -C /usr/local/
.....
[root@syf3 ~]# cd /usr/local/
[root@syf3 local]# ls
bin etc games include lib lib64 libexec mycat sbin share src
定义环境变量
[root@syf3 local]# vim /etc/profile
....
export MYCAT_HOME=/usr/local/mycat
~
[root@syf3 local]# source /etc/profile
[root@syf3 local]# echo #MYCAT_HOME
/usr/local/mycat
编辑mycat服务读写分离的schema.xml,设置数据库写入节点为master,读取节点slave,注意IP要修改
[root@syf3 local]# cd mycat/
[root@syf3 mycat]# ls
bin catlet conf lib logs version.txt
[root@syf3 mycat]# cd conf/
[root@syf3 conf]# ls
autopartition-long.txt rule.xml
auto-sharding-long.txt schema.xml
auto-sharding-rang-mod.txt sequence_conf.properties
cacheservice.properties sequence_db_conf.properties
ehcache.xml sequence_distributed_conf.properties
index_to_charset.properties sequence_time_conf.properties
log4j2.xml server.xml
migrateTables.properties sharding-by-enum.txt
myid.properties wrapper.conf
partition-hash-int.txt zkconf
partition-range-mod.txt zkdownload[root@syf3 conf]# vim schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="USERDB" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1"></schema>
<dataNode name="dn1" dataHost="localhost1" database="shenyi" />
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="3" dbType="mysql" dbDriver="native" writeType="0" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat><writeHost host="hostM1" url="192.168.100.10:3306" user="root" password="redhat"> <readHost host="hostS1" url="192.168.100.20:3306" user="root" password="redhat" /> </writeHost>
</dataHost></mycat:schema>
~
编辑mycat用户的/usr/local/mycat/conf/目录下的server.xml,修改root用户的密码redhat,访问mycat的逻辑库为USERDB,注意删除后几行
[root@syf3 conf]# vim server.xml <user name="root"><property name="password">redhat</property><property name="schemas">USERDB</property><!-- 表级 DML 权限设置 --><!-- <privileges check="false"><schema name="TESTDB" dml="0110" ><table name="tb01" dml="0000"></table><table name="tb02" dml="1111"></table></schema></privileges> --></user></mycat:server>
~
启动mycat服务
[root@syf3 conf]# cd ..
[root@syf3 mycat]# ls
bin catlet conf lib logs version.txt
[root@syf3 mycat]# cd bin/
[root@syf3 bin]# ls
dataMigrate.sh mycat startup_nowrap.sh wrapper-linux-x86-32
init_zk_data.sh rehash.sh wrapper-linux-ppc-64 wrapper-linux-x86-64
[root@syf3 bin]# ./mycat start
Starting Mycat-server...
[root@syf3 bin]# ss -anlt
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:111 *:*
LISTEN 0 128 *:6000 *:*
LISTEN 0 5 192.168.122.1:53 *:*
LISTEN 0 128 *:22 *:*
LISTEN 0 128 127.0.0.1:631 *:*
LISTEN 0 100 127.0.0.1:25 *:*
LISTEN 0 128 127.0.0.1:6010 *:*
LISTEN 0 1 127.0.0.1:32000 *:*
LISTEN 0 100 :::9066 :::*
LISTEN 0 128 :::111 :::*
LISTEN 0 128 :::6000 :::*
LISTEN 0 50 :::33749 :::*
LISTEN 0 128 :::22 :::*
LISTEN 0 128 ::1:631 :::*
LISTEN 0 100 ::1:25 :::*
LISTEN 0 128 ::1:6010 :::*
LISTEN 0 50 :::1984 :::*
LISTEN 0 100 :::8066 :::*
LISTEN 0 50 :::41794 :::*
(查看8066、9066端口是否开启,如果有开放8066和9066端口,则表示mycat服务开启成功 mycat服务默认的数据端口是8066,而9066端口则是mycat管理端口,用于管理mycat的整个集群状态)
在master和slave节点分别给root数据库用户授权,因为默认情况下,数据库用户root不允许远程登录
master节点和slave节点都需要做:
mysql> select User,Host from mysql.user;
+---------------+----------------+
| User | Host |
+---------------+----------------+
| lq | 192.168.100.20 |
| mysql.session | localhost |
| mysql.sys | localhost |
| root | localhost |
+---------------+----------------+
5 rows in set (0.01 sec)mysql> grant all privileges on *.* to 'root'@'%' identified by 'red
Query OK, 0 rows affected, 1 warning (0.01 sec)mysql> grant all privileges on *.* to 'root'@'%' identified by 'redQuery OK, 0 rows affected, 1 warning (0.00 sec)mysql> select User,Host from mysql.user;
+---------------+----------------+
| User | Host |
+---------------+----------------+
| root | % |
| lq | 192.168.100.20 |
| mysql.session | localhost |
| mysql.sys | localhost |
| root | localhost |
+---------------+----------------+
6 rows in set (0.00 sec)
在mycat节点验证
在本地使用数据端口登录到mysql数据中,查询和插入数据,然后使用管理端口查看读写分离效果
[root@syf3 ~]# yum -y install mariadb-server mariadb
.....
[root@syf3 ~]# systemctl restart mariadb
[root@syf3 ~]# systemctl enable mariadb
Created symlink from /etc/systemd/system/multi-user.target.wants/mariadb.service to /usr/lib/systemd/system/mariadb.service.
[root@syf3 ~]# mysql -uroot -predhat -P8066 -h127.0.0.1
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.6.29-mycat-1.6-RELEASE-20161028204710 MyCat Server (OpenCloundDB)Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.MySQL [(none)]> show databases;
+----------+
| DATABASE |
+----------+
| USERDB |
+----------+
1 row in set (0.00 sec)MySQL [(none)]> use USERDB;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -ADatabase changed
MySQL [USERDB]> show tables;
+------------------+
| Tables_in_shenyi |
+------------------+
| xs |
+------------------+
1 row in set (0.00 sec)MySQL [USERDB]> select * from xs;
+------+---------+
| id | name |
+------+---------+
| 1 | zs |
| 2 | lisi |
| 3 | wangwu |
| 4 | zhaoliu |
| 5 | sy |
+------+---------+
5 rows in set (0.05 sec)MySQL [USERDB]> insert into xs values(6,'shenyi');
Query OK, 1 row affected (0.02 sec)MySQL [USERDB]> select * from xs;
+------+---------+
| id | name |
+------+---------+
| 1 | zs |
| 2 | lisi |
| 3 | wangwu |
| 4 | zhaoliu |
| 5 | sy |
| 6 | shenyi |
+------+---------+
6 rows in set (0.00 sec)
通过管理接口9066来查看读写分离的情况,看写入的次数和查看的次数,依次做测试
[root@syf3 ~]# mysql -uroot -predhat -P9066 -h127.0.0.1
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.6.29-mycat-1.6-RELEASE-20161028204710 MyCat Server (monitor)Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.MySQL [(none)]> show @@datasource;
+----------+--------+-------+----------------+------+------+--------+------+------+---------+-----------+------------+
| DATANODE | NAME | TYPE | HOST | PORT | W/R | ACTIVE | IDLE | SIZE | EXECUTE | READ_LOAD | WRITE_LOAD |
+----------+--------+-------+----------------+------+------+--------+------+------+---------+-----------+------------+
| dn1 | hostM1 | mysql | 192.168.100.10 | 3306 | W | 0 | 7 | 1000 | 94 | 0 | 2 |
| dn1 | hostS1 | mysql | 192.168.100.20 | 3306 | R | 0 | 7 | 1000 | 86 | 4 | 0 |
+----------+--------+-------+----------------+------+------+--------+------+------+---------+-----------+------------+
2 rows in set (0.00 sec)