什么是 MySQL 的主从复制

  • Mysql内建的复制功能是构建大型高性能应用程序的基础, 将Mysql数据分布到多个系统上,这种分布机制是通过将Mysql某一台主机数据复制到其它主机(slaves)上,并重新执行一遍来实现的。复制过程中一个服务器充当主服务器,而一个或多个其它服务器充当从服务器。主服务器将更新写入二进制日志文件,并维护文件的一个索引以跟踪日志循环。这些日志可以记录发送到从服务器的更新。当一个从服务器连接主服务器时,它通知主服务器从服务器在日志中读取的最后一次成功更新的位置。从服务器接收从那时起发生的任何更新,然后封锁并等待主服务器通知新的更新。

为什么需要主从复制

  • 1. 数据分布 (Data distribution )
  • 2. 负载平衡(load balancing)
  • 3. 据备份(Backups) ,保证数据安全
  • 4. 高可用性和容错行(High availability and failover)
  • 5. 实现读写分离,缓解数据库压力

MySQL复制所带来的优势在于

  • 扩展能力:通过复制功能可以将MySQL的性能压力分担到一个或多个slave上。这要求所有 的写操作和修改操作都必须在Master上完成,而读操作可以被分配到一个或多个slave上;将读写分离到不同服务器执行之后, MySQL的读写性能得到提升。
  • 数据库备份:由于从实例是同步主实例的数据,所以可以将备份作业部署到从库。
  • 数据分析和报表:同样,一些数据分析和报表的实现可以在从实例执行,以减少对主库的性能影响。
  • 容灾能力:可以在物理距离较远的另一个数据中心建立一个slave,保证在主实例所在地区遭遇灾难时,在另一个数据中心能快速恢复。

MySQL复制有两种方法:

  • 传统方式: 基于主库的bin-log将日志事件和事件位置复制到从库,从库再加以应用来达到主从同步的目的。
  • Gtid方式: global transaction identifiers是基于事务来复制数据,因此也就不依赖日志文件,同时又能更好的保证主从库数据一致性。

MySQL复制有多种类型:

  • 异步复制:客户端发送DDL/DML语句给master,master执行完毕立即返回成功信息给客户端,而不管slave是否已经开始复制。这样的复制方式导致的问题是,当master写完了binlog,而slave还没有开始复制或者复制还没完成时,slave上和master上的数据暂时不一致,且此时master突然宕机,slave将会丢失一部分数据。如果此时把slave提升为新的master,那么整个数据库就永久丢失这部分数据。
  • 同步复制:客户端发送DDL/DML语句给master,master执行完毕后还需要等待所有的slave都写完了relay log才认为此次DDL/DML成功,然后才会返回成功信息给客户端。同步复制的问题是master必须等待,所以延迟较大,在MySQL中不使用这种复制方式。
  • 半同步复制:在异步复制的基础上,确保任何一个主库上的事务在提交之前至少有一个从库已经收到该事务并日志记录下来,即客户端发送DDL/DML语句给master,master执行完毕后还要等待一个slave写完relay log并返回确认信息给master,master才认为此次DDL/DML语句是成功的,然后才会发送成功信息给客户端。半同步复制只需等待一个slave的回应,且等待的超时时间可以设置,超时后会自动降级为异步复制,所以在局域网内(网络延迟很小)使用半同步复制是可行的
  • 延迟复制:在异步复制的基础上,人为设定主库和从库的数据同步延迟时间,即保证数据延迟至少是这个参数

MySQL复制有三种核心格式:

  • 基于语句的复制: 在主服务器执行SQL语句,在从服务器执行同样语句。MySQL默认采用基于语句的复制,效率较高。一旦发现没法精确复制时, 会自动选基于行的复制。
  • 基于行的复制: 把改变的内容复制过去,而不是把命令在从服务器上执行一遍. 从mysql5.0开始支持
  • 混合类型的复制: 默认采用基于语句的复制,一旦发现基于语句的无法精确的复制时,就会采用基于行的复制。

MySQL 的复制原理

  • master 服务器将数据的改变记录二进制 binlog 日志,当 master 上的数据发生改变时,则将其改变写入二进制日志中;
  • slave 服务器会在一定时间间隔内对 master 二进制日志进行探测其是否发生改变,如果发生改变,则开始一个 I/OThread 请求 master 二进制事件;
  • 同时主节点为每个 I/O 线程启动一个 dump 线程,用于向其发送二进制事件,并保存至从节点本地的中继日志中,从节点将启动 SQL 线程从中继日志中读取二进制日志,在本地重放,使得其数据和主节点的保持一致,最后 I/OThread 和 SQLThread 将进入睡眠状态,等待下一次被唤醒。

也就是:

  • 从库会生成两个线程,一个 I/O 线程,一个 SQL 线程;
  • I/O 线程会去请求主库的 binlog,并将得到的 binlog 写到本地的 relay-log(中继日志)文件中;主库会生成一个 log dump 线程,用来给从库 I/O 线程传 binlog;
  • SQL 线程,会读取 relay log 文件中的日志,并解析成sql语句逐一执行。

注意:

  • master 将操作语句记录到 binlog 日志中,然后授予 slave 远程连接的权限(master 一定要开启 binlog 二进制日志功能;通常为了数据安全考虑,slave 也开启binlog功能);
  • slave 开启两个线程:IO 线程和 SQL 线程。其中:IO 线程负责读取 master 的 binlog 内容到中继日志 relay log 里;SQL 线程负责从 relay log 日志里读出 binlog 内容,并更新到 slave 的数据库里,这样就能保证 slave 数据和 master 数据保持一致了;
  • MySQL 复制至少需要两个 MySQL 的服务,当然 MySQL 服务可以分布在不同的服务器上,也可以在一台服务器上启动多个服务;
  • MySQL复制最好确保 master 和 slave 服务器上的 MySQL 版本相同(如果不能满足版本一致,那么要保证 master 主节点的版本低于 slave 从节点的版本);
  • master 和 slave 两节点间时间需同步。
  • 默认情况下,MySQL的复制是异步的。slave可以不用一直连着master,即使中间断开了也能从断开的position处继续进行复制。
  • 复制是基于binlog的position进行的,复制之前必须保证position一致。(注:这是传统的复制方式所要求的)
  • 二进制日志目的是为了恢复定点数据库和主从复制,所以出于安全和功能考虑,极不建议将二进制日志和datadir放在同一磁盘上。

复制全局:

    Linux下MySQL主从复制(Binlog)的部署过程-LMLPHP

复制过滤:

    Linux下MySQL主从复制(Binlog)的部署过程-LMLPHP

具体步骤:

  • 第一步、在主服务器上记录二进制日志。在每个更新数据的事务完成之前,主服务器都会将数据更改记录到二进制日志中。即使事务在执行期间是交错的,mysql也会串行地将事务写入到二进制日志中。在把事件写入二进制日志之后,主服务器告诉存储引擎提交事务。
  • 第二步、从服务器把主服务器的二进制日志拷贝到自己的硬盘上,进入所谓的“中继日志”中。首先,它启动一个工作线程,叫I/O线程,这个I/O线程开启一个普通的客户端连接,然后启动一个特殊的二进制日志转储进程(它没有相应的SQL命令)。这个转储进程从主服务器的二进制日志中读取数据。它不会对事件进行轮询。如果3跟上了主服务器,就会进入休眠状态并等待有新的事件发生时主服务器发出的信号。I/O线程把数据写入从服务器的中继日志中。
  • 第三步、SQL线程读取中继日志,并且重放其中的事件,然后更新从服务器的数据。由于这个线程能跟上I/O线程,中继日志通常在操作系统的缓存中,所以中继日志的开销很低。SQL线程执行事件也可以被写入从服务器自己的二进制日志中,它对于有些场景很实用。

配置主从复制,可以总结为如下的步骤:

  • 1.在主服务器上,必须开启二进制日志机制和配置一个独立的ID
  • 2.在每一个从服务器上,配置一个唯一的ID,创建一个用来专门复制主服务器数据的账号
  • 3.在开始复制进程前,在主服务器上记录二进制文件的位置信息
  • 4.如果在开始复制之前,数据库中已经有数据,就必须先创建一个数据快照(可以使用mysqldump导出数据库,或者直接复制数据文件)
  • 5.配置从服务器要连接的主服务器的IP地址和登陆授权,二进制日志文件名和位置

官方YUM安装过程:https://dev.mysql.com/doc/refman/5.7/en/linux-installation-yum-repo.html

如何安装MySQL5.7.34(指定版本),可参考:https://www.cnblogs.com/zhangwencheng/p/15045074.html , MySQL5.7最新版本5.7.35(2021-08)

准备环境:

MySQL异步复制(过滤)部署过程。

  • Master01和Slave01 同样的操作;以Master01为例。
# yum安装MySQL5.7(默认最新版本)
[root@Mysql-Master01 ~]# wget https://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64/mysql57-community-release-el7-10.noarch.rpm
[root@Mysql-Master01 ~]# yum localinstall -y mysql57-community-release-el7-10.noarch.rpm
[root@Mysql-Master01 ~]# yum repolist enabled | grep "mysql.*-community.*"
[root@Mysql-Master01 ~]# yum install -y mysql-community-server
--------------------------------------------------------------------------------------------------------------------------
# 安装MySQL5.7.34(指定版本),必须按顺序安装(common-->libs-->client-->server)
# CentOS7版本需要先清理系统默认自带安装了MariaDB。
[root@Mysql-Master01 ~]# ls
mysql-community-client-5.7.34-1.el7.x86_64.rpm  mysql-community-libs-5.7.34-1.el7.x86_64.rpm
mysql-community-common-5.7.34-1.el7.x86_64.rpm  mysql-community-server-5.7.34-1.el7.x86_64.rpm

[root@Mysql-Master01 ~]# rpm -ivh mysql-community-common-5.7.34-1.el7.x86_64.rpm
[root@Mysql-Master01 ~]# rpm -ivh mysql-community-libs-5.7.34-1.el7.x86_64.rpm
[root@Mysql-Master01 ~]# rpm -ivh mysql-community-client-5.7.34-1.el7.x86_64.rpm
[root@Mysql-Master01 ~]# rpm -ivh mysql-community-server-5.7.34-1.el7.x86_64.rpm
==========================================================================================================================
# 启动mysqld
[root@Mysql-Master01 ~]# systemctl start mysqld
[root@Mysql-Master01 ~]# netstat -nutpl | grep mysql
tcp6       0      0 :::3306                 :::*                    LISTEN      5059/mysqld
[root@Mysql-Master01 ~]# ps -ef | grep mysql
mysql      5059      1  0 14:38 ?        00:00:02 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid

# mysql安全初始化
[root@Mysql-Master01 ~]# grep 'temporary password' /var/log/mysqld.log
2021-08-12T02:11:09.461541Z 1 [Note] A temporary password is generated for root@localhost: 2ofTdw8ntD>V

[root@Mysql-Slave01 ~]# mysql_secure_installation

Securing the MySQL server deployment.

Enter password for user root:    # 输入上面的初始密码 2ofTdw8ntD>V
The 'validate_password' plugin is installed on the server.
The subsequent steps will run with the existing configuration
of the plugin.
Using existing password for root.

Estimated strength of the password: 100
Change the password for root ? ((Press y|Y for Yes, any other key for No) : y    # 是否修改root密码

New password:      # 输入符合密码策略的root密码

Re-enter new password:    # 再次输入

Estimated strength of the password: 100
Do you wish to continue with the password provided?(Press y|Y for Yes, any other key for No) : y    # 是否希望继续使用所提供的密码
By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL without having to have
a user account created for them. This is intended only for
testing, and to make the installation go a bit smoother.
You should remove them before moving into a production
environment.

Remove anonymous users? (Press y|Y for Yes, any other key for No) : y    # 是否删除匿名账号
Success.


Normally, root should only be allowed to connect from
'localhost'. This ensures that someone cannot guess at
the root password from the network.

Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y    # 是否禁止root远程登录
Success.

By default, MySQL comes with a database named 'test' that
anyone can access. This is also intended only for testing,
and should be removed before moving into a production
environment.


Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y    # 是否删除test库和对test库的访问权限
 - Dropping test database...
Success.

 - Removing privileges on test database...
Success.

Reloading the privilege tables will ensure that all changes
made so far will take effect immediately.

Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y    # 是否刷新授权表使修改生效
Success.

All done!
08-23 12:15