(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.具体需要三个线程来操作:
(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线程。
mysql主机:server1 172.25.19.1
mysql从机:server2 172.25.19.2
Mysql复制特点:
1.异步复制:主节点中一个用户请求一个写操作时,主接点不需要把写的数据在本地操作完成同时发送给从服务器并等待从服务器反馈写入完成,在响应用户,主机点只需要把写入操作在本地完成,就响应用户。但是,从节点中的数据有可能会落后主服务
2.这种复制方法容易造成主从数据不一致。
默认情况下,MySQL的复制功能是异步的,异步复制可以提供最佳的性能, 主库把binlog日志发送给从库,这一动作就结束了,并不会验证从库是否接收完毕,这一过程,也就意味着有可能出现当主服务器或从服务器端发生故障的时候,有可能从服务器没有接收到主服务器发送过来的binlog日志,这就会造成主服务器和从服务器的数据不一致,甚至在恢复时造成数据的丢失
mysql-community-client-5.7.24-1.el7.x86_64.rpm
mysql-community-common-5.7.24-1.el7.x86_64.rpm
mysql-community-libs-5.7.24-1.el7.x86_64.rpm
mysql-community-libs-compat-5.7.24-1.el7.x86_64.rpm
mysql-community-server-5.7.24-1.el7.x86_64.rpm
[root@server1 ~]# yum install -y mysql-community-client-5.7.24-1.el7.x86_64.rpm mysql-community-common-5.7.24-1.el7.x86_64.rpm mysql-community-libs-5.7.24-1.el7.x86_64.rpm mysql-community-libs-compat-5.7.24-1.el7.x86_64.rpm mysql-community-server-5.7.24-1.el7.x86_64.rpm
[root@server1 ~]# vim /etc/my.cnf ##主配置文件
[root@server1 ~]# systemctl start mysqld ##开启服务
[root@server1 ~]# grep password /var/log/mysqld.log ##查看初始密码
2019-01-16T11:16:43.929867Z 1 [Note] A temporary password is generated for root@localhost: 5Ufs1iSy#wjk
[root@server1 ~]# mysql_secure_installation ##mysql初始化设置
Enter password for user root: ##输入初始化密码
New password: Redhat-1 ##输入新密码(大小写+符号+数字,必须8位)
Re-enter new password:
Change the password for root ? ((Press y|Y for Yes, any other key for No) :
Remove anonymous users? (Press y|Y for Yes, any other key for No) : ##删除匿名用户
Disallow root login remotely? (Press y|Y for Yes, any other key for No) : ##不允许root远程登陆
Remove test database and access to it? (Press y|Y for Yes, any other key for No) : ##删除测试数据库并登陆
Reload privilege tables now? (Press y|Y for Yes, any other key for No) : ##重新加载权限表
[root@server1 ~]# mysql -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
[root@server1 ~]# scp mysql-community-client-5.7.24-1.el7.x86_64.rpm mysql-community-common-5.7.24-1.el7.x86_64.rpm mysql-community-libs-5.7.24-1.el7.x86_64.rpm mysql-community-libs-compat-5.7.24-1.el7.x86_64.rpm mysql-community-server-5.7.24-1.el7.x86_64.rpm [email protected]:/root
[root@server2 ~]# yum install * -y
[root@server2 ~]# vim /etc/my.cnf ##主配置文件
[root@server2 ~]# systemctl start mysqld
[root@server2 ~]# grep password /var/log/mysqld.log ##查看初始密码
[root@server2 ~]# mysql_secure_installation
Enter password for user root: ##输入初始化密码
New password: Redhat-1 ##输入新密码(大小写+符号+数字,必须8位)
Re-enter new password:
Change the password for root ? ((Press y|Y for Yes, any other key for No) :
Remove anonymous users? (Press y|Y for Yes, any other key for No) : ##删除匿名用户
Disallow root login remotely? (Press y|Y for Yes, any other key for No) : ##不允许root远程登陆
Remove test database and access to it? (Press y|Y for Yes, any other key for No) : ##删除测试数据库并登陆
Reload privilege tables now? (Press y|Y for Yes, any other key for No) : ##重新加载权限表
[root@server1 ~]# mysql -p
Enter password:
mysql> grant replication slave on *.* to repl@'172.25.19.%' identified by 'Redhat-1'; ##在主库上建立账户并授权;repl:master用户;172.25.19.% :这个网段的ip
mysql> show master status; ##查看主库状态。 二进制文件;pos号
[root@server1 ~]# cd /var/lib/mysql
[root@server1 mysql]# cat mysql-bin.index ##索引二进制日志文件
./mysql-bin.000001 ##这些里面记录了所有sql语句
./mysql-bin.000002
[root@server2 ~]# mysql -h 172.25.19.1 -u repl -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
[root@server2 ~]# mysql -p
Enter password:
mysql> change master to master_host='172.25.19.1',master_user='repl',master_password='Redhat-1',master_log_file='mysql-bin.000002',master_log_pos=983; ##在从库上建立账户并授权。master主机ip;主从复制用户;用户密码;二进制文件;pos号
mysql> start slave; ###开启slave
mysql> show slave status\G ##查看slave状态
*************************** 1. row ***************************
Slave_IO_State:
Master_Host: 172.25.19.1 ##master
Master_User: repl ##主从复制用户
Master_Port: 3306 ##服务接口3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000002 ##二进制文件(哪个文件)
Read_Master_Log_Pos: 983 ##pos号(的哪个位置)
Relay_Log_File: server2-relay-bin.000001 ##复制到哪个文件
Relay_Log_Pos: 4 ##从这个文件的哪个位置开始输入
Relay_Master_Log_File: mysql-bin.000002
Slave_IO_Running: Yes ##当为No:IO连接错误
Slave_SQL_Running: Yes ##当为No:数据不一致
......
注:
当看到Slave_IO_Running: Yes以及Slave_SQL_Running: Yes,则表示slave库已经正常运行了
当出现Slave_IO_Running: Connecting的提示时,说明主库和从库没有连接上,有以下三点原因:
1.网络问题:检查网络连接是否能够连接上
2.密码或POS号错误:查看pos号和主库的号是否对应
3.防火墙的问题:查看主库防火墙的策略,数据库是否拒绝外来连接,然后做相应的改动
1)主库建库、建表
[root@server1 ~]# mysql -p
Enter password:
mysql> create database westos;
mysql> use westos;
mysql> create table userlist(
-> username varchar(15) not null,
-> password varchar(15) not null);
mysql> desc userlist;
mysql> insert into userlist values('user1','123');
mysql> insert into userlist values('user2','456');
2)在slave从库上看到master主库上建立的库和表信息
[root@server2 ~]# mysql -p
Enter password:
mysql> use westos;
mysql> select * from userlist;
GTID是一个基于原始mysql服务器生成的一个已经被成功执行的全局事务ID,它由服务器ID以及事务ID组合而成。这个全局事务ID不仅仅在原始服务器器上唯一,在所有存在主从关系 的mysql服务器上也是唯一的。正是因为这样一个特性使得mysql的主从复制变得更加简单,以及数据库一致性更可靠。
GTID的优势:
1)更简单的实现failover,不用以前那样在需要找log_file和log_pos;
2)更简单的搭建主从复制;
3)比传统的复制更加安全;
4)GTID是连续的没有空洞的,保证数据的一致性,零丢失。
GTID的工作原理:
1)当一个事务在主库端执行并提交时,产生GTID,一同记录到binlog日志中;
2)binlog传输到slave,并存储到slave的relaylog后,读取这个GTID的这个值设置gtid_next变量,即告诉Slave,下一个要执行的GTID值;
3)sql线程从relay log中获取GTID,然后对比slave端的binlog是否有该GTID;
4)如果有记录,说明该GTID的事务已经执行,slave会忽略;
5)如果没有记录,slave就会执行该GTID事务,并记录该GTID到自身的binlog,在读取执行事务前会先检查其他session持有该GTID,确保不被重复执行;
6)在解析过程中会判断是否有主键,如果没有就用二级索引,如果没有就用全部扫描。
[root@server1 mysql]# cat auto.cnf ##查看uuid
[auto]
server-uuid=3472d0a6-1980-11e9-b8d1-525400963735
[root@server1 mysql]# mysqlbinlog mysql-bin.000002 ##专门可看二进制文件的工具
[root@server1 mysql]# vim /etc/my.cnf
加入:
[root@server1 mysql]# systemctl restart mysqld
[root@server2 mysql]# vim /etc/my.cnf
加入:
[root@server2 mysql]# systemctl restart mysqld
注:也可用下面这种方法临时配置:
mysql> stop slave;
mysql> change master to master_host='172.25.19.1', master_user='repl', master_password='Redhat-1', master_auto_position=1; ###自动更新master_log_file和pos
mysql> start slave;
[root@server1 mysql]# mysql -p
Enter password:
mysql> use westos;
mysql> insert into userlist values('user3','777');
mysql> use mysql;
mysql> select * from gtid_executed;
[root@server2 mysql]# mysql -p
Enter password:
mysql> use westos;
mysql> select * from userlist;
mysql> show slave status\G ##查看slave状态
*************************** 1. row ***************************
......
Slave_IO_Running: Yes
Slave_SQL_Running: Yes ##两个yes
......
Master_UUID: 3472d0a6-1980-11e9-b8d1-525400963735 ##master的uuid
......
Retrieved_Gtid_Set: 3472d0a6-1980-11e9-b8d1-525400963735:1 ##gtid号
Executed_Gtid_Set: 3472d0a6-1980-11e9-b8d1-525400963735:1 ##以gtid方式的第一次复制
......
一般主从复制,有三个线程参与,都是单线程:Binlog Dump(主) —–>IO Thread (从) —–> SQL Thread(从)。复制出现延迟一般出在两个地方
1)SQL线程忙不过来(可能需要应用数据量较大,可能和从库本身的一些操作有锁和资源的冲突;主库可以并发写,SQL线程不可以:主要原因)
2)网络抖动导致IO线程复制延迟(次要原因)。
多线程的思路就是把sql_thread 变成分发线程,然后由一组worker_thread来负责执行。
1)MySQL从5.6开始有了SQL Thread多个的概念,可以并发还原数据,即并行复制技术。
2)在MySQL 5.7中,引入了基于组提交的并行复制(Enhanced Multi-threaded Slaves):
设置参数 :
slave_parallel_workers>0 (一般打开16个线程)
global.slave_parallel_type=‘LOGICAL_CLOCK’,
其中,变量slave-parallel-type可以有两个值:
DATABASE 默认值:基于库的并行复制方式,若操作一个库,则只由一个线程负责,其他线程闲置
LOGICAL_CLOCK:基于组提交的并行复制方式,一个事件对应一个线程。
这个两阶段提交不是分布式事务的两阶段提交,而是在开启binlog之后,redo与binlog的两阶段提交。 两阶段提交,首先redo log prepare,然后写binlog,最后redo log commit。
prepare阶段:
主要进行redo log 的写入(具体点说是写入磁盘)。但此时在mysql内部并不认为事务已经commit完成。
commit阶段:
主要进行binlog的写入(先利用write()将binlog内存数据写入文件系统缓存,然后利用fsync()从文件系统缓存刷到磁盘上),最后再调用存储引擎
组提交分为redo log的组提交和binlog 组提交
1)redo log的组提交
WAL(Write-Ahead-Logging)是实现事务持久性的一个常用技术,其基本原理就是利用顺序写redo到磁盘来避免磁盘页面的随机写,提高IO效率的同时,保证了事务的持久性。虽然可以顺序写redo日志,但仍然需要一次日志刷盘动作,受限于磁盘IO,这个操作依然是事务并发的瓶颈。
组提交思想就是,将多个redo刷盘动作合并,减少磁盘顺序写的次数。
Innodb的日志系统里面,每条redo log都有一个LSN,LSN是单调递增的。每个事务执行更新操作都会包含一条或多条redo log,各个事务将日志拷贝到log_sys_buffer时,都会获取当前最大的LSN,因此可以保证不同事务的LSN不会重复。
a.获取 log_mutex
b.若flushed_to_disk_lsn>=lsn,表示日志已经被刷盘,跳转e
c.若 current_flush_lsn>=lsn,表示日志正在刷盘中,跳转e后进入等待状态
d.将小于LSN的日志刷盘(flush and sync)
e.退出log_mutex
2)Abinlog的组提交
MySQL 5.6 引入BLGC.二进制日志的提交过程分成三个阶段,Flush stage、Sync stage、Commit stage。
那么事务提交过程简化为:
存储引擎(InnoDB) Prepare —-> 数据库上层(Binary Log) Flush Stage —-> Sync Stage —-> 调存储引擎(InnoDB)Commit stage.
每个stage阶段都有各自的队列,使每个session的事务进行排队。当一个线程注册了一个空队列,该线程就视为该队列的leader,后注册到该队列的线程为follower,leader控制队列中follower的行为。leader同时带领当前队列的所有follower到下一个stage去执行,当遇到下一个stage并非空队列,此时leader可以变成follower到此队列中(注:follower的线程不可能变成leader)
在 Flush stage:所有已经注册线程都将写入binary log缓存
在Sync stage :binary log缓存的数据将会sync到磁盘,当sync_binlog=1时所有该队列事务的二进制日志缓存永久写入磁盘
在 Commit stage:leader根据顺序调用存储引擎提交事务。
【server1】
mysql> show processlist;
【server2】
mysql> show processlist;
[root@server2 mysql]# vim /etc/my.cnf
###数据写入表,不写入文件
[root@server2 mysql]# systemctl restart mysqld
mysql> show processlist;
注:
写入文件查看
[root@server2 mysql]# cat master.info
[root@server2 mysql]# cat relay-log.info
写入表查看方式
mysql>use mysql;
mysql> select * from slave_master_info;
mysql> mysql> select * from slave_relay_log_info;
介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到relay log中才返回给客户端。相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,这个延迟最少是一个TCP/IP往返的时间。所以,半同步复制最好在低延时的网络中使用。
(1)MySQL 5.5 引入了一种半同步复制模式。该模式可以确保从服务器接收完主服务器发送的binlog日志文件并写入到自己的中继日志relay log里,然后会给主服务器一个反馈,告诉主服务器已经接收完毕,这时主服务线程才返回给当前session告知操作完成。
(2)当出现超时情况是,主服务器会暂时切换到异步复制模式,直到至少有一个从服务器从及时收到信息为止。
(3)中继日志的自我修复:
从MySQL 5.5.X 版本开始,增加了relay_log_recovery参数,这个参数的作用是:当slave从库宕机后,假如relay.log损坏了,导致一部分中继日志没有处理,则自动放弃所有未执行的relay-log,并且重新从master上获取日志,这样就保证了relay-log的完整性。默认情况下该功能是关闭的,将relay_log_recovery的值设置为1时,可在slave从库上开启该功能,建议开启。
(4)半同步复制与异步复制的切换:
半同步复制的工作原理就是当slave从库IO_Thread线程将binlog日志接收完毕之后,要给master主库一个确认,如果rpl_semi_sync_master_timeout=10000 (10秒)超过10秒未收到slave从库的接受确认信号,那么就会自动切换为传统的异步复制模式。
1)安装插件
mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; ##安装master插件
mysql> SELECT PLUGIN_NAME,PLUGIN_STATUS
-> FROM INFORMATION_SCHEMA.PLUGINS
-> WHERE PLUGIN_NAME LIKE '%semi%'; ##查看配置是否成功
2)对比半同步开启前后的状态变量
mysql> mysql> show status like 'rpl%';
mysql> set global rpl_semi_sync_master_enabled=ON; ##启动半同步复制
mysql> show status like 'rpl%';
mysql> show variables like 'rpl%'; ###环境变量
mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so'; ##安装slave插件
mysql> SET GLOBAL rpl_semi_sync_slave_enabled=1; ##启动半同步复制
mysql> STOP SLAVE IO_THREAD; ##关闭IO
mysql> START SLAVE IO_THREAD; ##开启IO -----重启IO线程,若没有重启,则默认还是异步
【master】
mysql> show status like 'rpl%';
1)实验一:半异步复制,并计成功数
mysql> use westos;
mysql> insert into userlist values('user5','555'); ##插入数据
mysql> show status like 'rpl%';
2)实验二:超过时间,则自动转异步。半同步失败数+1
【slave】
mysql> STOP SLAVE IO_THREAD; ## IO线程关闭
【master】
mysql> insert into userlist values('user6','66');
mysql> insert into userlist values('user7','66');
mysql> show status like 'rpl%'; ##IO线程已关,转为异步复制过去
[root@server1 ~]# vim /etc/my.cnf
[root@server2 ~]# vim /etc/my.cnf
文件可加参数:
1、rpl_semi_sync_master_enabled = 1,表示在master上已经开启了半同步复制模式;
2、rpl_semi_sync_master_timeout = 10000,表示如果主库在某次事务中的等待时间超过10000毫秒,则降级为异步复制模式,不在等待slave从库。如果主库再次探测到slave从恢复了,则会自动切换回半同步复制模式;
3、rpl_semi_sync_master_wait_no_slave ,表示是否允许master每个事务提交后都要等待slave的接收确认信号。默认为ON,即每一个事务都会等待。如果为OFF,则slave追赶上之后,也不会开启半同步复制模式,需要手工开启;
4、rpl_semi_sync_master_trace_level = 32,指用于开启半同步复制模式时的调试级别,默认为32。
可以看出,本次实验在配置Master的时候,只设置了1,其他的都采取的默认设置。