Mysql的主从复制和读写分离

一.主从复制的原理

1.Mysql支持的复制类型

主( master )数据库启动 bin 二进制日志,这样会有一个 Dump 线程,这个线程是把主( master )数据库的写入操作都会记录到这个 bin 的二进制文件中。
然后从( slave )数据库会启动一个 I/O 线程(监控主服务器的二进制日志的变化),这个线程主要是把主(master )数据库的 bin 二进制文件读取到本地,并写入到中继日志( Relay log )文件中。最后从(slave )数据库其他 SQL 线程,把中继日志( Relay log )文件中的事件再执行一遍,更新从( slave )数据库的数据,保持主从数据一致,重新写入数据库。

2.复制的工作过程


(1)在每个事务更新数据完成之前,Master将这些改变记录到二进制日志。写入二进制日志完成后,Master通知存储引擎提交事务
(2)Slave将Master的Binary复制到中继日志(Relay log)。首先,Slave开始一个工作线程--I/O线程,I/O线程在Master上打开一个普通的连接,然后开始Binlog dump porcess。Binlog dump process从Master的二进制日志中读取时间,如果已经跟上Master,它会睡眠并等待Master产生新的事件。I/O线程将这些时间写入到中继日志。
(3)SQL 线程处理该过程的最后一步。SQL线程从中继日志读取时间,并重放其中事件而更新slave数据,使其与Master中的数据保持一致。只要该线程与I/O线程保持一致,中继日志通常会位于OS的缓存中,所以中继日志的开销很小
(4)复制过程有一个很重要的限制,即复制在slave上是串行化的,也就是说Master上的并行更新操作不能在slave上并行操作。

3.mysql主从复制的案例环境

如果只简单的施行主从复制的案例,即最少两台主机就可以完成
(1)案例环境搭建
master:192.168.10.101
slave1:192.168.10.102
slave2:192.168.10.103
(2)案例实施
1.关闭所有服务器的防火墙
systemctl stop firewalld
setenforce 0
2.确保时间是同步的
这里我们用虚拟机搭建的环境,可以通过将主机和虚拟机的时间同步来实现
date命令可以查询时间是否一致
3.在所有服务器上安装mysql数据库
4.配置mysql master主服务器
vim /etc/my.cnf
在mysql模块中添加以下数据
server-id=11
log-bin=master-bin //注明自己的bin-log(二进制日志)
log-slave-updates=true //slave可以是其他slave的master,从而扩散master的更新(可以不用添加)
binlog-format=MAXED //复制类型为混合类型的复制
binlog-ignore-db=test //不记录指定数据库的二进制日志
replicate-ignore-db=test //设置不需要的库
binlog_cahche_size=1M //日志缓存的大小
expire_logs_days=3
添加过以上参数后保存退出
systemctl restart mysqld //重启一下mysql数据库
5.登录到mysql中
mysql -uroot -p
授权复制权限
grant replication slave on *.* 'myslave'@'192.168.10.%' identified by '123456';
flush privileges; //刷新加载权限表
show master status; //显示主服务器的二进制日志文件信息,记住文件名和position
6.从服务器的配置
vim /etc/my.cnf
添加以下参数
server-id=22
relay-log=relay-log-bin //中继日志文件的名字为relay-log-bin
relay-log-index=slave-relay-bin.relay //此处定义了该索引文件的名字
保存并退出
7.登录到从服务器的mysql数据库中
mysql -uroot -p
配置主从复制的相关参数
change master to master_host='192.168.10.101', master_user='myslave', master_password='123456', master_log_file='master-bin.000001', master_log_pos=337;
注: Slave 的 IO 线程接收到信息后,将接收到的日志内容依次写入到 Slave 端的Relay Log文件(relay-log-bin.xxxxxx)的最末端,并将读取到的Master端的master-bin的文件名和位置记录到master- info文件中,以便在下一次读取的时候能够清楚的告诉Master"我需要从某个master-bin的哪个位置开始往后的日志内容
start slave //开启slave
show slave status\G //查看slave的状态信息,其中
Slave_IO_Running: Yes

Slave_SQL_Running: Yes

这两条参数要为yes

8.验证主从复制

(1)在主从服务器上分别查询数据库

show databases;

(2)在主服务器上创建数据库

create database test;

show databases;

(3)在从服务器上再次查询数据库,可以看到也有了test数据库

show databases;

二.Mysql读写分离原理

简单来说,读写分离就只在主服务器上写,只在从服务器上读。基本原理是是让主数据库处理事务性查询,而从数据库处理select查询。数据库复制用来把主数据库上事务性查询导致的变更同步到集群中的从数据库。

1.读写分离的过程


目前最为常见的Mysql读写分离分为两种
(1)基于程序代码内部实现
在代码中根据select、insert进行路由分类,这类方法也是目前生产环境应用最为广泛的。优点是型性能好,因为在程序代码中实现,不需要增加额外的设备作为硬件开支;缺点是需要开发人员来实现,运维人员无从下手
(2)基于中间代理层实现
代理一般位于客户端和服务器间,代理服务器接到客户端请求后通过判断后转发到后端数据库,有两个代表性程序。
1.Mysql-Proxy。作为Mysql的开源项目,通过其自带的lua脚本进行SQL判断,虽然是MYSQL官方产品,但是Mysql官方并不建议将Mysql-Proxy应用到生产环境
2.Amoeba。该程序由Java语言进行开发,阿里巴巴将其用于到生产环境,但它不支持事务和存储的过程。
经过比较,通过程序代码是是西安MYSQL读写分离自然是一个不错的选择,但是并不是所有的应用都适合在程序代码中是实现读写分离,像一些大型复杂的Java应用,如果在程序代码中实现读写分离对代码改动较大。一般就会考虑使用代理层来实现,本案例通过Amoeba来实现。

2.搭建Mysql读写分离

1.在主机上Amoeba上安装java环境

这里开启一个192.168.10.104的主机作为Amoeba主机

chmod +x jdk-6u14-linux-x64.bin //增加可执行的权限

./jdk-6u14-linux-x64.bin //启动安装程序

mv jdk1.6.0_14/ /usr/local/jdk1.6 //移动并重命名目录

vim /etc/profile //到全局配置文件中

添加到最末尾 //这些命令用于设置环境变量,以便系统能够正确识别和使用新安装的 JDK 以及 Amoeba。

export JAVA_HOME=/usr/local/jdk1.6 //

  • 设置 JAVA_HOME 环境变量,指向 JDK 的安装目录。

export CLASSPATH=CLASSPATH:JAVA_HOME/lib:$JAVA_HOME/jre/lib //

  • 更新 CLASSPATH 环境变量,包含 JDK 的库路径。

export PATH=JAVA_HOME/lib:JAVA_HOME/jre/bin:PATH:JAVA_HOME/bin //

  • 更新 PATH 环境变量,包含 JDK 的库和二进制文件路径。

export AMOEBA_HOME=/usr/local/amoeba/ //

  • 设置 AMOEBA_HOME 环境变量,指向 Amoeba 的安装目录。

export PATH=PATH:AMOEBA_HOME/bin //

  • 更新 PATH 环境变量,包含 Amoeba 的二进制文件路径。

souce /etc/profile //重新加载环境变量

java -version //查询版本,确认java是否安装成功

2.安装并配置amoeba

mkdir /usr/local/amoeba //创建amoeba安装目录

tar zxvf amoeba-mysql-binary-2.2.0.tar.gz -C /usr/local/amoeba/ //解压

chmod -R 755 /usr/local/amoeba //递归更改目录以及子目录的权限

/usr/local/amoeba/bin/amoeba

显示amoeba start|stop 表示即为成功

3.配置amoeba读写分离

(1)在三个mysql服务器中开放权限给amoeba访问(只在master中即可,会复制到slave中)

mysql grant all on *.* to test2@'192.168.10.%' identified by '123.com';

(2)在amoeba上配置amoeba.xml文件

systemctl stop firewalld

cd /usr/local/amoeba/conf //切换到amoeba的配置文件下

vim amoeba.xml

修改红色部分,此处设置的是mysql客户端连接amoeba的账号和密码

<property name="authenticator">

<bean class="com.meidusa.amoeba.mysql.server.MysqlClientAuthenticator">

<property name="user">amoeba</property> ##30行

<property name="password">123456</property> ##32行

<property name="filter">

<bean class="com.meidusa.amoeba.server.IPAccessController">

<property name="ipFile">${amoeba.home}/conf/access_list.conf</property>

</bean>

</property>

</bean>

</property>

。。。。。。。。略。。。。。。。

<queryRouter class="com.meidusa.amoeba.mysql.parser.MysqlQueryRouter">

<property name="ruleLoader">

<bean class="com.meidusa.amoeba.route.TableRuleFileLoader">

<property name="ruleFile">${amoeba.home}/conf/rule.xml</property>

<property name="functionFile">${amoeba.home}/conf/ruleFunctionMap.xml</property>

</bean>

</property>

<property name="sqlFunctionFile">${amoeba.home}/conf/functionMap.xml</property>

<property name="LRUMapSize">1500</property>

<property name="defaultPool">master</property> ##115行

<property name="writePool">master</property> ##118行

<property name="readPool">slaves</property> ##119行此处的注释去掉

<property name="needParse">true</property>

</queryRouter>

(3)编辑dbServer.xml文件

vim db.Server.xml

修改(注意去掉注释),slave2的复制一个slave1

<!-- mysql user -->

<property name="user">test</property> ##26行

<property name="password">123.com</property> ##29行,去掉注释符

</factoryConfig>

。。。。。。。。。略。。。。。。。。。。

<dbServer name="master" parent="abstractServer"> ##45行

<factoryConfig>

<!-- mysql ip -->

<property name="ipAddress">192.168.1.101</property> ##48行

</factoryConfig>

</dbServer>

<dbServer name="slave1" parent="abstractServer"> ##52行

<factoryConfig>

<!-- mysql ip -->

<property name="ipAddress">192.168.1.102</property> ##55行

</factoryConfig>

</dbServer>

<dbServer name="slave2" parent="abstractServer">

<factoryConfig>

<!-- mysql ip -->

<property name="ipAddress">192.168.1.103</property>

</factoryConfig>

</dbServer>

<dbServer name="slaves" virtual="true"> ##59行

<poolConfig class="com.meidusa.amoeba.server.MultipleServerPool">

<!-- Load balancing strategy: 1=ROUNDROBIN , 2=WEIGHTBASED , 3=HA-->

<property name="loadbalance">1</property>

<!-- Separated by commas,such as: server1,server2,server1 -->

<property name="poolNames">slave1,slave2</property> ##65行

</poolConfig>

</dbServer>

(4)启动amoeba软件

cd /usr/local/amoeba

bin/amoeba start&

注:当在前台运行某个作业时,终端被改作业占据;而在后台作业时,它不会占终端。可以使用&命令把作业放到后台执行

netstat -anpt | greo java

如果能看到8066和3306两个端口号,证明amoeba是正常开启的。

4.测试

(1)新开一台主句,作为client主机(192.168.10.105)

yum -y install mariadb

mysql -uroot -p 123456 -h 192.168.10.104 -P 8066

(2)在master服务器上创建表

stop slave;

use auth

create table users(id int(10),name char(50));

(3)在两个slave服务器上

stop slave;

(4)在master服务器上

insert into users values('1','zhangsan');

(5)在slave1上

use auth

insert into zang values('2','zhangsan');

(6)在slave2上

use auth

insert into zang values('3','zhangsan');

(7)在client上查询三次

use auth

select * from users;

对比三次的输出,验证读操作,发现没有在master上写入的数据,而slave上写的能查到

(8)在client上

use auth

insert into users values('4','zhangsan');

select * from users; //发现在client上查不到自己写的数据

(9)在master上

select * from users; //能查到在client上写入的数据,说明写操作在master上

(10)在slave上

select * from users; //发现没有数据,说明写入操作在master上

导出导入数据库:

[root@localhost ~]# mysqldump -u root -p --database auth > /opt/auth.sql

[root@localhost data]# mysql -u root -p < /opt/ auth.sql

相关推荐
chat2tomorrow5 分钟前
oceanbase数据库安装和连接实战(阿里云服务器操作)
数据库·ide·mysql·阿里云·云计算·oceanbase·sqlynx
战神刘玉栋12 分钟前
《字符串杀手锏 · 正则表达式之一》
数据库·mysql·正则表达式
起的昵称都被用过了16 分钟前
mysql-sql-第十四周
数据库·sql·mysql
YoungSoulwt23 分钟前
23- Redis 主从复制是怎么实现的?
数据库·redis
Serendipity25 分钟前
Mybatis
java·数据库·mybatis
文牧之36 分钟前
PostgreSQL的系统视图pg_stat_bgwriter
运维·数据库·postgresql
2301_7811725339 分钟前
ElasticSearch 和 MySQL的区别
大数据·mysql·elasticsearch
菜鸟赵大宝2 小时前
2023软考中级《软件设计师》(备考冲刺版) | 数据库系统
数据库·数据库开发·数据库架构
TNTLWT3 小时前
MySQL:设计数据库与操作
数据库·mysql
橙子味冰可乐3 小时前
isprintable()方法——判断字符是否为可打印字符
java·前端·javascript·数据库·python