在CentOS7上搭建MySQL主从复制与读写分离
MySQL主从复制原理
MySQL的主从复制和MySQL的读写分离两者有着紧密联系,首先要部署主从复制,只有主从复制完成了,才能在此基础上进行数据的读写分离。
(1)MySQL支持复制的类型。
1)基于语句的复制。MySQL默认采用基于语句的复制,效率比较高。
2)基于行的复制。把改变的内容复制过去,而不是把命令在从服务器上执行一遍。
3)混合类型的复制。默认采用基于语句的复制,一旦发现基于语句无法精确复制时,就会采用基于行的复制。
(2)MySQL复制的工作过程如图所示。
1)在每个事务更新数据完成之前,Master在二进制日志记录这些改变。写入二进制日志完成后,Master通知存储引擎提交事务。
2)Slave将Master的Binary log复制到其中继日志。首先,Slave开始一个工作线程——I/O线程,I/O线程在Master上打开一个普通的链接,然后开始Binlog dump process。Binlog dump process从Master的二进制日志中读取事件,如果已经跟上Master,它会睡眠并等待Master产生新的事件。I/O线程将这些事件写入中继日志。
3)SQL slave thred(SQL从线程)处理该过程的最后一步。SQL线程从中继日志读取事件,并重放其中的事件而更新Slave的数据,使其与Master中的数据一致。只要该线程与I/O线程保持一致,中继日志通常会位于OS的缓存中,所以中继日志的开销很小。
复制过程中有一个很重要的限制,即复制在Slave上是串行化的,也就是说Master上的并行更新操作不能在Slave上并行操作。
MySQL读写分离原理
简单来说,读写分离(见图所示)就是只在主服务器上写,只在从服务器上读。基本原理是让主数据库处理事务性查询,而从数据库处理select查询。数据库复制被用来把事务性查询导致的变更同步到群集中的从数据库。
基于中间代理层实现:代理一般位于客户端和服务器之间,代理服务器接到客户端请求通过判断后转发到后端数据库。
Mysql复制优点:
1)通过添加从服务器来提高数据平台的可靠性,增加数据频台的高性能
2)提高数据的安全性,怎以作沦寻
3)缓解数据库的性能
Mysql复制类型:
1)异步复制:mysql默认是异步复制,主库不关心从收没收到数据,会直接返回给客户端,速度快,如果主库坏掉了,如果此时强制把从库提升为主库,有可能从库数据不完整
2)全同步复制:主库执行一个事务,会等待从库完事,才会返回给客户端,但是会导致效率慢,
3)半同步:主库执行完事务后,会等待一个从库完成事务后,在返回给客护端,
半同步复制的潜在问题:
客户端在存储引擎提交后,在得到从库确认过程中,主库死掉了,此时可能的情况有两种
- 事务还没发送到从库上,
此时,客户端会收到事务提交失败的信息,客户端会重新提交该事务到新的主上,当宕机的主库重新启动,会发现,该事务在从库被提交两次,一次是之前作为主的提交,一次是被新主同步过来的
- 事务已经发送到从库上
此时,从库已经收到该事务并应用了该事务,但是客户端仍会收到事务提交失败的信息,重新提交该事务到新的主上,
解决方案,mysql 5.7 引入了一种新的半同步解决方案,
Mysql 支持的复制方式
- (SBR)基于SQL 语句的复制:在主服务器上执行的SQL语句,在从服务器上同样执行
- (RBR)基于行的复制: 主服务器把表的行变化作为事件写入到二进制日志中,主服务器把代表了行的变化事件复制到从服务中,
- (MBR)混合模式复制,先采用基于语句的复制,一旦发现基于语句无法精确复制时,在采用行。
Mysql在 5.6 采用基于事务的复制
RBR的优点:
- 任何情况都可以复制,这对复制来说是最安全可靠的
- 更少的行级缩表
- 和其他大多数数据库系统的复制技术一样
- 多数情况下,从服务器的表有主键的话,复制就会快很多
RBR(行级复制)的缺点
- binlog文件较大
- 复杂的回滚时,binlog中会包含大量的数据
- 主服务器上执行多个UPDATE语句时,所有发生变化的记录都会写道binlog中,而且只写一个操作事务,这会导致频繁发生binlog的并发写问题。
- 不能通过查看日志文件来审计执行过的sql语句,
SBR(sql语句复制)的优点
- 历史悠久,技术成熟,binlog文件较小
- Binlog中包含了数据库所有的更改信息
- Binlog可以用于实时还原,而不仅仅用于复制
- 主从版本可以不一样,从服务器版本可以比主服务器版本高
SBR(sql语句复制)的缺点
- 不是所有的语句都能复制,尤其时包含不确定操作时
- 复制要求全表扫描
- 对于一些复杂的操作,在从服务器上资源消耗会更严重
- 主首先开启二进制,并且给从一个授权账号,
- 主的写入都会写道二进制日志里
- 从服务器开启IO线程,他会和主的IO线程建立连接,从会实时的探测主的二进制是否有变化,主有变化从会拿过来放到自己的中继日志,
- 从会开启一个sql线程,这个线程会实时读取中继日志变化并执行,实现数据同步,
==============mysql从服务器不能低于主服务器=================
实验环境
使用五台服务器模拟搭建,具体的拓扑如图所示:
实验环境表:
实施步骤
1.搭建MySQL主从复制。
(1)建立时间同步环境,在主节点上搭建时间同步服务器。
1)安装NTP。
[root@localhost ~]# yum install ntp -y
2)配置NTP。
[root@localhost ~]# vim /etc/ntp.conf
server 127.127.1.0 //本地是时钟源//
fudge 127.127.1.0 stratum 8 //设置时间层级为8(限制在15内)//
3)重启服务。
[root@localhost ~]# systemctl restart ntpd.service
(2)在从节点服务器上进行时间同步。
[root@localhost ~]# yum install ntpdate -y
[root@localhost ~]# ntpdate 192.168.200.111 //同步主服务器的时间//
同步网络上的时间:
[root@localhost ~]# crontab -l
**/5***/usr/sbin/ntpdate pool.ntp.org > /dev/null 2>&1
(3)在每台服务器上关闭firewalld防火墙。
[root@localhost ~]# systemctl stop firewalld.service //关闭防火墙//
[root@localhost ~]# setenforce 0
(4)安装MySQL数据库。在Master、Slave1、Slave2上安装,我用的数据库是MySQL5.7.17这里安装完毕不再演示。
(5)配置MySQL Master主服务器。
1)在/etc/my.cnf中修改或者增加以下内容。
[root@localhost mysql]# vim /etc/my.cnf
server-id = 11
log-bin=master-bin //主服务器日志文件//
log-slave-updates=true //从服务器更新二进制日志//
2)重启MySQL服务。
[root@localhost ~]# systemctl restart mysqld.service
3)登录MySQL程序,给服务器授权。
[root@localhost ~]# mysql -uroot -p
MariaDB [(none)]> grant replication slave on *.* to 'myslave'@'192.168.200.11_' identified by '123456';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> 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)
其中File列显示日志名,position列显示偏移量。
备份原有的数据:
Mysqldump -uroot -p123123 --all-databses > /root/alldbbackup.sql
Scp /root/alldbbackup.sql root@192.168.200.112:/root/
Scp /root/alldbbackup.sql root@192.168.200.112:/root
导入备份脚本
Mysql -u root -p < /root/alldbackup.sql
从库连接主库
Mysql -u myslave -p 123456 -h 192.168.200.111
(6)配置从服务器。
1)在/etc/my.cnf中修改或者增加以下内容。
[root@localhost ~]# vim /etc/my.cnf
server-id = 22
relay-log=relay-log-bin //从主服务器上同步日志文件记录到本地//
relay-log-index=slave-relay-bin.index //定义relay-log的位置和名称//
这里要注意server-id不能与主服务器相同。
2)重启mysql服务。
[root@localhost ~]# systemctl restart mysqld.service
3)登录mysql,配置同步。
按主服务器结果更改下面命令中的master_log_file和master_log_pos的参数。
从:
[root@localhost ~]# mysql -u root -p
MariaDB [(none)]> stop slave;
Query OK, 0 rows affected, 1 warning (0.00 sec)
MariaDB [(none)]> change master to master_host='192.168.200.111', master_user='myslave',master_password='123456',master_log_file='master-bin.000001', master_log_pos=456;
Query OK, 0 rows affected (0.01 sec)
4)启动同步。
mysql> start slave;
5)查看Slave状态,确保以下两个值为YES。
mysql> show slave status\G;
*************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.126.138 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 Slave_SQL_Running: Yes Replicate_Do_DB: Replicate_Ignore_DB:
如果报错的话:
MariaDB [(none)]> stop slave;
Query OK, 0 rows affected (0.00 sec)
清空配置:
MariaDB [(none)]> reset slave;
Query OK, 0 rows affected (0.00 sec)
重新写入配置:
在启动
MariaDB [(none)]> start slave;
Query OK, 0 rows affected (0.00 sec)
(7)验证主从复制效果。
1)在主服务器上新建数据库 db_test。
mysql> create database db_test;
2)在主、从服务器上分别查看数据库,显示数据库相同,则主从复制成功。
mysql> show databases; //主服务器//+--------------------+
| Database |+--------------------+
| information_schema |
| db_test |
| mysql |
| performance_schema |
| school |
| sys |+--------------------+
6 rows in set (0.03 sec)
mysql> show databases; //从服务器//+--------------------+
| Database |+--------------------+
| information_schema |
| db_test |
| kgc |
| mysql |
| performance_schema |
| sys |+--------------------+
6 rows in set (0.05 sec)
搭建MySQL读写分离
Amoeba(变形虫),这个软件致力于MySQL的分布式数据库前端代理层,它主要为应用层访问MySQL时充当SQL路由,并具有负载均衡、高可用性、SQL过滤、读写分离、可路由相关到目标数据库、可并发请求多台数据库。通过Amoeba能够完成多数据源的高可用、负载均衡、数据切片的功能。在主服务器上写,在从服务器上读,
目前较为常见的mysql读写分离方案有两种,
- 基于代码内部实现
在代码中根据select insert 进行理由分类,这类方法也是目前应用最广泛的,优点是性能好,因为在代码中实现,不需要增加额外的设备作为硬件开支,缺点是需要开发人员来实现,
- 基于中间代理层实现,
(1)在主机Amoeba上安装Java环境。
Amoeba是基于jdk1.5开发的,所以官方推荐使用jdk1.5或1.6版本,高版本不建议使用。
[root@localhost ~]# systemctl stop firewalld.service
[root@localhost ~]# setenforce 0
[root@localhost ~]# ls
amoeba-mysql-binary-2.2.0.tar.gz anaconda-ks.cfg jdk1.6.0_14 jdk-6u14-linux-x64.bin 公共 模板 视频 图片 文档 下载
[root@localhost tomcat]# cp jdk-6u14-linux-x64.bin /usr/local/
添加执行权限:
[root@localhost tomcat]# chmod +x jdk*
[root@localhost local]# ./jdk-6u14-linux-x64.bin //根据提示按Enter键完成即可//
[root@localhost local]# mv jdk1.6.0_14/ /usr/local/jdk1.6
[root@localhost local]# vim /etc/profile
增加以下配置
export JAVA_HOME=/usr/local/jdk1.6
ExportCLASSPATH=$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@localhost local]# source /etc/profile //启动//
查看版本
[root@localhost local]# java -version
java version "1.7.0_75"
OpenJDK Runtime Environment (rhel-2.5.4.2.el7_0-x86_64 u75-b13)
OpenJDK 64-Bit Server VM (build 24.75-b04, mixed mode)
删掉命令字
[root@localhost local]# rm -rf /usr/bin/java
重新加载
[root@localhost local]# source /etc/profile
[root@localhost local]# java -version
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)
Java环境已配置成功。
(2)安装并配置Amoeba软件
[root@localhost local]# mkdir /usr/local/amoeba 创建工作路径//
[root@localhost tomcat]# tar xf amoeba-mysql-binary-2.2.0.tar.gz -C /usr/local/amoeba //解压安装包//
[root@localhost tomcat]# chmod -R 755 /usr/local/amoeba/ //提示amoeba权限//
[root@localhost tomcat]# /usr/local/amoeba/bin/amoeba
amoeba start|stop //显示此内容说明Amoeba安装成功//
(3)配置Amoeba读写分离,两个Slave读负载均衡。
1)Master、Slave1、Slave2中开放权限给Amoeba访问。
grant all on *.* to test@'192.168.126.%' identified by '123.com';
MariaDB [(none)]> flush privileges;
Query OK, 0 rows affected (2.29 sec)
2)编辑amoeba.xml配置文件。修改前先备份
[root@localhost tomcat]# cd /usr/local/amoeba/
[root@localhost amoeba]# vim conf/amoeba.xml
---30行--
<property name="user">amoeba</property>
----32行---------
<property name="password">123456</property>
---117-去掉注释-
<property name="defaultPool">master</property>
<property name="writePool">master</property>
<property name="readPool">slaves</property>
3)编辑dbServers.xml配置文件。
vi conf/dbServers.xml
--26-29--去掉注释-- //行//用户名是授权的那个
<property name="user">test</property>
<property name="password">123.com</property>
-----42-主服务器地址---<dbServer name="master" parent="abstractServer">
<property name="ipAddress">192.168.126.138</property>
--52-从服务器主机名-<dbServer name="slave1" parent="abstractServer">
--55-从服务器地址-
<property name="ipAddress">192.168.126.162</property>
从服务器slave2 <dbServer name="slave2" parent="abstractServer"> //添加//
<property name="ipAddress">192.168.126.232</property>
<dbServer name="slaves" virtual="true">
<poolConfig class="com.meidusa.amoeba.server.MultipleServerPool">
--末尾--<property name="poolNames">slave1,slave2</property> //修改//
</poolConfig>
4)配置无误后,可用启动Amoeba软件,其默认端口为TCP 8066。
[root@localhost amoeba]# nohup /usr/local/amoeba/bin/amoeba start &
[root@localhost amoeba]# netstat -anpt | grep java
tcp6 0 0 127.0.0.1:28750 :::* LISTEN 3370/java
tcp6 0 0 :::8066 :::* LISTEN 3370/java
.....//省略//
(4) 测试
1)在client主机上。
[root@localhost ~]# yum install mysql -y
可以通过代理访问MySQL:
[root@localhost ~]# mysql -u amoeba -p123456 -h 192.168.126.132 -P8066
..... //省略//
MySQL [(none)]>
2)在Master上创建一个表,同步到各从服务器上,然后关闭各从服务器的Slave功能,再插入区别语句。
mysql> use db_test;
Database changed
mysql> create table zang (id int(10),name varchar(10),address varchar(20)); //创建表//
Query OK, 0 rows affected (0.06 sec)
分别在从两台服务器上关闭slave功能:
mysql> stop slave;
然后在主服务器上插入区别语句:
mysql> insert into zang values('1','zhang','this_is_master'); //插入数据//
3)从服务器上手动插入其他内容。
slave1:mysql> use db_test;
mysql> insert into zang values('2','zhang','this_is_slave1'); //插入数据//
Query OK, 1 row affected (0.03 sec)
slave2:mysql> use db_test;
mysql> insert into zang values('3','zhang','this_is_slave2'); //插入数据//
Query OK, 1 row affected (0.03 sec)
4)测试读操作
在client主机上第一次查询的结果如下:
MySQL [db_test]> select * from zang;
+------+-------+----------------+| id | name | address |
+------+-------+----------------+| 3 | zhang | this_is_slave2 |
+------+-------+----------------+1 row in set (0.01 sec)
第二次查询的结果如下:
MySQL [db_test]> select * from zang;
+------+-------+----------------+| id | name | address |
+------+-------+----------------+| 2 | zhang | this_is_slave1 |
+------+-------+----------------+1 row in set (0.01 sec)
第三次查询结果:
MySQL [db_test]> select * from zang;
+------+-------+----------------+| id | name | address |
+------+-------+----------------+| 3 | zhang | this_is_slave2 |
+------+-------+----------------+1 row in set (0.01 sec)
5)测试写操作。
在client主机上插入一条语句:
MySQL [db_test]> insert into zang values('5','zhang','write_test');
Query OK, 1 row affected (0.02 sec)
但在client上查询不到,最终只有在Master上才能查看到这条语句内容,说明写的操作在Master服务器上。
mysql> select * from zang;
+------+-------+----------------+| id | name | address |
+------+-------+----------------+| 1 | zhang | this_is_master || 5 | zhang | write_test |
+------+-------+----------------+2 rows in set (0.01 sec)
由此验证,已经实现了MySQL读写分离,目前所有的写操作全部在Master主服务器上,用来避免数据 的不同步;所有的读操作都分摊给了Slave从服务器,用来分担数据库的压力。