目录
MySQL主从复制
主从复制架构和原理
服务性能扩展方式
向上扩展,垂直扩展
向外扩展,横向扩展
复制的功用
1、数据分布
2、负载均衡读操作
3、备份
4、高可用和故障切换
5、MySQL升级测试
复制架构
主从复制原理
主从复制的工作过程:
1、Master节点的数据的改变记录成二进制日志(bin log),
2、当Master上的数据发生改变时,则将其改变写入二进制日志中。
3、开启一个slave服务线程传递给从服务器
4、Slave节点会在一定时间间隔内对Master的二进制日志进行探测其是否发生改变,如果发生改变,则开始一个I/O线程请求 Master的二进制事件。
5、同时Master节点为每个I/O线程启动一个dump线程,用于向其发送二进制事件,并保存至Slave节点本地的中继日志(Relay log)中,Slave节点将启动SQL线程从中继日志中读取二进制日志,在本地重放,即解析成 sql 语句逐一执行,使得其数据和 Master节点的保持一致,最后I/O线程和SQL线程将进入睡眠状态,等待下一次被唤醒。注:
●中继日志通常会位于 OS 缓存中,所以中继日志的开销很小。
●复制过程有一个很重要的限制,即复制在 Slave上是串行化的,也就是说 Master上的并行更新操作不能在 Slave上并行操作
主从复制相关线程
主节点:
dump Thread:为每个Slave的I/O Thread启动一个dump线程,用于向其发送binary log events
从节点:
I/O Thread:向Master请求二进制日志事件,并保存于中继日志中
SQL Thread:从中继日志中读取日志事件,在本地完成重放
跟复制功能相关的文件:
master.info:用于保存slave(从服务)连接至master(主服务)时的相关信息,例如账号、密码、服务器地址等
relay-log.info(中继日志):保存在当前slave节点上已经复制的当前二进制日志和本地relay-log日志的对应关系
mariadb-relay-bin.00000#: 中继日志,保存从主节点复制过来的二进制日志,本质就是二进制日志
MySQL 主从复制延迟
-
master服务器高并发,形成大量事务
-
网络延迟
-
主从硬件设备导致 cpu主频、内存io、硬盘io
-
本来就不是同步复制、而是异步复制 从库优化Mysql参数。比如增大innodb_buffer_pool_size,让更多操作在Mysql内存中完成,减少磁盘操作。 从库使用高性能主机。包括cpu强悍、内存加大。避免使用虚拟云主机,使用物理主机,这样提升了i/o面性。 从库使用SSD磁盘 网络优化,避免跨机房实现同步
MySQL扩展
-
读写分离
-
复制:每个节点都有相同的数据集,向外扩展,基于二进制日志的单向复制
什么是读写分离
读写分离,基本的原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。
MySQL 读写分离原理
读写分离就是只在主服务器上写,只在从服务器上读。基本的原理是让主数据库处理事务性操作,而从数据库处理 select 查询。数据库复制被用来把主数据库上事务性操作导致的变更同步到集群中的从数据库。目前较为常见的 MySQL 读写分离分为以下两种:
1)基于程序代码内部实现
在代码中根据 select、insert 进行路由分类,这类方法也是目前生产环境应用最广泛的。
优点是性能较好,因为在程序代码中实现,不需要增加额外的设备为硬件开支;缺点是需要开发人员来实现,运维人员无从下手。但是并不是所有的应用都适合在程序代码中实现读写分离,像一些大型复杂的Java应用,如果在程序代码中实现读写分离对代码改动就较大。
2)基于中间代理层实现
代理一般位于客户端和服务器之间,代理服务器接到客户端请求后通过判断后转发到后端数据库,有以下代表性程序。
(1)MySQL-Proxy。MySQL-Proxy 为 MySQL 开源项目,通过其自带的 lua 脚本进行SQL 判断。
(2)Atlas。是由奇虎360的Web平台部基础架构团队开发维护的一个基于MySQL协议的数据中间层项目。它是在mysql-proxy 0.8.2版本的基础上,对其进行了优化,增加了一些新的功能特性。360内部使用Atlas运行的mysql业务,每天承载的读写请求数达几十亿条。支持事物以及存储过程。
(3)Amoeba。由陈思儒开发,作者曾就职于阿里巴巴。该程序由Java语言进行开发,阿里巴巴将其用于生产环境。但是它不支持事务和存储过程。由于使用MySQL Proxy 需要写大量的Lua脚本,这些Lua并不是现成的,而是需要自己去写。这对于并不熟悉MySQL Proxy 内置变量和MySQL Protocol 的人来说是非常困难的。
Amoeba是一个非常容易使用、可移植性非常强的软件。因此它在生产环境中被广泛应用于数据库的代理层。
为什么要读写分离
因为数据库的“写”(写10000条数据可能要3分钟)操作是比较耗时的。
但是数据库的“读”(读10000条数据可能只要5秒钟)。
所以读写分离,解决的是,数据库的写入,影响了查询的效率。什么时候需要读写分离
数据库不一定要读写分离,如果程序使用数据库较多时,而更新少,查询多的情况下会考虑使用。利用数据库主从同步,再通过读写分离可以分担数据库压力,提高性能。
主从复制与读写分离
在实际的生产环境中,对数据库的读和写都在同一个数据库服务器中,是不能满足实际需求的。无论是在安全性、高可用性还是高并发等各个方面都是完全不能满足实际需求的。因此,通过主从复制的方式来同步数据,再通过读写分离来提升数据库的并发负载能力。有点类似于rsync,但是不同的是rsync是对磁盘文件做备份,而mysql主从复制是对数据库中的数据、语句做备份。
mysq支持的复制类型
(1)STATEMENT:基于语句的复制。在服务器上执行sql语句,在从服务器上执行同样的语句,mysql默认采用基于语句的复制,执行效率高。
(2)ROW:基于行的复制。把改变的内容复制过去,而不是把命令在从服务器上执行一遍。
(3)MIXED:混合类型的复制。默认采用基于语句的复制,一旦发现基于语句无法精确复制时,就会采用基于行的复制。
实战项目
各种中间件地址
mysql-proxy:Oracle,MySQL :: Download MySQL Proxy (Archived Versions)
Atlas:Qihoo,Atlas/README_ZH.md at master · Qihoo360/Atlas · GitHub
dbproxy:美团,https://github.com/Meituan-Dianping/DBProxy
Cetus:网易乐得,https://github.com/Lede-Inc/cetus
Amoeba:amoeba download | SourceForge.net
Cobar:阿里巴巴,Amoeba的升级版, GitHub - alibaba/cobar: a proxy for sharding databases and tables
Mycat:基于Cobar http://www.mycat.io/ (原网站)
环境准备
master(主)服务器:192.168.37.100 mysql 5.7
slave(从)服务器:192.168.37.101 mysql 5.7
slave(从)服务器:192.168.37.102 mysql 5.7
Amoeba 服务器:192.168.37.105 jdk 1.6 、amoeba
客户端服务器: 192.168.37.106 mysql
搭建主从复制
搭建时间同步
主从服务器安装时间同步器
[root@localhost /opt]# yum install ntp -y
--------------------------------------------------------配置主服务器--------------------------------------------------
修改配置文件
[root@localhost /opt]# vim /etc/ntp.conf
开启服务
[root@localhost /opt]# service ntpd start
-------------------------------------------------------配置从服务器----------------------------------------------------
开启服务
[root@localhost ~]# [root@localhost /opt]# service ntpd start Redirecting to /bin/systemctl start ntpd.service
执行同步
[root@localhost ~]# /usr/sbin/ntpdate 192.168.37.100 5 Dec 15:04:35 ntpdate[83019]: the NTP socket is in use, exiting
计划每30分钟同步一次
两台从服务器同样的操作
配置主从
开启二进制文件
--------------------------------------------------------主服务器-----------------------------------------------------------
配置mysql主配置文件
[root@localhost /opt]# vim /etc/my.cnf
重启数据库
[root@localhost /opt]# systemctl restart mysqld.service
进入数据库,replication主从配置
(root@localhost) [(none)]> grant replication slave on *.* to 'myslave'@'192.168.37.%' identified by '123456'; Query OK, 0 rows affected, 1 warning (0.00 sec)
<!--myslave是自己创建的名字,ip填写从服务器或网段即可-->
刷新
(root@localhost) [(none)]> flush privileges;
查看
(root@localhost) [(none)]> show master status; +------------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-------------------+ | mysql-bin.000005 | 603 | | | | +------------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec)
-------------------------------------------------------从服务器-----------------------------------------------------------
配置mysql主配置文件
[root@localhost ~]# vim /etc/my.cnf
server-id = 2
#修改,注意id与Master的不同,两个Slave的id也要不同
relay-log=relay-log-bin
#添加,开启中继日志,从主服务器上同步日志文件记录到本地
relay-log-index=slave-relay-bin.index
#添加,定义中继日志文件的位置和名称,一般和relay-log在同一目录重启MySQL服务
[root@localhost ~]# systemctl restart mysqld.service
进入数据库
模板
mysql>help change master to ..... CHANGE MASTER TO MASTER_HOST='master2.mycompany.com', MASTER_USER='replication', MASTER_PASSWORD='bigs3cret', MASTER_PORT=3306, MASTER_LOG_FILE='master2-bin.001', MASTER_LOG_POS=4, MASTER_CONNECT_RETRY=10; .....
change master to master_host='192.168.37.100',master_user='myslave',master_password='123456',master_r_log_file='mysql-bin.000005',master_log_pos=603; Query OK, 0 rows affected, 2 warnings (0.01 sec)
主服务器的IP地址,用户名replication时配置的,密码也是,二进制文件.与主服务器相对应
(root@localhost) [(none)]> show master status; +------------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-------------------+ | mysql-bin.000005 | 603 | | | | +------------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec)
开启
mysql> start slave; Query OK, 0 rows affected (0.00 sec)
查看状态
mysql> show slave status\G;
<!--第二台从服务器配置一样,唯一不同的是主配置文件server-id设为3-->
第二台从服务器也成功了
验证主从同步
在主服务器创建一个库
(root@localhost) [(none)]> create database xiaob;
创建一个表
create table bb (id int primary key,name varchar(20),Gender varchar(2)); Query OK, 0 rows affected (0.01 sec)
![]()
在从服务器上看
mysql> show tables; +-----------------+ | Tables_in_xiaob | +-----------------+ | bb | +-----------------+ 1 row in set (0.00 sec)
主从同步已经成功了!!!
搭建Amoeba 实现读写分离
安装jdk
因为 Amoeba 基于是 jdk1.5 开发的,所以官方推荐使用 jdk1.5 或 1.6 版本,高版本不建议使用。
首先下载jdk1.6版本和中间件amoeba
复制jdk到/usr/local目录下
[root@localhost /opt]# cp jdk-6u14-linux-x64.bin /usr/local/
给执行权限
[root@localhost /opt]# chmod +x /usr/local/jdk-6u14-linux-x64.bin
安装jdk,一路回车到底,最后输入yes 自动安装
[root@localhost /usr/local]# ./jdk-6u14-linux-x64.bin
改个名字
[root@localhost /usr/local]# mv jdk1.6.0_14/ jdk1.6
添加环境变量
[root@localhost /usr/local]# 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@localhost /usr/local]# source /etc/profile
Java环境安装完毕!!!
[root@localhost /usr/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)
安装amoeba中间件
新创建一个目录
[root@localhost /usr/local]# mkdir amoeba
解压
[root@localhost /usr/local]# cd /opt/ [root@localhost /opt]# ls amoeba-mysql-binary-2.2.0.tar.gz jdk-6u14-linux-x64.bin rh [root@localhost /opt]# tar zxf amoeba-mysql-binary-2.2.0.tar.gz -C /usr/local/amoeba/
给权限
[root@localhost /opt]# chmod -R 755 /usr/local/amoeba/
安装amoeba ,出现amoeba start|stop代表安装成功
[root@localhost /usr/local/amoeba/bin]# /usr/local/amoeba/bin/amoeba amoeba start|stop
配置 Amoeba读写分离,两个 Slave 读负载均衡
先在Master、Slave1、Slave2 的mysql上开放权限给 Amoeba 访问
(root@localhost) [(none)]> grant all on *.* to test@'192.168.37.%' identified by '123123'; Query OK, 0 rows affected, 1 warning (0.01 sec)
刷新
(root@localhost) [(none)]> flush privileges; Query OK, 0 rows affected (0.00 sec)
修改amoeba配置
[root@localhost /usr/local/amoeba]# cd conf/
最好备份amoeba.xml文件
[root@localhost /usr/local/amoeba/conf]# cp amoeba.xml amoeba.xml.bak
全局配置
[root@localhost /usr/local/amoeba/conf]# vim amoeba.xml
![]()
配置 dbServers.xml文件,还是最好先备份一份
[root@localhost /usr/local/amoeba/conf]# cp dbServers.xml dbServers.xml.bak
[root@localhost /usr/local/amoeba/conf]# vim dbServers.xml
开启amoeba
[root@localhost /usr/local/amoeba/conf]# amoeba start &
<!--此命令以前台的方式启动,会输出启动时的信息,检查没有错误信息后,中断,并后台运行-->:
查看Java端口号
[root@localhost /usr/local/amoeba/conf]# netstat -ntap |grep java tcp6 0 0 127.0.0.1:46361 :::* LISTEN 53022/java tcp6 0 0 :::8066 :::* LISTEN 53022/java tcp6 0 0 192.168.37.105:43478 192.168.37.102:3306 ESTABLISHED 53022/java tcp6 0 0 192.168.37.105:54060 192.168.37.100:3306 ESTABLISHED 53022/java tcp6 0 0 192.168.37.105:37016 192.168.37.101:3306 ESTABLISHED 53022/java
在客户端连接amoeba,密码是全局配置设置的密码
[root@localhost /usr/local/mysql]# mysql -u amoeba -p -h 192.168.37.105 -P8066
这样就进来了,表是主服务器上的
mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | bank | | bbs | | hellodb | | jianghu | | mysql | | performance_schema | | sys | | usr | | xb | | xiaob | +--------------------+ 11 rows in set (0.01 sec)
验证读写分离
首先把主从关闭
mysql> stop slave;
在第一台从服务器添加数据
mysql> insert into bb values(1,'dav','m');
在第二台从服务器添加数据
mysql> insert into bb values(2,'xiaoli','女'); Query OK, 1 row affected (0.00 sec)
轮询
mysql> select * from bb; +----+------+--------+ | id | name | Gender | +----+------+--------+ | 1 | dav | m | +----+------+--------+ 1 row in set (0.00 sec) mysql> select * from bb; +----+--------+--------+ | id | name | Gender | +----+--------+--------+ | 2 | xiaoli | 女 | +----+--------+--------+ 1 row in set (0.00 sec) mysql> select * from bb; +----+------+--------+ | id | name | Gender | +----+------+--------+ | 1 | dav | m | +----+------+--------+ 1 row in set (0.01 sec) mysql> select * from bb; +----+--------+--------+ | id | name | Gender | +----+--------+--------+ | 2 | xiaoli | 女 | +----+--------+--------+ 1 row in set (0.01 sec)
验证写全部开启
mysql> set global general_log=1;
mysql> show variables like 'general%'; +------------------+-------------------------------------+ | Variable_name | Value | +------------------+-------------------------------------+ | general_log | ON | | general_log_file | /usr/local/mysql/data/localhost.log | +------------------+-------------------------------------+ 2 rows in set (0.00 sec)
再重新查看bb表
查看信息
读写分离就成功了!!!
可能遇到的问题
造成主从不一致的原因
1、主库binlog格式为Statement,同步到从库执行后可能造成主从不一致。
2、主库执行更改前有执行set sql_log_bin=0,会使主库不记录binlog,从库也无法变更这部分数据。
3、从节点未设置只读,误操作写入数据
4、主库或从库意外宕机,宕机可能会造成binlog或者relaylog文件出现损坏,导致主从不一致
5、主从实例版本不一致,特别是高版本是主,低版本为从的情况下,主数据库上面支持的功能,从不支持
6、数据库上面可能不支持该功能
7、MySQL自身bug导致
主从不一致修复方法
将从库重新实现
虽然这也是一种解决方法,但是这个方案恢复时间比较慢,而且有时候从库也是承担一部分的查询操作的,不能贸然重建。
使用percona-toolkit工具辅助
PT工具包中包含pt-table-checksum和pt-table-sync两个工具,主要用于检测主从是否一致以及修复数据不一致情况。这种方案优点是修复速度快,不需要停止主从辅助,缺点是需要知识积累,需要时间去学习,去测试,特别是在生产环境,还是要小心使用
手动重建不一致的表
在从库发现某几张表与主库数据不一致,而这几张表数据量也比较大,手工比对数据不现实,并且重做整个库也比较慢,这个时候可以只重做这几张表来修复主从不一致。这种方案缺点是在执行导入期间需要暂时停止从库复制,不过也是可以接受的
避免主从不一致
1、主库binlog采用ROW格式
2、主从实例数据库版本保持一致
3、主库做好账号权限把控,不可以执行set sql_log_bin=0
4、从库开启只读,不允许人为写入
5、定期进行主从一致性检验