资讯详情

主从复制读写分离保姆级教学

前言

一、案例前置知识点

1、 mysq复制类型的支持

一. 主从复制

1.主从复制

??1.1 同步复制

??1.1.1.主从同步机制

??1.1.2.配置主从同步

??1.1.3.配置主服务器

??1.1.4.从服务器配置

??1.1.4.用户同步备份

??1.1.备份原始文件

??1.1.6.主要从同步技巧和排错开始

??1.2异步复制

??1.2.1异步复制配置

??1.2.2.打开主库Binlog,并设置server-id

??1.2.3、设置读

??1.2.4.获取主库的日志名和偏移量

??1.2.备份主库数据

??1.2.6.恢复主库的写作操作

??1.2.7.将备份的主库数据从库中复制到

??1.2.8、从库配置

??1.2.9、启动从库

??1.2.10.从库设置主从信息

??1.2.11、从库启动slave线程,并检查

??1.2.12测试主从

??1.2.13、排错

??1.3半同步复制

??1.3.1、先确认MySQL服务器是否支持动态增加插件

??1.3.2 、对用插件分别安装在主从库上

??1.3.4.在主从库中打开半同步复制

??1.3.5.检查主库半同步复制状态

??1.4 半同步复制增强

??1.5全量复制

??1.5.1.查看主库现有数据库

??1.5.锁定主数据库

??1.5.3.开始备份主数据库

??1.6、增量复制

??1.7.多线程复制

二. Mysql的主从复制

编辑

??2.1 一主一从

??2.1.1 window、linux之间的通讯

??2.1.2 linux、linux之间的通讯

??2.2 双主双从

??2.2.1.配置双主机

三. Redis的主从复制

??3.1.主从复制

??3.1.1、全量复制

??3.1.2、增量复制

??3.1.3、部分复制

??3.2、Redis主从复制

??3.2.1、启动redis服务端

??3.2. 2、服务器ip、端口命令

??3.2.3.获取添加的数据。

??3.2. 4.获取在执行中slaveof命令之前master服务器数据。

??3.2.5.测试所有服务器是否可以添加数据

??3.3、哨兵模式

??3.4java代码结合

四、mysql读写分离原理

??1.基于程序代码内部实现

??2、 基于中间代理的实现

??3、MySQLProxy介绍

五、主从复制可能出现的问题


前言

主要介绍mysql主从复制和redis的主从复制 从浅到深理解原理以及如何操作 并参考了许多博主的文章

一、案例前置知识点

1、 mysq复制类型的支持

1) 复制基于的复制。在服务器上执行sql在服务器上执行相同的语句,mysql基于语句的默认复制,执行效率高。

2) 基于行的复制。复制过去的变化,而不是从服务器上执行命令。

3) 混合类型的复制。默认采用基于语句的复制,一旦发现基于语句无法精确复制时,就会采用基于行的复制。

2、 复制工作流程

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 thread(sql处理过程的最后一步,sql线程从中继日志读取事件,重放事件并更新slave数据,使其与master中的数据一致,只要该线程与I/O线程一致,中继日志通常位于os因此,中继日志的成本很小。

cb8ff6aee13f68b8d99acad9f68564d0.png

redis主从一开始就复制,mycat主从复制接入点,缺点是会导致延迟

一. 主从复制

主从复制是指数据可以从一个MySQL数据库服务器主节点复制到一个或多个从节点

那么主从复制的原理对应什么呢?呢?

主从复制的优缺点如下: 优点:

主从复制可以保证数据的安全,主机停机后可以继续工作,提高服务器的整体性能。

1 、数据预热,如果主数据库停机,可以继续从数据库工作,避免数据丢失,更好地实现负载平衡。 2.降低磁盘访问频率,提高服务器&nsp; 性能; 3、读写分离,使数据库能支持更大的并发;

1、主从复制方式

 🤙1.1 同步复制  

         所谓的同步复制,意思是master的变化,必须等待slave-1,slave-2,...,slave-n完成后才能 返回。这样,显然不可取,也不是MYSQL复制的默认设置。比如,在WEB前端页面上,用户增加了条记录,需要等待很长时间。

使用主从同步的好处:

  • 通过增加从服务器来提高数据库的性能,在主服务器上执行写入和更新,在从服务器上向外提供读功能,可以动态地调整从服务器的数量,从而调整整个数据库的性能。
  • 提高数据安全-因为数据已复制到从服务器,从服务器可以终止复制进程,所以,可以在从服务器上备份而不破坏主服务器相应数据
  • 在主服务器上生成实时数据,而在从服务器上分析这些数据,从而提高主服务器的性能  

,mysql是异步复制的,而MySQL Cluster是同步复制的。有很多种主从同步的方法,但核心的方法有两种,Statement Based Replication(SBR)基于SQL语句的复制,另一种是Row Based Replication(RBR)基于行的复制,也可以使用Mixed Based Replication(MBR)。在mysql5.6中,默认使用的是SBR。而mysql 5.6.5和往后的版本是基于global transaction identifiers(GTIDs)来进行事务复制。当使用GTIDs时可以大大简化复制过程,因为GTIDs完全基于事务,只要在主服务器上提交了事务,那么从服务器就一定会执行该事务。

通过设置服务器的系统变量binlog_format来指定要使用的格式:

1.SBR:当使用二进制日志时,主服务器会把SQL语句写入到日志中,然后从服务器会执行该日志,这就是SBR,在mysql5.1.4之前的版本都只能使用这种格式。使用SBR会有如下 🎽长处:

  1. 日志文件更小
  2. 记录了所有的语句,可以用来日后审计

 🤡弊端:

  1. 使用如下函数的语句不能被正确地复制:load_file(); uuid(), uuid_short(); user(); found_rows(); sysdate(); get_lock(); is_free_lock(); is_used_lock(); master_pos_wait(); rand(); release_lock(); sleep(); version();
  2. 在日志中出现如下警告信息的不能正确地复制:[Warning] Statement is not safe to log in statement format.
  3. 或者在客户端中出现show warnings
  4. Insert … select语句会执行大量的行级锁表
  5. Update语句会执行大量的行级锁表来扫描整个表

2.RBR:主服务器把表的行变化作为事件写入到二进制日志中,主服务器把代表了行变化的事件复制到从服务中,使用RBR的;

🎽长处:

  1. 所有的数据变化都是被复制,这是最安全的复制方式
  2. 更少的行级锁表

🤡弊端:

  1. 日志会很大
  2. 不能通过查看日志来审计执行过的sql语句,不过可以通过使用mysqlbinlog  
  3. --base64-output=decode-rows --verbose来查看数据的 变动
  4. MBR:既使用SBR也使用RBR,默认使用SBR

🌈1.1.1、主从同步机制

Mysql服务器之间的主从同步是基于二进制日志机制,主服务器使用二进制日志来记录数据库的变动情况,从服务器通过读取和执行该日志文件来保持和主服务器的数据一致。

在使用二进制日志时,主服务器的所有操作都会被记录下来,然后从服务器会接收到该日志的一个副本。从服务器可以指定执行该日志中的哪一类事件(譬如只插入数据或者只更新数据),默认会执行日志中的所有语句。

每一个从服务器会记录关于二进制日志的信息:文件名和已经处理过的语句,这样意味着不同的从服务器可以分别执行同一个二进制日志的不同部分,并且从服务器可以随时连接或者中断和服务器的连接。

主服务器和每一个从服务器都必须配置一个唯一的ID号(在my.cnf文件的[mysqld]模块下有一个server-id配置项),另外,每一个从服务器还需要通过CHANGE MASTER TO语句来配置它要连接的主服务器的ip地址,日志文件名称和该日志里面的位置(这些信息存储在主服务器的数据库里);

🌈1.1.2、配置主从同步

有很多种配置主从同步的方法,可以总结为如下的步骤:

1.在主服务器上,必须开启二进制日志机制和配置一个独立的ID

2.在每一个从服务器上,配置一个唯一的ID,创建一个用来专门复制主服务器数据的账号

3.在开始复制进程前,在主服务器上记录二进制文件的位置信息

4.如果在开始复制之前,数据库中已经有数据,就必须先创建一个数据快照(可以使用mysqldump导出数据库,或者直接复制数据文件)

5.配置从服务器要连接的主服务器的IP地址和登陆授权,二进制日志文件名和位置  

🌈1.1.3、配置主服务器

🍎1.更改配置文件,首先检查你的主服务器上的my.cnf文件中是否已经在[mysqld]模块下配置了log-bin和server-id;

[mysqld]
 
log-bin=mysql-bin
 
server-id=1

上面的log-bin和server-id的值都是可以改为其他值的,如果没有上面的配置,首先关闭mysql服务器,然后添加上去,接着重启服务器;

🍎2.创建用户,每一个从服务器都需要用到一个账户名和密码来连接主服务器,可以为每一个从服务器都创建一个账户,也可以让全部服务器使用同一个账户。下面就为同一个ip网段的所有从服务器创建一个只能进行主从同步的账户。

首先登陆mysql,然后创建一个用户名为rep,密码为123456的账户,该账户可以被192.168.253网段下的所有ip地址使用,且该账户只能进行主从同步  

mysql > grant replication slave on *.* to ‘rep’@‘192.168.253.%’ identified by ‘123456’;

🍎3.获取二进制日志的信息并导出数据库,步骤:

首先登陆数据库,然后刷新所有的表,同时给数据库加上一把锁,阻止对数据库进行任何的写操作;

mysql > flush tables with read lock;

然后执行下面的语句获取二进制日志的信息:

mysql > show master status;

 获得如图信息:

 

File的值是当前使用的二进制日志的文件名,Position是该日志里面的位置信息(不需要纠结这个究竟代表什么),记住这两个值,会在下面配置从服务器时用到。

注意:如果之前的服务器并没有配置使用二进制日志,那么使用上面的sql语句会显示空,在锁表之后,再导出数据库里的数据(如果数据库里没有数据,可以忽略这一步)。  

[root@localhost backup]# mysqldump -uroot -p'123456' -S /data/3306/data/mysql.sock --all-databases > /server/backup/mysql_bak.$(date +%F).sql

如果数据量很大,可以在导出时就压缩为原来的大概三分之一:

[root@localhost backup]# mysqldump -uroot -p'123456' -S /data/3306/data/mysql.sock --all-databases | gzip > /server/backup/mysql_bak.$(date +%F).sql.gz

这时可以对数据库解锁,恢复对主数据库的操作:

mysql > unlock tables;

🌈1.1.4、配置从服务器

首先检查从服务器上的my.cnf文件中是否已经在[mysqld]模块下配置leserver-id

[mysqld]
 
server-id=2

注意上面的server-id的值都是可以改为其他值的(建议更改为ip地址的最后一个字段),如果没有上面的配置,首先关闭mysql服务器,然后添加上去,接着重启服务器。

如果有多个从服务器上,那么每个服务器上配置的server-id都必须不一致。从服务器上没必要配置log-bin,当然也可以配置log-bin选项,因为可以在从服务器上进行数据备份和灾难恢复,或者某一天让这个从服务器变成一个主服务器。

如果主服务器导出了数据,下面就导入该文件,如果主服务器没有数据,就忽略这一步。  

[root@localhost ~]# mysql -uroot -p'123456' -S /data/3306/data/mysql.sock < /server/backup/mysql_bak.2015-07-01.sql

如果从主服务器上拿过来的是压缩文件,就先解压再导入

🍋配置同步参数,登陆mysql,输入如下信息:

  mysql> CHANGE MASTER TO   -> MASTER_HOST='master_host_name',   -> MASTER_USER='replication_user_name',   -> MASTER_PASSWORD='replication_password',   -> MASTER_LOG_FILE='recorded_log_file_name',

如图所示:

 🍋启动主从同步进程:

mysql > start slave;

🍋检查状态:

mysql > show slave status \G

 如图所示:

 上面的两个进程都显示YES则表示配置成功。

🌈1.1.4、使用主从同步来备份

使用mysqldump来备份,把主服务器的数据复制到从服务器上,然后备份从服务器的数据,在数据量不是很大的时候使用mysqldump命令,对于很大的数据库,就直接备份数据文件。

步骤:(以下的所有操作都在从服务器上进行)

🧣1.首先暂停从服务器的复制进程  

shell > mysqladmin stop-slave

或者只是暂停SQL进程,从服务器仍然能接收二进制日志的事件,但不会执行这些事件,这样能在重启SQL进程时加快复制进度

shell > mysql -e ‘stop slave sql_thread;’

🧣2.使用mysqldump导出全部或部分的数据库

shell > mysqldump --all-databases > fulldb.dump

🧣3.在导出数据库后,重启复制进程

shell > mysqladmin start-slave

 

🌈1.1.5、备份原始文件

为了保证数据文件的完整性,在备份之前首先关闭从服务器,步骤:

🐳1.关闭从服务器:

shell > mysqladmin shutdown

🐳2.复制数据文件,可以使用压缩命令,假如当前目录就是数据库的数据目录,在my.cnf文件中的配置项datadir的值就是该目录的位置

shell > tar cf /tmp/dbbackup.tar ./data

🐳3.然后再启动mysql服务器

🌈1.1.6、主从同步的小技巧、排错

🎯

主服务器第一次导入数据,如果你从其他地方拿来了要导入到主服务器中的数据,此时只要在主服务器中导入一次即可,因为这些数据会自动发送到从服务器中,在主服务器上使用命令

shell > mysql -h master < other_data.sql

增加从服务器,本来已经至少有一个从服务器时(暂时命名为slave1),决定再添加其余的从服务器(slave2),此时就不需要像上面那样去操作主服务器,只要复制一个已经存在的从服务器就可以了。

🎯2.排错

Slave_IO_Running: NO

这是一个很常见的错误(我也曾对这个错误咬牙切齿),总结起来就三个原因:

  1. 主服务器的网络不通,或者主服务器的防火墙拒绝了外部连接3306端口
  2. 在配置从服务器时,输错了ip地址和密码,或者主服务器在创建用户时写错了用户名和密码
  3. 在配置从服务器时,输错了主服务器的二进制日志信息

排错过程:(主服务器ip:192.168.12.139,从服务器ip:192.168.12.204)

第1步就是检查错误日志,如果不能快速排错,可以按我的步骤试试:

🍎1.首先在从服务器上执行ping程序,确定能ping通主服务器

在从服务器上执行mysq的远程连接:

[root@slave204 log]# mysql -urep -p -h 192.168.12.139 -P3306

如果显示ERROR 1045 (28000): Access denied for user 'test'@'192.168.12.204' (using password: YES)则跳转到第3

🍎2.登陆主服务器的mysql,查看所有的用户

mysql > select user,host from mysql.user;

 上图就是我的错误根源,可以看到用户名完全写错了,先删除错误的用户:

mysql > drop user “rep@192.168.12.%”@”%”;

再重新创建用户:

mysql > grant replication slave on *.* to ‘rep’@‘192.168.12.%’ identified by ‘123456’;
 
mysql > flush privileges;

🍎3.假如用户名没有错,那么如何排除是否是输入的密码错误呢?

额,我也想知道方法。最好就是多输入几遍,或者重新创建用户名和密码来测试。问题还没有解决,转到4

🍎4.在你的防火墙中添加3306端口

[root@localhost mysql]# firewall-cmd --zone=public --add-port=3306/tcp --permanent
 
[root@localhost mysql]# firewall-cmd --reload

再关闭selinux:

[root@slave204 log]# vi /etc/sysconfig/selinux

把SELINUX=enforcing改为SELINUX=disabled

[root@slave204 log]# source /etc/sysconfig/selinux

登录主服务器,查看服务器状态

mysql > show master status \G

然后重新配置一次从服务器,在配置之前首先关闭主从同步进程

mysql > stop slave;

之外的方法,我也没试过了,请踊跃尝试其他方式;

🤙1.2

MySQL的复制的,主从复制至少需要两个MYSQL服务,这些MySQL服务可以分布在不同的服务器上,也可以在同一台服务器上。

MySQL主从异步复制是最常见的复制场景。数据的完整性依赖于主库BINLOG的不丢失,只要主库的BINLOG不丢失,那么就算主库宕机了,我们还可以通过BINLOG把丢失的部分数据通过手工同步到从库上去。

注意:主库宕机的情况下,DBA可以通过mysqlbinlog工具手工访问主库binlog,抽取缺失的日志并同步到从库上去;也可以通过配置高可用MHA架构来自动抽取缺失的数据补全从库,或者启用Global Transaction Identifiers(GTID)来自动抽取缺失binlog到从库。

MySQL在BINLOG中记录事务(或SQL语句),也就是说对于支持事务的的引擎(例如InnoDB)来说,每个事务提交时都需要写BINLOG;对于不支持事务的引擎(例如MyISAM)来说,每个SQL语句执行完成时,都需要写BINLOG。为了保证Binlog的安全,MySQL引入sync_binlog参数来控制BINLOG刷新到磁盘的频率。

命令:  

show variables like 'sync_binlog';

🥝

前提:主从数据库的版本一致

命令:

>grant replication slave on *.* to 'test'@'192.168.142.176' identified by '123';
 
>flush privileges;

🥝1.2.

命令:

vim /mysql/3306/my.cnf
 
[mysqld]
 
log-bin = mysql-bin
 
server-id = 1

🥝1.2.3、设置读锁

在主库上设置读锁定有效,目的是确保在此过程中没有对数据的操作,避免主从数据不一致;

> flush tables with read lock;

🥝1.2.4、获取主库日志名和偏移量

获取主库上当前Binlog的日志名和偏移量,为了在从库中,从此位置开始复制;

命令:

>show master status;

🥝1.2.5、备份主库的数据

我这里的主从库是在一台服务器上,如果不在一台机器上,先将主库数据打包,到后面再传过去就可以了。这里我将主库的数据备份到了/test目录下,其实可以不用备份,后面直接cp到从库,但还是感受一下备份的安全感;

命令:

cp /mysql/3306/data /test

🥝1.2.6、恢复主库的写操作

命令:

> unlock tables;

🥝1.2.7、备份的主库数据拷贝到从库

将第5步中备份的主库数据拷贝到从库上命令:

cp /test /mysql/3307/data

🥝1.2.8、从库配置

从库配置文件中增加server-id,注意这个值是唯一的,要区别于主库和其他从库;

命令:

vim /mysql/3307/my.cnf
 
[mysqld]
 
server-id = 2

🥝1.2.9、启动从库

启动从库的时候,我们加一个--skip-slave-start参数,表示这个从库启动是不自动同步,也就是不启动主从复制进程,方便后面对从库的进一步配置

/usr/local/mysql/bin/mysqld_safe --defaults-file=/mysql/3308/my.cnf --skip-slave-start

注意:由于我这里用的是MySQL的多实例,所以启动的时候需要用--defaults-file参数指定一下具体实例的my.cnf文件,如果主从库不再同一台机器上,就不需要加这个参数了,默认是/etc/my.cnf

🥝1.2.10、从库设置主从信息

这里要注意的是配置要参照之前在主库的配置;

> change master to
 
-> master_host='192.168.142.176',        #主库服务器的IP
 
-> master_port=3306,                      #主库端口
 
-> master_user='test',                     #主库用于复制的用户
 
-> master_password='123',                  #用户密码
 
-> master_log_file='mysql-bin.000002',     #主库的Binlog日志名
 
-> master_log_pos=570;                     #Binlog日志的偏移量(从什么位置开始复制)

🥝1.2.11、从库启动slave线程,并检查

>start slave;
 
>show processlist \G

 

🥝1.2.12、测试主从复制

主库上创建一张表,并加入数据,看从库是否复制

>use test
 
>create table emp (id int,name varchar(11));
 
>insert into emp values(1,’tom’),(2,’jerry’);

 

  然后在从库上查看:

>use test
 
>show tables;
 
>select * from emp;

  可以看到,已经成功复制过去了;

🥝1.2.

如果在从库发现没有复制过来数据,先看看slave的状态,试用一下命令:

>show slave status \G

 

  • 上图中,前面几行是主库的信息,红色框内的两个参数必须要是yes,才能正确复制
  • Slave_IO_Running表示从库的IO线程,Slave_SQL_running表示从库的SQL线程,两个必须都为yes。
  • 如果为no,可以去从库的错误日志(log-err)中查看原因

到这里,MySQL的异步复制就配置完成了~

在异步复制中,主库执行完操作后,写入binlog日志后,就返回客户端,这一动作就结束了,并不会验证从库有没有收到,完不完整,所以这样可能会造成数据的不一致。

说到底,复制过程中数据是否一致,主要取决于Binlog日志的安全性与完整性

在MySQL中,有sync_binlog=n这一参数,他的值表示每进行n次事务提交,MySQL就将Binlog刷新到磁盘。如果这个值为1,就代表每提交一次事务(SQL),就将Binlog往磁盘刷新一次,这样一来,就算数据库宕机了,那么最多只能损失一次事务的数据。

但是,一旦多个事务并发提交时,由于受sync_binlog的限制,MySQL只能按顺序来处理这些请求,另外,高频率的刷新binlog对IO的影响也很大,进一步影响了数据库的性能,所以,一般这个值都设为0或者其他值,在数据的安全性和高并发下的性能之间取得一个平衡。

为了更加有效的保护Binlog的安全性和完整性,MySQL5 .5之后引入了半同步复制;  

🤙1.3

在异步复制中,我们遇到的一个主要问题就是,在复制过程当中,主库不会去验证Binlog有没有成功复制到从库,那如果主库提交一个事务并写入Binlog中后,当从库还没有从主库得到Binlog时,主库宕机了或因磁盘损坏等故障导致该事务的Binlog丢失了,那从库就不会得到这个事务,也就造成了主从数据的不一致。

而半同步复制,当主库每提交一个事务后,不会立即返回,而是等待其中一个从库接收到Binlog并成功写入Relay-log中才返回客户端,所以这样就保证了一个事务至少有两份日志,一份保存在主库的Binlog,另一份保存在其中一个从库的Relay-log中,从而保证了数据的安全性和一致性。

另外,在半同步复制时,如果主库的一个事务提交成功了,在推送到从库的过程当中,从库宕机了或网络故障,导致从库并没有接收到这个事务的Binlog,此时主库会等待一段时间(这个时间由rpl_semi_sync_master_timeout的毫秒数决定),如果这个时间过后还无法推送到从库,那MySQL会自动从半同步复制切换为异步复制,当从库恢复正常连接到主库后,主库又会自动切换回半同步复制。

半同步复制的“半”体现在,虽然主从库的Binlog是同步的,但主库不会等待从库执行完Relay-log后才返回,而是确认从库接收到Binlog,达到主从Binlog同步的目的后就返回了,所以从库的数据对于主库来说还是有延时的,这个延时就是从库执行Relay-log的时间。所以只能称为半同步。

配置半同步复制:

半同步模式是作为MySQL5.5的一个插件来实现的,主从库使用的插件不一样

🐳1.3.1、先确认MySQL服务器是否支持动态增加插件

mysql>select @@have_dynamic_loading

 

🐳1.3.

插件一般默认在MySQL安装目录/lib/plugin下,可以去查看一下是否存在

主库的插件是semisync_master.so,从库是semisync_slave.so

mysql>install plugin rpl_semi_sync_master soname ‘semisync_master.so’;
 
mysql>install plugin rpl_semi_sync_slave soname ‘semisync_slave.so’;

🐳

mysql>select * from mysql.plugin;

 

 

🐳1.3.4、在主从库中开启半同步复制

mysql>set global rpl_semi_sync_master_enabled=1;
 
mysql>set global rpl_semi_sync_master_timeout=30000;

mysql>set global rpl_semi_sync_slave_enabled=1;

注意:如果之前配置的是异步复制,在这里要重启一下从库的IO线程,如果是全新的半同步则不用重启.

重启命令是:mysql>stop slave io_thread;start slave io_thread;

🐳1.3.5、在主库上查看半同步复制的状态

mysql>show status like '%semi_sync';

  在输出信息中,我们重点关注三个参数:

rpl_semi_sync_master_status  OFF/ON    #ON表示半同步复制打开,OFF表示关闭

rpl_semi_sync_master_yes_tx   [number]      #这个数字表示主库当前有几个事务说通过半同步复制到从库的

rpl_semi_sync_master_no_tx   [number]      #表示有几个事务不是通过半同步复制到从库的

🤙1.4 增强半同步复制

前面介绍的复制是异步操作,主库和从库的数据之间难免会存在一定的延迟,这样存在一个隐患:当在主库上写入一个事务并提交成功,而从库尚未得到主库的BINLOG日志时,主库由于磁盘损坏、内存故障、断电等原因意外宕机,导致主库上该事务BINLOG丢失,此时从库就会损失这个事务,从而造成主从不一致。

为了解决这个问题,从MySQL5.5开始,引入了半同步复制,此时的技术暂且称之为传统的半同步复制,因该技术发展到MySQL5.7后,已经演变为增强半同步复制(也成为无损复制)。在异步复制时,主库执行Commit提交操作并写入BINLOG日志后即可成功返回客户端,无需等待BINLOG日志传送给从库,如图所示。  

 

而半同步复制时,为了保证主库上的每一个BINLOG事务都能够被可靠地复制到从库上,主库在每次事务成功提交时,并不及时反馈给前端应用用户,而是等待至少一个从库(详见参数rpl_semi_sync_master_wait_for_slave_count)也接收到BINLOG事务并成功写入中继日志后,主库才返回Commit操作成功给客户端(不管是传统的半同步复制,还是增强的半同步复制,目的都是一样的,只不过两种方式有一个细微地方不同,将在下面说明)

半同步复制保证了事务成功提交后,至少有两份日志记录,一份在主库的BINLOG日志上,另一份在至少一个从库的中继日志Relay Log上,从而更进一步保证了数据的完整性。

在传统的半同步复制中,主库写数据到BINLOG,且执行Commit操作后,会一直等待从库的ACK,即从库写入Relay Log后,并将数据落盘,返回给主库消息,通知主库可以返回前端应用操作成功,这样会出现一个问题,就是实际上主库已经将该事务Commit到了事务引擎层,应用已经可以可以看到数据发生了变化,只是在等待返回而已,如果此时主库宕机,有可能从库还没能写入Relay Log,就会发生主从库不一致。增强半同步复制就是为了解决这个问题,做了微调,即主库写数据到BINLOG后,就开始等待从库的应答ACK,直到至少一个从库写入Relay Log后,并将数据落盘,然后返回给主库消息,通知主库可以执行Commit操作,然后主库开始提交到事务引擎层,应用此时可以看到数据发生了变化。增强半同步复制的大致流程如下图所示。  

 

半同步复制模式下,假如在传送BINLOG日志到从库时,从库宕机或者网络延迟,导致BINLOG并没有即使地传送到从库上,此时主库上的事务会等待一段时间(时间长短由参数rpl_semi_sync_master_timeout设置的毫秒数决定),如果BINLOG在这段时间内都无法成功发送到从库上,则MySQL自动调整复制为异步模式,事务正常返回提交结果给客户端。

半同步复制很大程度上取决于主从库之间的网络情况,往返时延RTT越小决定了从库的实时性越好。通俗地说,主从库之间的网络越快,从库约实时。

注意:往返时延RTT(Round-Trip Time)在计算机网络中是一个重要的性能指标,它表示从发送端发送数据开始到发送端接收到接收端的确认,总共经历的时长(这里可能有点拗口,我们可以理解为TCP三次握手的前两次握手)。

  • 一旦Ack超时,将退化为异步复制模式,那么异步复制的问题也将发送
  • 性能下降,增多至少一个RTT时间
  • 如果超时时间设置很大,然后因为网络原来长时间收不到ACK,用户提交是被挂起的,可用性收到打击(半同步一样存在)  

🤙1.5全量复制

        增量复制和全量复制主要涉及到redis的框架:

     用于初次复制或其它无法进行部分复制的情况,将主节点中的所有数据都发送给从节点,是一个IE非常重型的操作,当数据量较大时,会对主从节点和网络造成很大的开销,而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。

 

1、Redis 内部会发出一个同步命令,刚开始是 Psync 命令,Psync ? -1表示要求 master 主机同步数据; 2、主机会向从机发送 runid (redis-cli info server)和 offset,因为 slave 并没有对应的 offset,所以是全量复制; 3、从机slave会保存主机master的基本信息save masterinf; 4、主节点收到全量复制的命令后,执行bgsave(异步执行),在后台生成RDB文件(快照),并使用一个缓冲区(称为复制缓冲区)记录从现在开始执行的所有命令; 5、主机send RDB发送RDB文件给从机; 6、发送缓冲区数据 7、刷新旧的数据,从节点在载入主节点的数据之前要先将老数据清除; 8、加载RDB文件将数据库状态更新至主节点执行bgsave时的数据库状态和缓冲区数据加载;

1、bgsave时间

2、RDB文件网络传输时间

3、从节点清空数据的时间

4、从节点加载RDB的时间

当决定使用Mysql主从复制的数据库架构时,可能你的工程已经运行了一段时间,这是很常见的场景,比如目前我负责的一个项目,我想改变生产环境的单节点数据库的现状,通过主从复制为读写分离作铺垫。那么就需要两台及以上的节点作为数据库节点,这里所谓的节点不仅仅指服务器,也可能是同一台服务器,不同端口,使用docker进行隔离。

可是我在配置完主从复制后发现,主节点已经存在的数据并没有同步到从节点,当主节点向数据库写数据时,从节点重复这些操作,可从节点本身根本没有创建那些主节点的库,导致运行出错、IO线程坏死,最终从节点挂掉;

🏓1.5.1、查看主库的已有数据库

show databases;

假设其中test数据库是我们想要主从同步的数据库,模拟生产环境,该数据库中已经有了很多数据。

use test;

select * from user;

 

🏓1.5.2、锁定主数据库

锁定主数据库,只允许读取不允许写入,这样做的目的是防止备份过程中或备份完成之后有新数据插入,导致备份数据和主数据数据不一致,同样,这样做也有弊端,可能在锁库期间会影响正常的业务流程,所以我们应使锁库的粒度尽可能小。

flush tables with read lock;

查询主数据库状态,并记下FILE及Position的值:

show master status;

 

🏓1.5.3、开始备份主数据库

✨1.退出mysql终端,执行docker mysql备份命令

docker exec [CONTAINER] /usr/bin/mysqldump -u username --password=xxx [DATABASE] > back.sql

[CONTAINER] 是你自己容器的名字, [DATABASE]是你自己数据库的名字,back.sql是临时产生的备份文件,里面是sql语句, username是你自己的用户名,一般是root,xxx则是你自己的用户密码。该命令的意思是执行docker容器内mysql相关命令:mysqldump,将指定的数据库导出到宿主机当中

我们这里只需要备份test数据库,若要备份全部数据库,[DATABASE]处使用--all-databases

✨2.在主机上执行下列语句

 

图中的warnming是因为我们在命令行输入了密码,所以会有安全警告信息。可以看到,已经在宿主机上生成了back.sql。

在导入备份文件之前,需要在从库中手动建立相应的同名库CREATE DATABASE test;,否则会出现如下报错:

 

找不到相关数据库?

手动创建完数据库后执行下列语句:

cat back.sql | docker exec -i [CONTAINER] /usr/bin/mysql -u username --password=xxx [DATABASE]

unlock tables;

🤙1.6、增量复制

1、如果全量复制过程中,master-slave网络连接断掉,那么salve重新连接master时,会触发增量复制;

2、master直接从自己的backlog中获取部分丢失的数据,发送给slave node,默认backlog就是1MB;

3、msater就是根据slave发送的psync中的offset来从backlog中获取数据的;

Master继续将新的所有收集到的修改命令依次传给slave,完成同步, 主机修改了数据会给予从机修改的数据同步,叫做增量复制;

🤙1.7、多线程复制

在MySQL5.7中,带来了全新的多线程复制技术,解决了当master同一个schema下的数据发生了变更,从库不能并发应用的问题,同时也真正将binlog组提交的优势充分发挥出来,保障了从库并发应用Relay Log的能力。

在MySQL8.0中,多线程复制又进行了技术更新,引入了writeset的概念,而在之前的版本中,如果主库的同一个会话顺序执行多个不同相关对象的事务,例如,先执行了Update A表的数据,又执行了Update B表的数据,那么BINLOG在复制到从库后,这两个事务是不能并行执行的,writeset的到来,突破了这个限制。

1. master 节点上的binlogdump 线程,在slave 与其正常连接的情况下,将binlog 发送到slave 上。

2. slave 节点的I/O Thread ,通过读取master 节点binlog 日志名称以及偏移量信息将其拷贝到本地relay log 日志文件。

3. slave 节点的SQL Thread,该线程读取relay log 日志信息,将在master 节点上提交的事务在本地回放,达到与主库数据保持一致的目的。

MySQL5.5 及以前的复制一般主从复制有三个线程且都是单线程:

Binlog Dump(主) ‐‐> IO Thread(从) ‐‐> SQL Thread(从)。

而master 这边是通过并发线程提交,事务通过LSN 写入binlog;但是Slave 只有一个IO 线程和SQL 线程,是单线程,所以在业务大的情况下就很容易造成主从延时.

如果在MySQL 5.6 版本开启并行复制功能(slave_parallel_workers > 0),那么SQL 线程就变为了coordinator 线程,coordinator 线程主要负责以下两部分内容:

Coordinator+worker(多个)

若判断可以并行执行,那么选择worker 线程执行事务的二进制日志。

若判断不可以并行执行,如该操作是DDL,亦或者是事务跨schema 操作,则等待所有的worker 线程执行完成之后,再执行当前的日志。

这意味着coordinator 线程并不是仅将日志发送给worker 线程,自己也可以回放日志,但是所有可以并行的操作交付由worker 线程完成。

上述机制实现的基于schema 的并行复制,存在的问题是:

这样设计的并行复制效果并不高,如果用户实例仅有一个库,那么就无法实现并行回放,甚至性能会比原来的单线程更差,而单库多表是比多库多表更为常见的一种情形。

MySQL5.7 的MTS(Enhanced Muti‐threadedslaves)MySQL 5.7 引入了新的机制来实现并行复制,不再有基于库的并行复制限制,主要思想就是slave 服务器的回放与主机是一致的,即master 服务器上是怎么并行执行,slave 上就怎样进行并行回放。

mysql v5.7.2 进行了优化,增加了参数slave_parallel_type,参数有两个选项:

LOGICAL_CLOCK:基于逻辑时钟 ,可以在一个DATABASE 中并发执行relay log 事务DATABASE: 基于数据库,v5.6 默认是这个参数,改参数每个库只能一个线程;

slave‐parallel‐type,其可以配置的值有:

DATABASE:默认值,基于库的并行复制方式

LOGICAL_CLOCK:基于组提交的并行复制方式

mysql> show variables like 'slave_parallel%';
+------------------------+----------+
| Variable_name          | Value    |
+------------------------+----------+
| slave_parallel_type    | DATABASE |
| slave_parallel_workers | 0        |
+------------------------+----------+
mysql> stop slave;
mysql> set global slave_parallel_type='LOGICAL_CLOCK';
Query OK, 0 rows affected (0.00 sec)
mysql> set global slave_parallel_type='LOGICAL_CLOCK';
Query OK, 0 rows affected (0.00 sec)
mysql> set global slave_parallel_workers=2;
Query OK, 0 rows affected (0.00 sec)
mysql> start slave;
Query OK, 0 rows affected (0.01 sec)
mysql> show processlist;
+-------+-------------+-----------+------+---------+------+--------------------------------------------------------+------------------+
| Id    | User        | Host      | db   | Command | Time | State                                                  | Info             |
+-------+-------------+-----------+------+---------+------+--------------------------------------------------------+------------------+
| 26077 | root        | localhost | NULL | Query   |    0 | starting                                               | show processlist |
| 26141 | system user |           | NULL | Connect |   40 | Waiting for master to send event                       | NULL             |
| 26142 | system user |           | NULL | Connect |   40 | Slave has read all relay log; waiting for more updates | NULL             |
| 26143 | system user |           | NULL | Connect |   40 | Waiting for an event from Coordinator                  | NULL             |
| 26144 | system user |           | NULL | Connect |   40 | Waiting for an event from Coordinator                  | NULL             |
+-------+-------------+-----------+------+---------+------+--------------------------------------------------------+------------------+
mysql> show variables like 'slave_parallel%';
+------------------------+---------------+
| Variable_name          | Value         |
+------------------------+---------------+
| slave_parallel_type    | LOGICAL_CLOCK |
| slave_parallel_workers | 2             |
+------------------------+---------------+

master_info_repository=TABLE (开启MTS 功能后,会频繁更新master.info,设置为TABLE 减小开销)relay_log_info_repository=TABLE

slave_master_info 记录了首次同步master 的位置relay_log_recovery=ON (slave IO 线程crash,如果relay‐log损坏,则自动放弃所有未执行的relay‐log,重新从master 上获取日志,保证relay‐log 的完整性)

slave_preserve_commit_order=ON (保证提交的顺序性)在slave 上应用事务的顺序是无序的,和relay log 中记录的事务顺序不一样,这样数据一致性是无法保证的,为了保证事务是按照relay log 中记录的顺序来回放,就需要开启参数slave_preserve_commit_order。

虽然mysql5.7 添加MTS 后,虽然slave 可以并行应用relay log,但commit 部分仍然是顺序提交,其中可能会有等待的情况。

 

优化参数:

[mysqld]
bind-address=0.0.0.0
port=3306
datadir=/data/mysql
socket=/data/mysql/mysql.sock
user=mysql
skip-name-resolve
slow_query_log=on
long_query_time=1
slow_query_log_file=/data/mysql/mysql-slow.log
innodb-file-per-table=1
innodb_flush_log_at_trx_commit = 2
log_warnings = 1
connect_timeout = 60
net_read_timeout = 120
performance_schema_max_table_instances = 400
server-id = 2
relay-log = relay-log
relay-log-index = relay-log.index
slave_parallel_type=LOGICAL_CLOCK
slave_parallel_workers=2
master_info_repository=TABLE
relay_log_info_repository=TABLE
relay_log_recovery=ON
slave_preserve_commit_order=ON
log_bin=mysql‐bin
log_slave_updates=ON
 
[mysqld_safe]
log-error=/data/mysql/mysqld.log
pid-file=/data/mysql/mysqld.pid

重启mysqld进入数据库

mysql> show variables like '%slave_parallel_type%';
+---------------------+---------------+
| Variable_name       | Value         |
+---------------------+---------------+
| slave_parallel_type | LOGICAL_CLOCK |
+---------------------+---------------+
mysql> show variables like '%slave_parallel_worker%';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| slave_parallel_workers | 2     |
+------------------------+-------+
mysql> show variables like '%master_info_repository%';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| master_info_repository | TABLE |
+------------------------+-------+
mysql> set global relay_log_info_repository='TABLE';
Query OK, 0 rows affected (0.00 sec)
 
mysql> show variables like '%relay_log_info_repository%';
+---------------------------+-------+
| Variable_name             | Value |
+---------------------------+-------+
| relay_log_info_repository | TABLE |
+---------------------------+-------+
mysql> show variables like '%relay_log_recovery%';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| relay_log_recovery | ON    |
+--------------------+-------+
1 row in set (0.00 sec)
 
mysql> show variables like '%slave_preserve_commit_order%';
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| slave_preserve_commit_order | ON    |
+-----------------------------+-------+

开启slave

mysql> start slave;
Query OK, 0 rows affected (0.00 sec)
 
mysql> show slave  status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.132.121
                  Master_User: replication
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: master-bin.000002
          Read_Master_Log_Pos: 40870
               Relay_Log_File: relay-log.000010
                Relay_Log_Pos: 321
        Relay_Master_Log_File: master-bin.000002
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 40870
              Relay_Log_Space: 689
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 1
                  Master_UUID: 6b00724f-a094-11e9-8f47-000c2991dd19
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: 
                Auto_Position: 0
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 

主库写入数据

[root@master mysql]#mysql -uroot -p123456  -e 'use darren;insert into test values (1);
[root@master mysql]#mysql -uroot -p123456  -e 'use darren;insert into test values (1);
[root@master mysql]#mysql -uroot -p123456
mysql> mysql> show master status;
+-------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+-------------------+
| master-bin.000002 | 41388 | | | |
+-------------------+----------+--------------+------------------+-------------------+

从库

mysql> use mysql;
Database changed
mysql> show tables;
+---------------------------+
| Tables_in_mysql           |
+---------------------------+
| columns_priv              |
| db                        |
| engine_cost               |
| event                     |
| func                      |
| general_log               |
| gtid_executed             |
| help_category             |
| help_keyword              |
| help_relation             |
| help_topic                |
| innodb_index_stats        |
| innodb_table_stats        |
| ndb_binlog_index          |
| plugin                    |
| proc                      |
| procs_priv                |
| proxies_priv              |
| server_cost               |
| servers                   |
| slave_master_info         |
| slave_relay_log_info      |
| slave_worker_info         |
| slow_log                  |
| tables_priv               |
| time_zone                 |
| time_zone_leap_second     |
| time_zone_name            |
| time_zone_transition      |
| time_zone_transition_type |
| user                      |
+---------------------------+
mysql> select * from slave_master_info;
+-----------------+-------------------+----------------+-----------------+-------------+---------------+------+---------------+-------------+--------+------------+----------+------------+---------+------------------------+-----------+------+--------------------+--------------------------------------+-------------+---------+-------------+-----------------------+--------------+-------------+
| Number_of_lines | Master_log_name   | Master_log_pos | Host            | User_name   | User_password | Port | Connect_retry | Enabled_ssl | Ssl_ca | Ssl_capath | Ssl_cert | Ssl_cipher | Ssl_key | Ssl_verify_server_cert | Heartbeat | Bind | Ignored_server_ids | Uuid                                 | Retry_count | Ssl_crl | Ssl_crlpath | Enabled_auto_position | Channel_name | Tls_version |
+-----------------+-------------------+----------------+-----------------+-------------+---------------+------+---------------+-------------+--------+------------+----------+------------+---------+------------------------+-----------+------+--------------------+--------------------------------------+-------------+---------+-------------+-----------------------+--------------+-------------+
|              25 | master-bin.000002 |          40870 | 192.168.132.121 | replication | 1234567       | 3306 |            60 |           0 |        |            |          |            |         |                      0 |        30 |      | 0                  | 6b00724f-a094-11e9-8f47-000c2991dd19 |       86400 |         |             |                     0 |              |             |
+-----------------+-------------------+----------------+-----------------+-------------+---------------+------+---------------+-------------+--------+------------+----------+------------+---------+------------------------+-----------+------+--------------------+--------------------------------------+-------------+---------+-------------+-----------------------+--------------+-------------+
mysql> select * from slave_relay_log_info;
+-----------------+--------------------+---------------+-------------------+----------------+-----------+-------------------+----+--------------+
| Number_of_lines | Relay_log_name     | Relay_log_pos | Master_log_name   | Master_log_pos | Sql_delay | Number_of_workers | Id | Channel_name |
+-----------------+--------------------+---------------+-------------------+----------------+-----------+-------------------+----+--------------+
|               7 | ./relay-log.000010 |           839 | master-bin.000002 |          41388 |         0 |                 2 |  1 |              |
+-----------------+--------------------+---------------+-------------------+----------------+-----------+-------------------+----+--------------+

查看线程

<

标签: mts传感器rhmmps2g

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台