企业——MYSQL(基于GTID)的主从复制、基于组提交的并行复制、半同步复制、全同步
一、什么是主从复制?
主从复制,是用来建立一个和主数据库完全一样的数据库环境,称为从数据库;主数据库一般是准实时的业务数据库。
二、主从复制的作用(好处,或者说为什么要做主从)
1、做数据的热备,作为后备数据库,主数据库服务器故障后,可切换到从数据库继续工作,避免数据丢失。
2、架构的扩展。业务量越来越大,I/O访问频率过高,单机无法满足,此时做多库的存储,降低磁盘I/O访问的频率,提高单个机器的I/O性能。
3、读写分离,使数据库能支撑更大的并发。在报表中尤其重要。由于部分报表sql语句非常的慢,导致锁表,影响前台服务。如果前台使用master,报表使用slave,那么报表sql将不会造成前台锁,保证了前台速度。
三、主从复制的原理:
1.数据库有个bin-log二进制文件,记录了所有sql语句。
2.我们的目标就是把主数据库的bin-log文件的sql语句复制过来。
3.让其在从数据的relay-log重做日志文件中再执行一次这些sql语句即可。
4.下面的主从配置就是围绕这个原理配置
5.具体需要三个线程来操作:
--(1).binlog输出线程:每当有从库连接到主库的时候,主库都会创建一个线程然后发送binlog内容到从库。在从库里,当复制开始的时候,从库就会创建两个线程进行处理:
--(2).从库I/O线程:当START SLAVE语句在从库开始执行之后,从库创建一个I/O线程,该线程连接到主库并请求主库发送binlog里面的更新记录到从库上。从库I/O线程读取主库的binlog输出线程发送的更新并拷贝这些更新到本地文件,其中包括relay log文件。
--(3).从库的SQL线程:从库创建一个SQL线程,这个线程读取从库I/O线程写到relay log的更新事件并执行。
可以知道,对于每一个主从复制的连接,都有三个线程。拥有多个从库的主库为每一个连接到主库的从库创建一个binlog输出线程,每一个从库都有它自己的I/O线程和SQL线程。
大体步骤:
步骤一:主库db的更新事件(update、insert、delete)被写到binlog
步骤二:从库发起连接,连接到主库
步骤三:此时主库创建一个binlog dump thread线程,把binlog的内容发送到从库
步骤四:从库启动之后,创建一个I/O线程,读取主库传过来的binlog内容并写入到relay log.
步骤五:还会创建一个SQL线程,从relay log里面读取内容,从Exec_Master_Log_Pos位置开始执行读取到的更新事件,将更新内容写入到slave的db.
请注意当你进行复制时,所有对复制中的表的更新必须在主服务器上进行。否则,你必须要小心,
以避免用户对主服务器上的表进行的更新与对从服务器上的表所进行的更新之间的冲突。
四.试验过程
1.实验环境
redhat6.5
iptables和selinux关闭
master:vm1 (172.25.254.1)
slave:vm2,3
(172.25.254.2 172.25.254.3)
2.数据同步
在vm1,2,3上:
安装mysql,安全初始化
yum install mysql-server -y
tar xf
mysql-5.7.17-1.el6.x86_64.rpm-bundle.tar
ls
yum reinstall
mysql-community-client-5.7.17-1.el6.x86_64.rpm
mysql-community-common-5.7.17-1.el6.x86_64.rpm
mysql-community-libs-5.7.17-1.el6.x86_64.rpm
mysql-community-libs-compat-5.7.17-1.el6.x86_64.rpm
mysql-community-server-5.7.17-1.el6.x86_64.rpm -y
/etc/init.d/mysqld start
在vm1(master)上:master是多线程的
vim /etc/my.cnf
在[mysqld]下添加一下参数
log-bin=mysql-bin ##启动二进制日志系统 二进制文件名
binlog-do-db=westos ##二进制需要同步的数据库名,如果需要同步多个库,例如要再同步 westos
库,再添加一行“binlog-do-db=westos”,以此类推
server-id=1/2 ##必须为 1 到 232–1 之间的一个正整数值 在slave上设置为2
binlog-ignore-db=mysql ##禁止同步 mysql 数据库
/etc/init.d/mysqld start
mysql -p ##上面的截图,给数据库初始化更改了密码,现在输入的是自己设置的密码。
mysql> grant replication slave on
*.* to repl@'%' identified by 'Wang+1212'; ##设置允许从库复制。replication 复制的意思
mysql>Flush privileges; ##需用flush privileges刷新MySQL的系统权限相关表,否则会出现拒绝访问;还有一种方法,就是重新启动mysql服务器,来使新设置生效。
参数解释:
replication ##表示授权复制的权限
*.* ##表示所有数据库可以进行同步
repl ##表示授权名,可以随意填写
'172.25.254.%' ##表示授权172.25.254.0/24的网段所有服务器可以同步, %表示任意
调试:
(1)在 master 上用下面的命令查看
mysql> show master status; ##记录 File 和 Position 的值,下面会用到。(每更新或者写入数据库东西,就会生成新的二进制文件,及从什么位置更新的更新号。主库的Posisiton一直是变更的。)
(2)查看二进制日志是否打开 (二进制文件bin log开启之后,才可以记录和同步)
通过show variables语句的like可以匹配多个值或者某个值。
通过百分号(%)这个通配符进行匹配,可以匹配多项,名字包含connect的都会被显示出来。%在结束处:表示以搜索的关键字为首。%在开始处:表示以搜索的关键字为结束。
show variables语句:
show variables主要是用来查看系统变量的值.
执行SHOW VARIABLES命令不需要任何权限,只要求能够连接到服务器就可以.
使用like语句表示用variable_name进行匹配.
%百分号通配符可以用在匹配模式中的任何位置
在vm2(slave)上:
mysql> change master to
master_host='172.25.254.1' , master_user='repl' , master_password='Wang+1212' ,
master_log_file='mysql-bin.000001', master_log_pos=446;
Query OK, 0 rows affected, 2 warnings
(0.18 sec)
mysql> start slave;
Query OK, 0 rows affected (0.07
sec)
mysql> show slave status\G;
。。。
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
。。。
##如果都是 yes,表示从库的 I/O,Slave_SQL 线程都正确开启.表明数据库正在同步
测试:
在vm1(master)上:
mysql> create database
westos;
Query OK, 1 row affected (0.35
sec)
mysql> use westos;
mysql>creata table usertb(
->username varchar(20) not null,
->password varchar(25) not null);
mysql> insert into usertb values
('wf','111');
在vm2(slave)上: (查看可以同步到新建的数据库westos,因此主从复制的配置成功)
mysql> show databases;
mysql> use westos;
mysql> show tables;
mysql> select * from usertb;
==============================================================================================
Slave的两个线程是NO的可能的原因是:
(1).Slave_IO_Running:NO的原因:用户认证,防火墙或者网络的问题 IO是异步的(同步安全,但是太慢,因此使用半同步复制,需要一个半同步的插件,专开一个线程等待IO回应,然后继续同步)
(2)Slave_SQL_Running:NO的原因:日至不同步(这个是NO,可能没那么严重,可能是因为回放文件的时候,和本来的数据有冲突,可以手动解决)
防止加重冲突越来越高(冲突来源:因为master是多线程的,SQL是单线程的)当master更加繁忙的时候,会加重SQL的延迟,数据不同步的情况,解决方法:并行复制
基于GTID的模式:
有GTID NEXT,通过这个才读取之后的数据,这个是不需要手动制定的从哪里开始存储的信息和存储的日志的名称。GTID会自动的发现。
==============================================================================================
3.开启GTID模式(基于GTID的主从复制)
mysql数据库从5.6.5开始新增一种基于GDIT的复制方式。GTID (Global Transaction ID) 是对于一个已提交事务的编号,并且是一个全局唯一的编号。 GTID 实际上 是由 UUID+TID 组成的。其中 UUID 是一个 MySQL 实例的唯一标识。
主从复制,默认是通过pos复制(postion),就是说在日志文档里,将用户进行的每一项操作都进行编号(pos),每一个event都有一个起始编号,一个终止编号,我们在配置主从复制时从节点时,要输入master的log_pos值就是这个原因,要求它从哪个pos开始同步数据库里的数据,这也是传统复制技术。
MySQL5.6增加了GTID复制,GTID就是类似于pos的一个作用,不过它是整个mysql复制架构全局通用的,就是说在这整个mysql冗余架构中,它们的日志文件里事件的GTID值是一致的.
GTID (Global Transaction ID) 是对于一个已提交事务的编号,并且是一个全局唯一的编号。 GTID 实际上 是由 UUID+TID 组成的。其中 UUID 是一个 MySQL 实例的唯一标识。TID 代表了该实例上已经提交的事务数量,并且随着事务提交单调递增。
通过GDIT保证每个主库上提交的事务在集群中有一个唯一的ID.这种方式强化了数据库的主备一致性,故障恢复以及容错能力
在vm1,2上:
vim /etc/my.cnf ##数据库的配置文件 添加有关GTID的模块 (主从数据库都需要开启)
gtid_mode=ON ##开启GTID的模块
enforce-gtid-consistency=true ##强制一直保持GTID
/etc/init.d/mysqld restart ##因为重写了事件,就会重新写一个二进制文件了
在vm1上,只要更改了事件,就会自动创建一个新的二进制日志文件:
二进制文件所在目录:
cd /var/lib/mysql
cat mysql-bin.index
./mysql-bin.000001
./mysql-bin.000002
./mysql-bin.000003
./mysql-bin.000004
在vm2(slave)上重新配置master的信息:(在从库端先停掉slave,然后重新创建连接)
mysql -p
stop slave;
change master to
master_host='172.25.254.1',master_user='repl',master_password='Wang+1212',MASTER_AUTO_POSITION=1; ## MASTER_AUTO_POSITION=1 动态获取master的数据
start slave;
show slave status\G;
。。。
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
。。。
测试:
在vm1上:
mysql> insert into usertb values
('wf1','111');
Query OK, 1 row affected (0.35
sec)
mysql> select * from usertb;
在vm2上: 查看可以同步到新建的信息
mysql> use westos;
mysql> select * from usertb;
当在从库中看到了相关的主库添加的信息后,就代表基于GTID的数据库的主从复制配置成功。
查看进程信息:
mysql> show processlist;
##上面的是IO线程 下面的是SQL线程
4.MYSQL的基于组提交的并行复制(解决复制延迟的问题)
=======================================================================================
判断主从延迟:
mysql> show slave status\G;
Seconds_Behind_Master:0 ##落后master多少秒
对比一下后面的数字是否一样,一样则没有延迟。
Read_Master_Log_Pos: 2664
Exec_Master_Log_Pos: 2664
并行复制的原理:
在普通的主从复制架构中,slave服务器上有两个线程:IO线程和SQL线程。IO线程负责接收master的二进制日志(准确的说是二进制日志的event),SQL线程负责应用二进制日志(准确的说是relay-log)。
在MySQL5.6版本中的并行复制中(需开启并行复制功能),SQL线程变为coordinator线程,判断是否可以并发执行:如可以并行执行,选择worker线程执行二进制日志。如不可并行执行,是DDL或是跨schema的操作,则等待所有的worker线程执行完成之后再执行当前日志。coordinator线程不仅仅可以将日志发送给worker线程,也可以回放日志,但是并行的操作都会交给worker线程来完成。MYSQL 5.7中引入了slave-parallel-type参数,用来控制并发复制的方式,可用的值为DATABASE和LOGICAL_CLOCK。DATABASE:默认值,基于库的并行复制方式。LOGICAL_CLOCK:基于组提交的并行复制方式(LOGICAL_CLOCK是一个全局递增的64位长整型数字,主要通过它来判断哪些事务能够并发)
使用并行复制的原因是:解决延迟的问题
=======================================================================================
配置主从复制:
将原本的默认的基于库的并行复制的方式,更改为基于组提交的并行复制方式。
在vm2(slave)上:
在数据库中:
mysql> use mysql;
mysql> select * from
slave_master_info;
Empty set (0.00 sec) ##在默认基于库的并行复制模式下,这个是空的
vim /etc/my.cnf
slave-parallel-type=LOGICAL_CLOCK
##默认是DATABASE,基于库的并行复制;LOGICAL_CLOCK:基于组提交的并行复制
slave-parallel-workers=16 ##设置为几,就会有几个进程号
##原来的SQL变成了调度进程 slave-parallel-workers=16(不要设置为1,经过中转,会更慢)
master_info_repository=TABLE
relay_log_info_repository=TABLE
relay_log_recovery=ON
/etc/init.d/mysqld restart
5.IO异步,更改为半同步复制
MySQL默认的复制即是异步的,主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理,这样就会有一个问题,主库如果down掉了,此时主库上已经提交的事务可能并没有传到从库上,如果此时,强行将从库提升为主库,可能导致新主库上的数据不完整。
半同步复制就是为了解决数据丢失的问题。
在vm1(master)上:
mysql -p
show processlist; ##查看进程列表
mysql> INSTALL PLUGIN
rpl_semi_sync_master SONAME 'semisync_master.so'; ##主库安装服务插件,并且开启半同步复制
mysql> show plugins;
mysql> SET GLOBAL rpl_semi_sync_master_enabled=1/ON
mysql> show status like
'%rpl%'; ##查看主从复制的状态
mysql> select plugin_name,plugin_status
-> from information_schema.plugins
-> where plugin_name like '%semi%';
+----------------------+---------------+
| plugin_name | plugin_status |
+----------------------+---------------+
| rpl_semi_sync_master | ACTIVE |
+----------------------+---------------+
1 row in set (0.00 sec)
mysql> show variables like '%semi%'; #环境变量
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled | ON |
| rpl_semi_sync_master_timeout | 10000 | ##默认10000毫秒(10秒),生产环境中建议设置为无穷大
| rpl_semi_sync_master_trace_level | 32 |
| rpl_semi_sync_master_wait_for_slave_count | 1 |
| rpl_semi_sync_master_wait_no_slave | ON |
| rpl_semi_sync_master_wait_point | AFTER_SYNC |
+-------------------------------------------+------------+
6 rows in set (0.00 sec)
在vm2(slave)上:
mysql -p
show processlist; ##查看进程列表
mysql> INSTALL PLUGIN
rpl_semi_sync_slave SONAME 'semisync_slave.so'; ##加入插件
mysql> show plugins; ##查看插件
mysql> SET GLOBAL
rpl_semi_sync_slave_enabled=1
mysql> show status like
'%rpl%'; ##查看主从复制的状态 现在是关闭的
mysql> stop slave io_thread; ##如果不执行这两步的话,主从复制的状态是OFF
mysql> start slave io_thread;
mysql> show status like
'%rpl%'; ##查看主从复制的状态 现在是开启的
注意:要重启从上的IO线程,如果没有重启,则默认还是异步复制,重启后,slave会在master上注册为半同步复制的slave角色
测试:
(1)
在vm1上:
mysql> insert into usertb values
('wf2','222');
mysql> select * from usertb;
mysql> show status like
'%rpl%';
##这时就可以看到
Rpl_semi_sync_master_yes_tx 1 通过主从复制成功复制了一次
在vm2上:
mysql> select * from usertb; ##可以查看到刚才添加的信息
(2)
在vm2上:
mysql> stop slave io_thread;
在vm1上:
mysql> insert into usertb values
('wf3','333');
mysql> select * from usertb;
mysql> show status like
'%rpl%';
##这时就可以看到
Rpl_semi_sync_master_no_tx 1 通过主从复制失败复制了一次
注意:MYSQL默认的是异步复制,可以修改复制方式为半同步复制,也可以根据需求修改半同步复制的一些参数,比如:时间。但是需要注意的是,如果在设定的时间内master到得不到slave的回复,那么复制的方式就会变回异步复制的方式,因此,如果设置为半同步复制的话,参数的等待的时间需要设定到无穷大
6.如果写操作较少,而读操作很多时,可以采取这种结构。你可以将读操作分布到其它的 slave,从而减小
master 的压力。但是当读少写多的情况,当 slave 增加到一定数量时,slave 对 master 的负载以及网络带宽都会成为一个严重
的问题。这种结构虽然简单,但是,它却非常灵活,足够满足大多数应用需求。当设置 log_slave_updates 时,你可以让 slave 扮演其它
slave 的 master。此时,slave 把 SQL 线程执行的事
件写进行自己的二进制日志(binary log),然后,其它的 slave 可以获取这些事件并执行它,从而有效缓解
master 的压力 (vm1
-> vm2 -> vm3) 缓解master的压力,不会因为master坏了,而使服务用不了
(一主多从,用于读多写少的场景)
在vm2上:
vim /etc/my.cnf
....
server-id=2
log-bin=mysql-bin
binlog-ignore-db=mysqlbinlog-do-db=test
log-slave-updates
....
/etc/init.d/mysqld restart
在vm2上创建同步帐户,并给予权限:
mysql> GRANT REPLICATION
SLAVE,RELOAD,SUPER ON *.* TO westos@'172.25.254.2'
IDENTIFIED BY 'westos';
mysql>Flush privileges;
mysql> show master status;
在vm3上添加配置:
vim /etc/my.cnf
server-id=3
/etc/init.d/mysqld start
在vm3上执行一下命令:
mysql> change master to
master_host='', master_user='westos',
master_password='westos',
master_log_file='mysql-bin.000001',master_log_pos=217;
mysql> slave start;
mysql> show slave status\G;
.....
Slave_IO_Running: Yes ##数据发送过来,保存到reloy.log
如果数据没有发送过来,如果是NO的话
Slave_SQL_Running: Yes ##用于作回放reloy.log 单线程
延迟更高 解决:并行复制
.....
7.全同步(无中心化)
通过一致性协议 -> 推送到每一个节点 -> 判断是否可执行 -> 可执行全部节点执行,不可执行的话,全部节点不执行
全同步的实验步骤:
注意:将前面的所有服务全部停掉,并且删除服务
/etc/init.d/mysqld stop
chkconfig mysqld off
cd /var/lib/mysql
ls
rm -fr *
日至存储格式 奇数节点(最多9个,最少3个)
vim /etc/my.cnf
server_id=1
gtid_mode=ON
enforce_gtid_consistency=ON
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format=ROW
transaction_write_set_extraction=XXHASH64
loose- group_replication_group_name="b8b8d7dd-9312-4539-971c-b013f49d6e51" ##组名 UUID 可以随意写
loose-group_replication_start_on_boot=off
loose-group_replication_local_address=
"172.25.0.1:24901"
loose-group_replication_group_seeds=
"172.25.0.1:24901,172.25.0.2:24901,172.25.0.3:24901"
##启动数据库示例 组所有节点
loose-group_replication_bootstrap_group=off
loose-group_replication_ip_whitelist="172.25.0.0/24"
loose-group_replication_enforce_update_everywhere_checks=on
loose-group_replication_single_primary_mode=off
默认单组模式 将其设置为off
##默认不生效,数据库没有插件,加参数loose,不会影响数据库的启动
/etc/init.d/mysqld restart
mysql -p
SET SQL_LOG_BIN=0; 关闭二进制日志
GRANT REPLICATION SLAVE ON *.* TO
repl_user@'%' IDENTIFIED BY 'Wang+1212';
创建用户
reset master ##清空原数据
SET SQL_LOG_BIN=1; 开启二进制日志
CHANGE MASTER TO
MASTER_USER='repl_user',MASTER_PASSWORD='Wang+1212' FOR CHANNEL
'group_replication_recovery'; ##添加到组中
INSTALL PLUGIN group_replication
SONAME 'group_replication.so';
在第一个节点上做这个操作:初始化节点,设置组的主节点
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL
group_replication_bootstrap_group=OFF;
在其余的节点执行:
START GROUP_REPLICATION;
mysql> select * from
performance_schema.replication_group_members;
在vm1上:
mysql> select * from
performance_schema.replication_group_members;
mysql> create databases wf;
mysql> use wf;
mysql> create table t1 (c1 int
primary key ,c2 text not null);
mysql> insert into t1 values
(1,'111');
测试:
分别在不同的服务器上,添加信息