前言
MySQL主从复制和读写分离是提高数据库性能和可靠性的重要技术手段。通过主从复制,可以将主数据库的更新实时同步到从数据库,实现数据备份、故障恢复和负载均衡;而读写分离则可以将读操作分发到从数据库,减轻主数据库的读压力,提高整体性能。这两种技术结合使用能够有效提升数据库的吞吐量和容错能力,提供更稳定和高效的数据库服务。
目录
一、主从复制
1. 概述
MySQL 主从复制是一种数据复制技术,其中一个数据库服务器(称为主服务器)的更改会被自动地传播到一个或多个其他数据库服务器(称为从服务器)。
2. 原理架构
2.1 环境模型
2.2 原理过程
① 当 master 数据更新后会将完整的 sql 语句记录在二进制日志文件 mysql-bin-* 中
② master dump 进程正常处于休眠状态,不定期按需检测二进制日志,发现日志有更新,会通知 slave i/o 线程
③ slave i/o 线程向 master dump 进程进行数据请求,得到回复后,将二进制日志文件复制到本地,保存为中继日志
④ slave 基于中继日志进行读取恢复操作,sql 线程将执行的sql语句同步到自己的数据库中,从而达到与 master 数据的一致性
3. 核心块
主从复制的过程主要有两个核心:
① 两个日志
- 二进制日志:记录了对数据库的所有更改操作,包括插入、更新、删除等
- 中继日志:记录了从主服务器上读取的二进制日志的内容
② 三个线程
- dump thread:将主服务器上的数据变更记录到二进制日志文件中。这些记录包含了对数据库的所有更改,以便在从服务器上进行复制;其作用可细分为:监听 i/o 线程请求、将二进制日志更新的数据发送给 slave 的 i/o 线程
- i/o thread:负责从主服务器获取二进制日志事件,并将其复制到从服务器上的中继日志文件中
- sql thread:负责读取中继日志文件中的更新信息,并在从服务器上执行这些更新,以确保从服务器上的数据与主服务器保持同步
4. 主从复制的疑问
① 为什么要复制:保证数据的完整性
② 谁复制谁:从(slave)角色复制主(master)数据
③ 数据存放在哪:二进制日志文件 mysql-bin-* 中
5. 主从复制延迟
① master服务器高并发,形成大量事务
② 网络延迟
③ 主从硬件设备导致:cpu主频、内存io、硬盘io
④ 本来就不是同步复制、而是异步复制
6. 实现主从复制
环境准备:所有设备均关闭防火墙与核心防护
Master 服务器:192.168.190.100,mysql5.7
Slave1 服务器:192.168.190.101,mysql5.7
Slave2 服务器:192.168.190.102,mysql5.7
6.1 Master 服务器配置
① 安装 ntp 网络时间协议软件,并配置系统时间同步
[root@master ~]# yum install ntp -y
[root@master ~]# vim /etc/ntp.conf
末尾添加:
server 127.127.190.0 # 设置本地是时钟源
fudge 127.127.190.0 stratum 8 # 设置时间层级为8(限制在15内)
[root@master ~]# systemctl start ntpd # 或者 service ntpd start
② 编辑 mysql 核心配置
[root@master ~]# vim /etc/my.cnf
[mysqld]
server-id = 1 # 主从复制mysql服务器唯一标示
log-bin=master-bin # 主服务器开启二进制日志
binlog_format = MIXED # 设置binlog的格式为MIXED(STATEMENT+ROW)
log-slave-updates=true # 允许slave从master复制数据时可以写入到自己的二进制日志
[root@master ~]# systemctl restart mysqld.service
③ 创建复制用户,并给从服务器授权
[root@master ~]# mysql -u root -p'123456'
mysql> grant replication slave on *.* to 'fql'@'192.168.190.%' identified by '123456';
# 授予fql用户在所有数据库上允许从IP地址以‘192.168.190段连接给与复制从库的权限,使用密码‘123456’进行身份验证
mysql> flush privileges;
# 用于重新加载授权表
mysql> show master status; # 查看master状态
+-------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+-------------------+
| master-bin.000001 | 600 | | | |
+-------------------+----------+--------------+------------------+-------------------+
# File 列显示日志名,Position 列显示偏移量
6.2 Slave1、Slave2 服务器配置
① 安装 ntp 网络时间协议软件 ntpdate 工具,并与 Master 同步时间
[root@slvae1 ~]# yum install ntpdate -y
[root@slvae1 ~]# service ntpd start # 启动 ntpd 服务
Redirecting to /bin/systemctl start ntpd.service
[root@slvae1 ~]# whereis ntpdate
ntpdate: /usr/sbin/ntpdate /usr/share/man/man8/ntpdate.8.gz
[root@slvae1 ~]# /usr/sbin/ntpdate 192.168.190.100 # 与 master 同步时间
28 Mar 15:16:57 ntpdate[5329]: the NTP socket is in use, exiting
[root@slave2 ~]# yum instll ntpdate -y
[root@slave2 ~]# service ntpd start
Redirecting to /bin/systemctl start ntpd.service
[root@slave2 ~]# /usr/sbin/ntpdate 192.168.190.100
28 Mar 15:17:07 ntpdate[39238]: the NTP socket is in use, exiting
② 配置计划任务
[root@slvae1 ~]# crontab -e # 编辑cron表,新建计划
*/30 * * * * /usr/sbin/ntpdate 192.168.190.100 # 每隔半小时与 master 同步时间
[root@slave2 ~]# crontab -e
*/30 * * * * /usr/sbin/ntpdate 192.168.190.100
③ 分别编辑 mysql 核心配置
[root@slvae1 ~]# vim /etc/my.cnf
[mysqld]
server-id = 2 # id唯一
relay-log=relay-log-bin # 开启中继日志,从主服务器上同步日志文件记录到本地
relay-log-index=slave-relay-bin.index # 定义中继日志文件的位置和名称,一般和relay-log在同一目录
relay_log_recovery = 1 # 开启,当slave从库宕机后,假如relay-log损坏了,导致一部分中继日志没有处理,则自动放弃所有未执行的relay-log,并且重新从master上获取日志
[root@slvae1 ~]# systemctl restart mysqld.service
[root@slave2 ~]# vim /etc/my.cnf
[mysqld]
server-id = 3
relay-log=relay-log-bin
relay-log-index=slave-relay-bin.index
relay_log_recovery = 1
[root@slave2 ~]# systemctl restart mysqld.service
④ 更改主从同步(复制)的配置
slvae1、slvae2:
[root@slvae1 ~]# mysql -u root -p'123456'
mysql> change master to master_host='192.168.190.100',master_user='fql',master_password='123456',master_log_file='master-bin.000001',master_log_pos=600;
# master_host='192.168.190.100':这是主服务器的IP地址或主机名
# master_user='fql':这是用于主从复制的用户名
# master_password='123456':这是用于主从复制的密码
# master_log_file='master-bin.000001':这是主服务器上用于记录二进制日志的文件名
# master_log_pos=600:这是主服务器上用于记录二进制日志的位置
mysql> start slave; # 启动同步,如有报错执行 reset slave;
mysql> show slave status\G;
Slave_IO_Running: Yes # 确保IO和SQL线程都是Yes,代表同步正常
Slave_SQL_Running: Yes
6.3 结果验证
① 主服务器上进入执行数据库操作更新
mysql> create database fang; # 新建库
② 从服务器查看
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| fang | # 查询到新增库
| mysql |
| performance_schema |
| sys |
+--------------------+
二、读写分离
1. 概述
MySQL读写分离是一种常见的数据库优化策略,它通过将读和写操作分配给不同的数据库服务器来提高系统的性能和可伸缩性。在这种架构中,主服务器负责处理所有的写操作,而多个从服务器则负责处理读操作。
2. 原理过程
① 当客户端应用程序发送读操作(如select)到 MySQL 数据库时,这些操作会被发送到负载均衡器 Amoeba
② 负载均衡器会根据从服务器的负载情况,将读操作分配给一个或多个 slave 服务器;将写操作分配给 master 服务器
③ 从服务器接收到读操作后,会在自己的数据库上执行这些操作,并将操作的结果返回给客户端应用程序
3. 实现读写分离
环境准备:所有设备均关闭防火墙与核心防护
Master 服务器:192.168.190.100,mysql5.7
Slave1 服务器:192.168.190.101,mysql5.7
Slave2 服务器:192.168.190.102,mysql5.7
Amoeba 服务器:192.168.190.103,jdk1.6、Amoeba
客户端 服务器:192.168.190.104,mysql
3.1 Amoeba 服务器配置
① 安装 Java 环境
Amoeba 基于是 jdk1.5 开发的,所以官方推荐使用 jdk1.5 或 1.6 版本,高版本不建议使用。
[root@amoeba opt]# ls
amoeba-mysql-binary-2.2.0.tar.gz jdk-6u14-linux-x64.bin rh
[root@amoeba opt]# chmod +x jdk-6u14-linux-x64.bin
[root@amoeba opt]# ./jdk-6u14-linux-x64.bin # 按空格键,输入yes,按enter,最后弹窗点否
[root@amoeba opt]# mv jdk1.6.0_14/ /usr/local/jdk1.6
[root@amoeba opt]# vim /etc/profile # 配置环境变量
export JAVA_HOME=/usr/local/jdk1.6
export CLASSPATH=$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib
export PATH=$JAVA_HOME/lib:$JAVA_HOME/jre/bin/:$PATH:$HOME/bin
export AMOEBA_HOME=/usr/local/amoeba
export PATH=$PATH:$AMOEBA_HOME/bin
[root@amoeba opt]# source /etc/profile # 生效配置
[root@amoeba opt]# java -version # 查看当前系统中Java的版本
java version "1.6.0_14"
Java(TM) SE Runtime Environment (build 1.6.0_14-b08)
Java HotSpot(TM) 64-Bit Server VM (build 14.0-b16, mixed mode)
② 安装 Amoeba软件
[root@amoeba opt]# mkdir /usr/local/amoeba
[root@amoeba opt]# tar zxvf amoeba-mysql-binary-2.2.0.tar.gz -C /usr/local/amoeba/
[root@amoeba opt]# chmod -R 755 /usr/local/amoeba/
[root@amoeba opt]# /usr/local/amoeba/bin/amoeba
amoeba start|stop
③ 配置 Amoeba 读写分离,两个 Slave 读负载均衡
先在Master、Slave1、Slave2 的mysql上开放权限给 Amoeba 访问
mysql> grant all on *.* to 'fang'@'192.168.190.%' identified by '123456';
④ 配置 Amoeba 服务
[root@amoeba opt]# cd /usr/local/amoeba/conf/
[root@amoeba conf]# cp amoeba.xml amoeba.xml.bak
30 <property name="user">amoeba</property>
32 <property name="password">123456</property>
115 <property name="defaultPool">master</property>
117 <!-- --> # 去掉注释
118 <property name="writePool">master</property>
119 <property name="readPool">slaves</property>
⑤ 修改数据库配置文件
[root@amoeba conf]# cp dbServers.xml{,bak}
[root@amoeba conf]# vim dbServers.xml
22 <!-- mysql schema
23 <property name="schema">test</property> # 注释
24 -->
26 <property name="user">fang</property> # 修改用户名
28 <!-- mysql password -->
29 <property name="password">123456</property> # 去掉注释,修改密码
45 <dbServer name="master" parent="abstractServer"> # 设置主服务器的名master
48 <property name="ipAddress">192.168.190.100</property> # 设置主服务器的地址
52 <dbServer name="slave1" parent="abstractServer"> # 设置从服务器的名slave1
55 <property name="ipAddress">192.168.190.101</property> # 设置从服务器1的地址
# 复制6行
58 <dbServer name="slave2" parent="abstractServer"> # 设置从服务器的名slave2
61 <property name="ipAddress">192.168.190.102</property> # 设置从服务器2的地址
65 <dbServer name="slaves" virtual="true">
71 <property name="poolNames">slave1,slave2</property>
⑥ 启动 Amoeba 软件
[root@amoeba conf]# /usr/local/amoeba/bin/amoeba start & # 后台启动Amoeba软件
[root@amoeba conf]# netstat -anpt | grep java # 查看8066端口是否开启,默认端口为TCP 8066
tcp6 0 0 127.0.0.1:10973 :::* LISTEN 2592/java
tcp6 0 0 :::8066 :::* LISTEN 2592/java
tcp6 0 0 192.168.190.103:56068 192.168.190.100:3306 ESTABLISHED 2592/java
tcp6 0 0 192.168.190.103:40426 192.168.190.101:3306 ESTABLISHED 2592/java
tcp6 0 0 192.168.190.103:53846 192.168.190.102:3306 ESTABLISHED 2592/java
3.2 测试结果
① 在主服务器上新建表
mysql> use fang;
mysql> create table class (id int(5),name char(15),age int(3));
② 在两台从服务器上关闭同步,并分别在三台服务器上插入一条数据
slave1:
mysql> stop slave;
mysql> use fang;
mysql> insert into class values('1','slave1','10');
slave2:
mysql> stop slave;
mysql> use fang;
mysql> insert into class values('2','slave2','20');
master:
mysql> insert into class values('3','slave3','30');
③ 在客户端安装 MariaDB 数据库服务器和客户端
[root@amoeba ~]# yum install -y mariadb-server mariadb # 安装MariaDB数据库服务器和客户端
[root@amoeba ~]# systemctl start mariadb.service
[root@amoeba ~]# mysql -u amoeba -p123456 -h 192.168.190.103 -P8066
④ 客户端查看表信息
MySQL [(none)]> use fang;
MySQL [fang]> select * from class;
+------+--------+------+
| id | name | age |
+------+--------+------+
| 2 | slave2 | 20 |
+------+--------+------+
1 row in set (0.01 sec)
MySQL [fang]> select * from class;
+------+--------+------+
| id | name | age |
+------+--------+------+
| 1 | slave1 | 10 |
+------+--------+------+
1 row in set (0.01 sec)
# 轮询调取slave1、slave2的数据
⑤ 开启从服务器数据同步,客户端再次查看表信息
slave1、slave2:
mysql> start slave;
客户端:
MySQL [fang]> select * from class;
+------+--------+------+
| id | name | age |
+------+--------+------+
| 1 | slave1 | 10 |
| 3 | slave3 | 30 |
+------+--------+------+
2 rows in set (0.01 sec)
MySQL [fang]> select * from class;
+------+--------+------+
| id | name | age |
+------+--------+------+
| 2 | slave2 | 20 |
| 3 | slave3 | 30 |
+------+--------+------+
2 rows in set (0.01 sec)
# 实现同步在主服务器上添加的数据,并轮询展示