一、主从复制概述
1.1、概述
主从复制是指将主数据库的 DDL 和 DML 操作通过二进制日志传到从库服务器中,然后在从库上对这些日志重新执行(也叫重做),从而使得从库和主库的数据保持同步。
MySQL支持一台主库同时向多台从库进行复制, 从库同时也可以作为其他从服务器的主库,实现链状复制。
1.2、原理
MySQL主从复制的核心就是 二进制日志,具体的过程如下:
复制三步骤
步骤1: Master 将写操作记录到二进制日志( binlog )。
步骤2: Slave 将 Master 的binary log events拷贝到它的中继日志( relay log );
步骤3: Slave 重做中继日志中的事件,将改变应用到自己的数据库中。 MySQL复制是异步的且串行化的,而且重启后从 接入点 开始复制。
搭建主从复制的作用:
第1个作用:读写分离。
第2个作用就是数据备份。
第3个作用是具有高可用性。
复制的问题
复制的最大问题: 延时
1.3、二进制日志
二进制日志(BINLOG)记录了所有的 DDL(数据定义语言)语句和 DML(数据操纵语言)语句,但不包括数据查询(SELECT、SHOW)语句。
作用:①. 灾难时的数据恢复;②. MySQL的主从复制。在MySQL8版本中,默认二进制日志是开启着的,涉及到的参数如下:
show variables like '%log_bin%';
1.4、中继日志(relay log)
中继日志只在主从服务器架构的从服务器上存在。从服务器为了与主服务器保持一致,要从主服务器读取二进制日志的内容,并且把读取到的信息写入本地的日志文件中,这个从服务器本地的日志文件就叫中继日志。然后,从服务器读取中继日志,并根据中继日志的内容对从服务器的数据进行更新,完成主从服务器的数据同步。
搭建好主从服务器之后,中继日志默认会保存在从服务器的数据目录下。文件名的格式是: 从服务器名 -relay-bin.序号。中继日志还有一个索引文件: 从服务器名 -relaybin.index ,用来定位当前正在使用的中继日志。
1.5、关于MySQL中的日志
我们来看看官网:
https://dev.mysql.com/doc/refman/8.0/en/server-logs.html
MySQL有不同类型的日志文件,用来存储不同类型的日志,分为二进制日志、错误日志、通用查询日志和慢查询日志,这也是常用的4种。MySQL 8又新增两种支持的日志: 中继日志和数据定义语句日志。使用这些日志文件,可以查看MySQL内部发生的事情。这6类日志分别为:
- 慢查询日志:记录所有执行时间超过long_query_time的所有查询,方便我们对查询进行优化。
- 通用查询日志:记录所有连接的起始时间和终止时间,以及连接发送给数据库服务器的所有指令,对我们复原操作的实际场景、发现问题,甚至是对数据库操作的审计都有很大的帮助。
- 错误日志:记录MySQL服务的启动、运行或停止MySQL服务时出现的问题,方便我们了解服务器的状态,从而对服务器进行维护。
- 二进制日志:记录所有更改数据的语句,可以用于主从服务器之间的数据同步,以及服务器遇到故障时数据的无损失恢复。
- 中继日志:用于主从服务器架构中,从服务器用来存放主服务器二进制日志内容的一个中间文件。从服务器通过读取中继日志的内容,来同步主服务器上的操作。
- 数据定义语句日志:记录数据定义语句执行的元数据操作。
除二进制日志外,其他日志都是文本文件。默认情况下,所有日志创建于MySQL数据目录中。
二、一主一从架构
2.1、环境准备
两台已经已经安装好MySQL:8.0.25的Linux服务器。注意,我们为了避免不必要的一些麻烦,请将主从服务器数据库的版本保持一致
修改UUID,不然在某一一些的场景下会报错:
vim /var/lib/mysql/auto.cnf
systemctl restart mysqld
两台服务器都已经关闭掉了防火墙:
systemctl status firewalld.service
2.2、主机的配置文件
vim /etc/my.cnf
主从所有配置项都配置在 [mysqld] 节点下,且都是小写字母。具体参数配置如下:
#[必须]主服务器唯一ID
server-id=1
#[必须]启用二进制日志,指名路径。比如:自己本地的路径/log/mysqlbin
log-bin=master-bin
2.3、从机配置文件
vim /etc/my.cnf
server-id=2
2.4、重启主从服务器
systemctl restart mysqld
2.5、主机:建立账户并授权
CREATE USER 'slave1'@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE ON *.* TO 'slave1'@'%';
ALTER USER 'slave1'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
flush privileges;
查询Master的状态,并记录下File和Position的值。
show master status;
+-------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+-------------------+
| master-bin.000001 | 1139 | | | |
+-------------------+----------+--------------+------------------+-------------------+
注意:执行完此步骤后不要再操作主服务器MySQL
,防止主服务器状态值变化。
2.6、从机:配置需要复制的主机
步骤1:从机上复制主机的命令
CHANGE MASTER TO
MASTER_HOST='主机的IP地址',
MASTER_USER='主机用户名',
MASTER_PASSWORD='主机用户名的密码',
MASTER_LOG_FILE='mysql-bin.具体数字',
MASTER_LOG_POS=具体值;
CHANGE MASTER TO
MASTER_HOST='192.168.200.129',
MASTER_USER='slave1',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='master-bin.000001',
MASTER_LOG_POS=1139;
CHANGE MASTER TO MASTER_HOST='192.168.200.129', MASTER_USER='slave1', MASTER_PASSWORD='123456', MASTER_LOG_FILE='master-bin.000001', MASTER_LOG_POS=1139;
步骤2:启动slave同步
START SLAVE;
查看同步状态:
SHOW SLAVE STATUS\G;
上面两个参数都是Yes,则说明主从配置成功!
2.7、测试
主机新建库、新建表、insert记录,从机复制:
CREATE DATABASE master_slave;
use master_slave;
CREATE TABLE mytbl(id INT,NAME VARCHAR(16));
INSERT INTO mytbl VALUES(1, 'zhang3');
INSERT INTO mytbl VALUES(2,'lisi');
从机测试:
use master_slave;
select * from mytbl;
三、读写分离
介绍:读写分离,简单地说是把对数据库的读和写操作分开,以对应不同的数据库服务器。主数据库提供写操作,从数据库提供读操作,这样能有效地减轻单台数据库的压力。
在主从架构的配置中,如果想要采取读写分离的策略,我们可以 自己编写程序 ,也可以通过 第三方的中间件
来实现。
-
自己编写程序的好处就在于比较自主,我们可以自己判断哪些查询在从库上来执行,针对实时性要求高的需求,我们还可以考虑哪些查询可以在主库上执行。同时,程序直接连接数据库,减少了中间件层,相当于减少了性能损耗。
-
采用中间件的方法有很明显的优势, 功能强大 , 使用简单 。但因为在客户端和数据库之间增加了中间件层会有一些 性能损耗 ,同时商业中间件也是有使用成本的。我们也可以考虑采取一些优秀的开源工具。
四、实现一主一从读写分离
CREATE TABLE `t_user` (
`user_id` int NOT NULL COMMENT '用户id',
`user_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '用户名称',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
创建实体类:
package cn.rwdemo.entity;
import lombok.Data;
@Data
public class UserEntity {
private Integer userId;
private String userName;
}
创建Mapper类:
package cn.rwdemo.mapper;
import cn.rwdemo.entity.UserEntity;
import org.apache.ibatis.annotations.*;
@Mapper
public interface UserMapper {
@Insert("insert into t_user(user_id,user_name) values(#{userId},#{userName})")
void insertUserInfo(UserEntity userEntity);
@Select("select * from t_user where user_id=#{userId}")
@Results({
@Result(property = "userId", column = "user_id"),
@Result(property = "userName", column = "user_name")
})
UserEntity getUserInfo(Integer userId);
}
创建配置文件:
spring:
application:
name: rw_demo
shardingsphere:
datasource:
master:
driver-class-name: com.mysql.cj.jdbc.Driver
password: 123456@root
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://192.168.200.129:3306/master_slave?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false
username: root
names: master,slave
slave:
driver-class-name: com.mysql.cj.jdbc.Driver
password: 123456@root
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://192.168.200.130:3306/master_slave?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false
username: root
masterslave:
master-data-source-name: master
name: ms
slave-data-source-names: slave
props:
sql:
show: true
测试:
package cn.rwdemo;
import cn.rwdemo.entity.UserEntity;
import cn.rwdemo.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
@SpringBootTest
public class UserTests {
@Resource
private UserMapper userMapper;
@Test
public void insertUserInfo() {
for (int i = 1; i <= 10; i++) {
UserEntity userEntity = new UserEntity();
userEntity.setUserId(i);
userEntity.setUserName("user" + i);
userMapper.insertUserInfo(userEntity);
}
}
@Test
public void getUserInfo() {
System.out.println(userMapper.getUserInfo(1));
}
}
五、开发环境说明
VM虚拟机:
cat /etc/redhat-release
cat /proc/version
查看防火墙状态:
systemctl status firewalld.service
关闭命令:
systemctl stop firewalld.service
开机禁用防火墙自启命令 :
systemctl disable firewalld.service
是否有mysql残留
rpm -qa | grep -i mysql
systemctl status mysqld.service
代码:
https://github.com/shixiaochuangjob/markdownfile/tree/main/20240821