MySQL主从同步详解

一:mysql主从原理

1.1 基本介绍
MySQL 内建的复制功能是构建大型,高性能应用程序的基础。将 MySQL 的 数亿分布到到多个系统上去,这种分步的机制,是通过将 MySQL 的某一台主机的数据复制到其它主机( Slave )上,并重新执行一遍来实现的。复制过程中一个服务器充当服务器,而一个或多个其它服务器充当从服务器。主服务器将更新写入二进制日志,并维护文件的一个索引以跟踪日志循环。这些日志可以记录发送到从服务器的更新。当一个从服务器连接主服务器时,它通知主服务器从服务器在日志中读取的最后一次成功更新的位置,从服务器接收从那时起发生的任何更新,然后封锁等等主服务器通知新的更新。
请注意当你进行复制时,所有对复制中的表的更新必须在主服务器上进行。否则,你必须要小心,以避免用户对主服务器上的表进行的更新与对服务器上的表所进行的更新之间的冲突

1.2 MySQL支持的复制类型
基于语句的复制。 在主服务器上执行的 SQL 语句,在从服务器上执行同样的语句。否则,你必须要小心,以避免用户对主服务器上的表进行的更新与对服务器上的表所进行的更新之间的冲突,配置:binlog_format = ‘STATEMENT’
基于行的复制。把改变的内容复制过去,而不是把命令在从服务器上执行一遍,从 MySQL 5.0开始支持,配置:binlog_format = ‘ROW’
混合类型的复制。默认采用基于语句的复制,一旦发现基于语句的无法精确的复制时,就会采用基于行的复制,配置:binlog_format = ‘MIXED’

1.3 mysql复制解决的问题
数据分布
负载平衡
备份
高可用性和容错行

1.4 复制是如何工作的
MySQL之间数据复制的基础是二进制日志文件(binary log file)。一台MySQL数据库一旦启用二进制日志后,其作为master,它的数据库中所有操作都会以“事件”的方式记录在二进制日志中,其他数据库作为slave通过一个I/O线程与主服务器保持通信,并监控master的二进制日志文件的变化,如果发现master二进制日志文件发生变化,则会把变化复制到自己的中继日志中,然后slave的一个SQL线程会把相关的“事件”执行到自己的数据库中,以此实现从数据库和主数据库的一致性,也就实现了主从复制。
可以简化为三个步骤(如下图):
Master 将改变记录到二进制日志中。
Slave 将 Master 的二进制日志拷贝到它的中继日志( Relay_log )
Slave 重做中继日志中的事件,将改变反映它自己的数据

Master 记录二进制的日志。在每个事务更新数据之前,Master 在二进制日志记录这些改变。 MySQL 将事务日志写入二进制日志,事务中的语句都是交叉执行的。在事件写入二进制日志完成后,Master 通知存储引擎提交事务。
Slave 将 Master 的 Binary log 拷贝到它自己的中继日志。首先 Slave 开始一个工作线程–I/O线程。I/O 线程在 Master 上打开一个连接,然后开始从二进制日志中读取事件,如果已经连上 Master,它会并等待master产生新的事件。I/O线程就这些事件写入中继日志。
SQL Slave Thread ( SQL从线程)处理该过程的最后一步。SQL纯种从中继日志读取事件,并重放其中的事件而更新 Slave 的数据。使其它与 Master 中的数据保持一致。只要该线程与 I/O 线程保持一致,中继日志通常会位于 OS 的缓存中,所以中继日志的开销很小。
此处,在 Master 中也有一个工作线程,和其他 MySQL 的连接一样,Slave 在 Master 中打开一个连接也会使得 Master 开始一个线程。复制过程有一个很重要的限制—复制在 Slave 上是串行化的,也就是说 Master 上的并行更新操作不能在 Slave 上并行操作。
1.5 复制实现细节分析

MySQL主从复制功能使用三个线程实现,一个在主服务器上,两个在从服务器上

1.5.1 Binlog转储线程。

当从服务器与主服务器连接时,主服务器会创建一个线程将二进制日志内容发送到从服务器。
该线程可以使用 语句 SHOW PROCESSLIST(下面有示例介绍) 在服务器 sql 控制台输出中标识为Binlog Dump线程。

二进制日志转储线程获取服务器上二进制日志上的锁,用于读取要发送到从服务器的每个事件。一旦事件被读取,即使在将事件发送到从服务器之前,锁会被释放。

1.5.2 从服务器I/O线程。

当在从服务器sql 控制台发出 START SLAVE语句时,从服务器将创建一个I/O线程,该线程连接到主服务器,并要求它发送记录在主服务器上的二进制更新日志。

从机I/O线程读取主服务器Binlog Dump线程发送的更新 (参考上面 Binlog转储线程 介绍),并将它们复制到自己的本地文件二进制日志中。

该线程的状态显示详情 Slave_IO_running 在输出端 使用 命令SHOW SLAVE STATUS

使用\G语句终结符,而不是分号,是为了,易读的垂直布局

这个命令在上面 查看从服务器状态 用到过

mysql> SHOW SLAVE STATUS\G
1
1.5.3 从服务器SQL线程。

从服务器创建一条SQL线程来读取由主服务器I/O线程写入的二级制日志,并执行其中包含的事件。

在前面的描述中,每个主/从连接有三个线程。主服务器为每个当前连接的从服务器创建一个二进制日志转储线程,每个从服务器都有自己的I/O和SQL线程。
从服务器使用两个线程将读取更新与主服务器更新事件,并将其执行为独立任务。因此,如果语句执行缓慢,则读取语句的任务不会减慢。

例如,如果从服务器开始几分钟没有运行,或者即使SQL线程远远落后,它的I/O线程也可以从主服务器建立连接时,快速获取所有二进制日志内容。

如果从服务器在SQL线程执行所有获取的语句之前停止,则I/O线程至少获取已经读取到的内容,以便将语句的安全副本存储在自己的二级制日志文件中,准备下次执行主从服务器建立连接,继续同步。

二 搭建主从同步数据库服务
1 从库自动同步主库的数据到本地
配置:
(1) 主库启动binlog日志,主库上的线程为Binlog转储线程
(2 )从库上会有2个子程序,IO和SQL线程
IO将主库上的binlog日志文件里的SQL命令读取到本地的relay-log(中继日志)文件里
SQL线程执行relay-log日志里的sql语句,重现master的数据操作

2 在配置主从同步之前,需要将主从两台服务器的数据保持一致,如果主服务器上的数据在从数据库服务器上不存在,那么从库的两个线程会宕掉

4 基本构建思路
–确保数据相同:从库必须要有主库上的数据
–配置主服务器:启用binlog日志,授权用户,查看当前正使用的Binlog日志(从库IO线程取SQL命令的地方)
–配置从服务器:设置server_id(标识自己的身份),指定主库信息
–测试配置: 客户端连接主库写入数据,在从库上也能查询到

5 配置主数据库服务器(mysql51)
(1)修改数据库配置文件

[root@mysql51 ~]# vim /etc/my.cnf
[mysqld]
log_bin=master51
server_id=51
binlog_format=mixed
validate_password_policy=0
validate_password_length=6
另外还可以指定当主、从网络中断时的重试超时时间(slave-net-timeout=60 )等
[root@mysql51 ~]# systemctl restart mysqld  重启服务
mysql> show master status;   查看主库信息
指定当主、从网络中断时的重试超时时间(slave-net-timeout=60 )等等

(2)用户授权(从服务器以此用户名和密码指定主库信息)

新建一个备份用户,授予复制权限,//同步授权只能授予全局权限,指定所有库,所有表
mysql> grant replication slave on *.* to repuser@"192.168.4.52" identified by "123456"    
 新建一个客户端登录用户,授予查询插入权限
mysql> grant insert,select on *.* to yaya100@"192.168.4.%"  identified  by "123456"     

(3)查看binlog日志文件

[root@mysql51 ~]# ls /var/lib/mysql/master*
/var/lib/mysql/master51.000001  /var/lib/mysql/master51.index

[root@mysql51 ~]# mysqlbinlog /var/lib/mysql/master51.000001 | grep -i grant

6 配置从数据库(mysql52)
(1) 关闭从库binlog日志(/etc/my.cnf 文件)
配置文件关闭binlog日志,开启server_id=52即可,随后重启mysqld服务
此时再查看主库或从库信息均为空

mysql> show slave status;
Empty set (0.00 sec)
mysql> show master status;
Empty set (0.00 sec)

(2)查询主库授权信息(mysql51)

mysql> select user,host from mysql.user;
+-----------+---------------+
| user      | host          |
+-----------+---------------+
| repluer   | 192.168.4.52   |
| yaya100 | 192.168.4.%   |
| admin     | localhost     |
| mysql.sys | localhost     |
| root      | localhost     |
+-----------+---------------+

(3)查看主库状态信息

mysql> show master status;
+-----------------+----------+--------------+------------------+-------------------+
| File            | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-----------------+----------+--------------+------------------+-------------------+
| master51.000001 |      452 |              |                  |                   |
+-----------------+----------+--------------+------------------+---``

(4)在从库上指定主库信息
注意:任何选项指定不正确,都会导致IO和SQL线程不工作

mysql> change master to  master_host="192.168.4.51",master_user="repluser",
    -> master_password="123456",
    -> master_log_file="master51.000001",
    -> master_log_pos=452;  
    (指定从当前主库日志文件的当前偏移量来进行数据同步,当然也可以指定其他的日志文件和偏移量来进行同步,只要文件和偏移量存在即可)

(5)检查Slave服务器的同步状态

mysql> start slave;
mysql> show slave status\G;
Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.4.51          主库ip
                  Master_User: repluser                   被授权的用户名
                  Master_Port: 3306
                Connect_Retry: 60                           60s后重新连接
              Master_Log_File: master51.000001         主binlog日志文件
          Read_Master_Log_Pos: 452                          主binlog日志文件偏移量
               Relay_Log_File: mysql52-relay-bin.000002       从库中继日志文件
                Relay_Log_Pos: 319                                   从库中继日志文件偏移量   
        Relay_Master_Log_File: master51.000001      
             Slave_IO_Running: Yes                        从库线程IO开启状态
            Slave_SQL_Running: Yes                      从库线程SQL开启状态
            ………………

注意 :如果IO和SQL线程没有运行,那么Last_IO_Error 和 Last_SQL_Error
这两个字段有值,会标明原因。如果出错,执行stop slave 停止从服务器,修改好后再执行start slave.
注意: master信息会自动保存在/mysql/master.info 文件中
以后要更改master信息时需要先stop slave.
注意:停止stop slave后,可以更改主库信息的单条选项信息,再开启start slave

什么时候sql线程会宕掉?sql线程在执行中继日志文件中的sql命令时,如果从库里没有主库里操作的库或者表,sql线程会立刻宕掉,Last_SQL_Error:字段会记录出错原因,如果出错,先关闭slave,把需要的库或者表建好,在开启slave即可。

7 客户端mysql50客户机测试:
(1)登录192.168.4.51的数据库

[root@mysql50 ]# mysql -h 192.168.4.51 -uwebadmin1 -p123456

(2)查询权限

mysql> show grants;
+--------------------------------------------------+
| Grants for yaya100@%                             |
+--------------------------------------------------+
| GRANT USAGE ON *.* TO 'webadmin1'@'192.168.4.%'              |
| GRANT SELECT, INSERT ON `db1`.* TO 'webadmin1'@'%' |
+--------------------------------------------------+

(3)插入数据

mysql> insert into db1.a values (1);
Query OK, 1 row affected (0.08 sec)
mysql> insert into db1.a values (2);
Query OK, 1 row affected (0.09 sec)
mysql> insert into db1.a values (3);
Query OK, 1 row affected (0.07 sec)

(4)主数据库51服务器查看数据是否插入

mysql> select * from db1.a;
+------+
| id   |
+------+
|    1 |
|    2 |
|    3 |
+------+

(5)从数据库52服务器查看数据是否从51同步

mysql> select * from db1.a
    -> ;
+------+
| id   |
+------+
|    1 |
|    2 |
|    3 |
+------+
至此,验证完成mysql数据库主从同步

(6)将从服务器数据库还原至正常的数据库
–删除4个从服务器配置文件,再重启即可
master.info
reloy_log.info
主机名-relay-bin.xxxxxx
主机名-relay-bin.index
–重启Mysqld服务即可,进入数据库验证: show slave status;

三 配置主从从同步结构
具体要求如下:
配置主机192.168.4.51为主数据库服务器
配置主机192.168.4.52为51主机的从库服务器
配置主机192.168.4.53为52主机的从库服务器
客户端连接主数据库服务器51主机创建的数据,连接52和53主机时,也可以访问到库、表、记录。
在上例中,mysql51和mysql52的主从同步已搭建完成,本例在之前的服务基础上继续搭建,完成 “主—>(从主)—>从” 的同步结构
注意:此时,mysql52主机作为mysql51主机的从服务器的同时,它还是mysql53主机的主服务器。
1 配置mysql52为mysql53的主master服务器(为了在启用binlog日志及同步之前保持主、从库的一致性,主从同步未配置之前,要保证从库上要有主库上的数据)
1.1配置文件开启binlog日志

[root@mysql52 mysql]# vim /etc/my.cnf
[mysqld]
log_slave_updates
log_bin=master52
#replicate-do-db=db1
server_id=52
binlog_format="mixed"
log_slave_updates                          //允许级联复制
[root@mysql52 mysql]# systemctl restart mysqld

1.2 mysql52对从库授权

新建一个备份用户,授予复制权限
mysql> grant replication slave on *.* to kenji@"192.168.4.53" identified by "123456"    
 新建一个客户端登录用户,授予查询插入权限
mysql> grant insert,select on *.* to tom@"192.168.4.%"  identified  by "123456"  

1.3 确保/var/lib/mysql下面最少有两个文件

[root@mysql52 mysql]# ls /var/lib/mysql/master52.*`
/var/lib/mysql/master52.000001  /var/lib/mysql/master52.000004  /var/lib/mysql/master52.index
/var/lib/mysql/master52.000002  /var/lib/mysql/master52.000005
/var/lib/mysql/master52.000003  /var/lib/mysql/master52.000006

1.4 查看正在使用的日志信息

mysql> show master status;
+-----------------+----------+--------------+------------------+-------------------+
| File            | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-----------------+----------+--------------+------------------+-------------------+
| master52.000006 |     1352 |              |                  |                   |
+-----------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.4.51
                  Master_User: repluser
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: master51.000008
          Read_Master_Log_Pos: 1352
               Relay_Log_File: mysql52-relay-bin.000016
                Relay_Log_Pos: 1517
        Relay_Master_Log_File: master51.000008
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes

2 配置从服务器mysql53 :192.168.4.53
2.1 修改配置文件

[root@mysql53 ~]# vim /etc/my.cnf
[mysqld]
server_id=53         //指定id时确保不与其他主机的id重复
replicate-ignore-db=db1            //不同步数据库db1的数据
validate_password_policy=0
validate_password_length=6
character_set_server=utf8
[root@mysql53 ~]# systemctl restart mysqld

2.2 以本机数据库root身份进入数据库为本机指定主服务器

mysql> change master to master_host="192.168.4.52",master_user="kenji",master_password="123456",master_log_file="master52.000006",master_log_pos=1352;

2.3 启动slave进程,并查看slave状态

mysql> start slave;
mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.4.52
                  Master_User: repluser
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: master52.000006
          Read_Master_Log_Pos: 1352
               Relay_Log_File: mysql53-relay-bin.000024
                Relay_Log_Pos: 1563
        Relay_Master_Log_File: master52.000006
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: db1             //不记录db1库的数据

3 客户端验证配置
3.1 查看服务器数据此刻是否一样

数据库192.168.4.51上查看
mysql> use db2;
Database changed
mysql> show tables;
+---------------+
| Tables_in_db2 |
+---------------+
| game          |
+---------------+
mysql> desc game;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id    | int(11) | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+
mysql> select * from game;
Empty set (0.00 sec)
数据库服务器52 和53依次查看,数据一致

3.2 利用51数据库的授权登录用户在客户端50上登录192.168.4.51的数据库,插入数据

[root@mysql50 aa]# mysql -h192.168.4.51 -uyaya100 -p123456
mysql> use db2;
Database changed
mysql> show tables;
+---------------+
| Tables_in_db2 |
+---------------+
| game          |
+---------------+
mysql> desc game;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id    | int(11) | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+
mysql> select * from game;
Empty set (0.00 sec)
mysql> insert into game values (111);
Query OK, 1 row affected (0.06 sec)

mysql> insert into game values (111);
Query OK, 1 row affected (0.07 sec)

3.3 分别进入三台服务器的数据库内查看数据是否同步更新

进入192.168.4.51查看
mysql> select * from game;
+------+
| id   |
+------+
|  111 |
|  111 |
+------+
2 rows in set (0.00 sec)
进入192.168.4.52查看
mysql> select * from game;
+------+
| id   |
+------+
|  111 |
|  111 |
+------+
2 rows in set (0.00 sec)
进入192.168.4.53查看
mysql> select * from game;
+------+
| id   |
+------+
|  111 |
|  111 |
+------+
2 rows in set (0.00 sec)
至此三台数据库服务器完成了主从同步

3.4 在服务器192.168.4.53的配置文件里写入了不同步更新db1的数据的字段
replicate-ignore-db=db1 ,现在来验证一下

1  客户端连接51数据库,在db1表内插入数据
mysql> use db1;
mysql> show tables;
+---------------+
| Tables_in_db1 |
+---------------+
| a             |
+---------------+
mysql> select * from a;
+------+
| id   |
+------+
|    2 |
+------+
插入数据
mysql> insert into a values(3);
Query OK, 1 row affected (0.09 sec)

mysql> insert into a values(4);
Query OK, 1 row affected (0.08 sec)

2 查看51数据库内写入的内容
mysql> select * from a;
+------+
| id   |
+------+
|    2 |
|    3 |
|    4 |
+------+
3 rows in set (0.00 sec)

3 查看52上数据是否同步更新
mysql> select * from a;
+------+
| id   |
+------+
|    2 |
|    3 |
|    4 |
+------+
  //更新成功

4 查看53的是否有更新,原理上是不更新数据
mysql> select * from a;
+------+
| id   |
+------+
|    2 |
+------+
1 row in set (0.00 sec)  //保持和初始状态一样

猜你喜欢

转载自blog.csdn.net/weixin_42104231/article/details/84404331