主从复制和读写分离
主从复制 核心原理
必须记住 3 个核心组件
- binlog(二进制日志) ------ 主库
- IO 线程 ------ 从库
- SQL 线程 ------ 从库
完整流程 - 主库执行增删改(insert/update/drop)
- MySQL 把这些操作记录到 binlog 日志文件
- 从库的 IO 线程连接主库,请求 binlog
- 主库的 dump 线程把 binlog 发给从库
- 从库 IO 线程将内容写到本地 relay log(中继日志)
- 从库 SQL 线程读取中继日志,重放 SQL 语句
- 两个 Yes 代表正常:IO_Running、SQL_Running
- 从库数据和主库完全一致

计划拓扑图


配置时间同步
通过时间戳实现业务的一致性
bash
# 所有节点
ntpdate ntp.aliyun.com
date -R
systemctl disable firewalld --now
setenforce 0
mysql主从服务器配置
mysql主服务器配置
bash
[root@mysql-master ~]# vim /etc/my.cnf
server-id = 11
log-bin = master-bin #主服务器日志文件
log-slave-updates = true #从服务器更新二进制日志
[root@mysql-master ~]# systemctl restart mysqld
##登陆mysql
[root@mysql-master ~]# mysql -u root -p
mysql> GRANT REPLICATION SLAVE ON *.* TO 'myslave'@'192.168.108.%' IDENTIFIED BY
'123456';
Query OK, 0 rows affected, 1 warning (0.01 sec)
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)
mysql> show master status;
+-------------------+----------+--------------+------------------+---------------
----+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+---------------
----+
| master-bin.000001 | 604 | | |
|
+-------------------+----------+--------------+------------------+---------------
----+
1 row in set (0.00 sec)
#检查有没有master-bin.000001
[root@mysql-master ~]# ls /usr/local/mysql/data
auto.cnf ibdata1 ib_logfile1 master-bin.000001 mysql
sys ib_buffer_pool ib_logfile0 ibtmp1 master-bin.index performance_schema
mysql从服务器配置
mysql-slave01,mysql-slave02都要做如下操作
bash
#主从是克隆的要做这个操作,否则UUID一致
[root@mysql-slave01 ~]# systemctl stop mysqld
[root@mysql-slave01 ~]# rm -f /usr/local/mysql/data/auto.cnf
[root@mysql-slave01 ~]# systemctl start mysqld
[root@mysql-slave01 ~]# vim /etc/my.cnf
server-id = 22 #另外一台为23
relay-log = relay-log-bin #从主服务器上同步日志文件记录到本地
relay-log-index = slave-relay-bin.index #定义relay-log的位置和名称
[root@mysql-slave01 ~]# systemctl restart mysqld
[root@mysql-slave01 ~]# mysql -u root -p
Enter password:
mysql> change master to master_host='192.168.108.101',master_user='myslave',master_password='123456',master_log_file='master-bin.000001',master_log_pos=604;
#master_log_file,master_log_pos与前面查询的相同
Query OK, 0 rows affected, 2 warnings (0.03 sec)
mysql> start slave;
Query OK, 0 rows affected (0.02 sec)
mysql> show slave status\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.108.101
Master_User: myslave
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: master-bin.000001
Read_Master_Log_Pos: 604
Relay_Log_File: relay-log-bin.000002
Relay_Log_Pos: 321
Relay_Master_Log_File: master-bin.000001
Slave_IO_Running: Yes #Yes
Slave_SQL_Running: Yes #Yes 确保有2个yes
Replicate_Do_DB:
Replicate_Ignore_DB:
...
slave02节点也是一样操作
##注意配置文件修改,其他一样
[root@mysql-slave02 ~]# vim /etc/my.cnf
server-id = 23
relay-log = relay-log-bin
relay-log-index = slave-relay-bin.index
验证主从同步
bash
# 主服务器上:
[root@mysql-master ~]# mysql -uroot -phuawei
mysql> create database school;
Query OK, 1 row affected (0.01 sec)
mysql> use school;
mysql> CREATE TABLE student (
id int UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
age tinyint UNSIGNED,
#height DECIMAL(5,2),
gender ENUM('M','F') default 'M'
)ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4;
mysql> insert student (name,age)values('路飞',20);
# 去从服务器上 show databases;
[root@mysql-slave01 ~]# mysql -uroot -phuawei
mysql> select * from school.student;
+----+--------+------+--------+
| id | name | age | gender |
+----+--------+------+--------+
| 10 | 路飞 | 20 | M |
+----+--------+------+--------+
1 row in set (0.00 sec)
amoeba服务器
读写分离 核心原理
基于 主从复制:
- 主库写入数据 → 自动同步到从库
- 应用程序收到 SQL:
insert/update/delete → 走主库
select → 走从库
Amoeba 是一款基于 Java 开发的 MySQL 数据库代理中间件,核心定位是SQL 路由
1.应用 ↔ Amoeba ↔ MySQL 主从集群,Amoeba 是中间 "SQL 路由器"
2.核心能力:自动识别 SQL 类型,写(INSERT/UPDATE/DELETE)走主库,读(SELECT)走从库,并对从库做负载均衡。
3.依赖前提:必须先搭好 MySQL 主从复制,Amoeba 只负责路由,不负责数据同步
工作流程(读写分离)
- 应用连接 Amoeba 代理(默认端口 8066),而非直接连 MySQL。
- Amoeba 解析 SQL:
写操作 → 转发到配置的 主库池(writePool)。
读操作 → 转发到配置的 从库池(readPool),并按轮询 / 权重做负载均衡。 - 主从复制保证从库数据与主库一致,应用无感知。
核心组件(配置文件)
amoeba.xml 代理核心配置:端口、认证、读写池、路由规则、线程池
dbServers.xml 定义后端 MySQL 节点(主 / 从)、连接信息、连接池
bash
#普通linux克隆得出amoeba
[root@amoeba ~]# hostnamectl set-hostname amoeba
[root@amoeba ~]# systemctl stop firewalld.service
[root@amoeba ~]# setenforce 0
[root@amoeba ~]# chmod +x jdk-6u14-linux-x64.bin
[root@amoeba ~]# ./jdk-6u14-linux-x64.bin
#到yes的时候,输入yes按enter
[root@amoeba ~]# mv jdk1.6.0_14/ /usr/local/jdk1.6
[root@amoeba ~]# vim /etc/profile
最下面加
export JAVA_HOME=/usr/local/jdk1.6 #java家目录
export CLASSPATH=$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib #类环境和jre
export PATH=$JAVA_HOME/lib:$JAVA_HOME/jre/bin/:$PATH:$HOME/bin
export AMOEBA_HOME=/usr/local/amoeba #指定amoeba路径
export PATH=$PATH:$AMOEBA_HOME/bin
[root@amoeba ~]# source /etc/profile
[root@amoeba ~]# mkdir /usr/local/amoeba
[root@amoeba ~]# tar zxvf amoeba-mysql-binary-2.2.0.tar.gz -C /usr/local/amoeba/
[root@amoeba ~]# chmod -R 755 /usr/local/amoeba/
#执行结果显示amoeba start|stop说明安装成功
[root@amoeba ~]# /usr/local/amoeba/bin/amoeba
amoeba start|stop
在三台mysql上添加权限开放给amoeba访问
bash
#amooba访问数据库的账号
#mysql-master
mysql> grant all on *.* to test@'192.168.108.%' identified by '123.com';
#mysql-slave01
mysql> grant all on *.* to test@'192.168.108.%' identified by '123.com';
#mysql-slave02
mysql> grant all on *.* to test@'192.168.108.%' identified by '123.com';
回到amoeba服务器
bash
[root@amoeba ~]# cd /usr/local/amoeba/
[root@amoeba amoeba]# vim conf/amoeba.xml
---30行--
<property name="user">amoeba</property #客户端访问amoeba账号
----32行---------
<property name="password">123456</property> #客户端访问ameoba密码
---117和120-去掉注释115行 <property name="defaultPool">master</property>
116
117
118行<property name="writePool">master</property>
119行<property name="readPool">slaves</property>
120
####################################################################################
[root@amoeba amoeba]# vim conf/dbServers.xml #数据库配置
---23--注意!!!(mysql5.7,默认没有test数据库所以需要修改为mysql数据库)-(mysql5.5直接忽
略)--
<!-- mysql schema -->
<property name="schema">mysql</property>
--25行到30行,第30行-->移动到28行后面
25 <!-- mysql user -->
26 <property name="user">test</property>
27
28 <!-- mysql password -->
29 <property name="password">123.com</property>
30
-----45到50行主服务器地址---
45行<dbServer name="master" parent="abstractServer">
48行<property name="ipAddress">192.168.108.101</property>
--52到57行从服务器主机名52行
<dbServer name="slave1" parent="abstractServer">
<property name="ipAddress">192.168.108.102</property>
---52到57行复制一份在58行后面
<dbServer name="slave2" parent="abstractServer">
<property name="ipAddress">192.168.108.103</property>
---仅跟在上面的配置后面,multiPool行(本来就有,修改)
<dbServer name="slaves" virtual="true">
<poolConfig class="com.meidusa.amoeba.server.MultipleServerPool #不改
<property name="poolNames">slave1,slave2</property>
</poolConfig> #不改
[root@amoeba ~]# /usr/local/amoeba/bin/amoeba start&
[1] 33499
[root@amoeba ~]# netstat -anpt | grep java
tcp6 0 0 :::8066 :::* LISTEN
33499/java
tcp6 0 0 127.0.0.1:21128 :::* LISTEN
33499/java
tcp6 0 0 192.168.108.110:41754 192.168.108.101:3306 ESTABLISHED
33499/java
tcp6 0 0 192.168.108.110:41722 192.168.108.102:3306 ESTABLISHED
33499/java
tcp6 0 0 192.168.108.110:36956 192.168.108.103:3306 ESTABLISHED
33499/java
测试客户端
bash
[root@mysql-client ~]# yum install -y mysql
[root@mysql-client ~]# mysql -u amoeba -p123456 -h 192.168.108.110 -P8066
#连接amoeba服务器,8086端口在amoeba上执行netstat -anpt|grep java看

在MASTER上
bash
[root@mysql-master ~]# mysql -u root -p
Enter password:
mysql> use school;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> insert student (name,age)values('鸣人',20);
Query OK, 1 row affected (0.00 sec)
#此时会同步
#mysql-slave01
mysql> select * from student;
+----+--------+------+--------+
| id | name | age | gender |
+----+--------+------+--------+
| 10 | 路飞 | 20 | M |
| 11 | 鸣人 | 20 | M |
+----+--------+------+--------+
2 rows in set (0.00 sec)
#mysql-slave02
mysql> select * from student;
+----+--------+------+--------+
| id | name | age | gender |
+----+--------+------+--------+
| 10 | 路飞 | 20 | M |
| 11 | 鸣人 | 20 | M |
+----+--------+------+--------+
2 rows in set (0.00 sec)
在两台从上关闭,客户端上插入数据,内容不会同步
bash
# mysql-slave01
mysql> stop slave;
# mysql-slave02
mysql> stop slave;
#mysql-client上添加,由于不会同步,只有mysql-master192.168.108.101节点有该记录
# mysql-client
MySQL [school]> insert student (name,age)values('卡卡西',30);
#mysql-slave01
mysql> use school;
mysql> insert student (name,age)values('卡卡西',31);
# mysql-slave02
mysql> use school;
mysql> insert student (name,age)values('卡卡西',32);
验证主从复制
在mysql-slave01和mysql-slave02上查看
bash
# mysql-slave01
mysql> select * from student;
+----+-----------+------+--------+
| id | name | age | gender |
+----+-----------+------+--------+
| 10 | 路飞 | 20 | M |
| 11 | 鸣人 | 20 | M |
| 12 | 卡卡西 | 31 | M |
+----+-----------+------+--------+
3 rows in set (0.00 sec)
#mysql-slave02
mysql> select * from student;
+----+-----------+------+--------+
| id | name | age | gender |
+----+-----------+------+--------+
| 10 | 路飞 | 20 | M |
| 11 | 鸣人 | 20 | M |
| 12 | 卡卡西 | 32| M |
+----+-----------+------+--------+
3 rows in set (0.00 sec)
并没有将客户端写入的insert student (name,age)values('卡卡西',30);同步
bash
# mysql-master
mysql> select * from student;
+----+-----------+------+--------+
| id | name | age | gender |
+----+-----------+------+--------+
| 10 | 路飞 | 20 | M |
| 11 | 鸣人 | 20 | M |
| 12 | 卡卡西 | 30 | M |
+----+-----------+------+--------+
3 rows in set (0.00 sec)
验证读写分离
在客户端上测试,第一次会向从服务器1读数,据-第二次会向从2读取
bash
#mysql-client
MySQL [(none)]> select * from school.student;
+----+-----------+------+--------+
| id | name | age | gender |
+----+-----------+------+--------+
| 10 | 路飞 | 20 | M |
| 11 | 鸣人 | 20 | M |
| 12 | 卡卡西 | 31 | M |
+----+-----------+------+--------+
3 rows in set (0.02 sec)
MySQL [(none)]> select * from school.student;
+----+-----------+------+--------+
| id | name | age | gender |
+----+-----------+------+--------+
| 10 | 路飞 | 20 | M |
| 11 | 鸣人 | 20 | M |
| 12 | 卡卡西 | 32 | M |
+----+-----------+------+--------+
3 rows in set (0.00 sec)
#都是从从节点读取的,读写分离,由实验结果可知:客户端的读取内容会从mysql-slave01和mysqlslave02上轮询得到。