MYSQL教程优化篇:数据库多节点之Mysql主从复制,主主复制

作者 : IT 大叔 本文共20375个字,预计阅读时间需要51分钟 发布时间: 2020-11-10

数据库多节点

多节点数据库的好处:
1.性能更高,通过负载均衡提高服务器的负载
2.安全问题,如果有多节点,一个数据库宕机还有另一个数据库可以访问
或者如果因为宕机导致一个数据库的数据丢失,还可以有其他数据库的备份,这个叫做数据冗余。

主从复制就是数据库多节点的其中一个方法:
它依赖于binlog日志

主从复制的使用场景:
两种:数据库备份 和 读写分离

实现读写分离,从而提高数据库的性能,比如有两台服务器,一台主节点,一台从节点,在主库进行增删改,所以主库要开事务,操作起来比较慢。而从库只进行读取,读是可以不添加事务的,会快很多,我们可以部署多台从库进行读取,只要数据写进了主库,所有的从库都会进行更新。
而且实现了数据冗余,增加了安全性。

主从复制的原理:
假设现在有一台主节点,四台从节点。主库专门负责写,从库只负责读。
主库开事务,从库不开。
当主库插入了一条数据,那么这个插入操作会被记录到主库的日志文件,而从库会读取主库的日志文件里面的操作,并执行一遍,这样从库也会产生这么一条数据。这个日志就是binlog日志

从节点不能太多,否则这些从库都去读取日志文件并同步就会导致同步的延时问题,从节点们间的数据就可能不一致。

如果从节点太多,我们就要在业务上进行改善了。

===================== =================================
binlog日志:

在这里顺便说一下,MySQL有哪些日志:
Error log  错误日志
General query log 普通查询日志
Slow query log  慢日志文件
binary log  二进制日志

binary log 就是个二进制文件,记录着数据库所有DML和DDL操作,他的作用有两个:
1.增量备份
2.主从复制

我们使用mysqldump进行备份的时候,一般是备份整个数据库,但是会产生比较大的数据,如果希望只备份新产生的值,就需要用到二进制日志,读取里面的操作。

mysql默认不开启binlog的。

show variables like "%log_bin%"
+---------------------------------+-------+
| Variable_name                   | Value |
+---------------------------------+-------+
| log_bin                         | OFF   |
| log_bin_trust_function_creators | OFF   |
| sql_log_bin                     | ON    |
+---------------------------------+-------+

 

开启binlog

进入my.cnf

vi /etc/my.cnf

在[mysqld]下加入两行(在其他地方加无效):

server-id = 1
log-bin=/var/lib/mysql/mysql-bin  #指定二进制文件的文件名的basename

重启

此时/var/lib/mysql会出现两个文件
mysql-bin.000001    #二进制文件
mysql-bin.index     #二进制索引文件

如何查看二进制日志内容:
mysqlbinlog 二进制文件名路径

或者 先登录客户端,然后

show binglog events in "日志文件名"

 

二进制日志部分内容如下:

# at 59484
#200113  9:14:45 server id 1  end_log_pos 59591         Query   thread_id=461  exec_time=0      error_code=0
use `test`/*!*/;
SET TIMESTAMP=1578878085/*!*/;
create table test1(id int,name varchar(100))
/*!*/;

# at 59591
#200113  9:15:15 server id 1  end_log_pos 59659         Query   thread_id=461  exec_time=0      error_code=0
SET TIMESTAMP=1578878115/*!*/;
BEGIN
/*!*/;

# at 59659
#200113  9:15:15 server id 1  end_log_pos 59756         Query   thread_id=461  exec_time=0      error_code=0
SET TIMESTAMP=1578878115/*!*/;
insert into test1 values (1,"zbp")
/*!*/;

# at 59756
#200113  9:15:15 server id 1  end_log_pos 59783         Xid = 25496
COMMIT/*!*/;

上面从 # at xxx 到 /*!*/;是一个完整的部分,这部分叫做事件,事件包括两部分:
事件头 是该操作的一些信息,如操作在二进制日志的开始位置 "# at 59591",执行时的时间 "SET TIMESTAMP=1578878115",执行所化的时间“exec_time=0” 等

事件体 具体的操作语句 如 “insert into test1 values (1,"zbp")”

所以根据二进制日志,我们可以做一些恢复操作。

show binglog events;  # 只显示mysql-bin.000001的内容,如果想查看其他日志文件内容就要指定日志文件名

*************************** 593. row ***************************
   Log_name: mysql-bin.000001
        Pos: 58929
 Event_type: Query
  Server_id: 1
End_log_pos: 59099
       Info: use `zbpblog`; UPDATE `blogs`  SET `real_click` = `real_click` + 1  WHERE  `create_time` <= 1578876263  AND `id` = '26'
*************************** 594. row ***************************
   Log_name: mysql-bin.000001
        Pos: 59099
 Event_type: Query
  Server_id: 1
End_log_pos: 59171
       Info: COMMIT
*************************** 595. row ***************************
   Log_name: mysql-bin.000001
        Pos: 59171
 Event_type: Query
  Server_id: 1
End_log_pos: 59242
       Info: BEGIN
*************************** 596. row ***************************
   Log_name: mysql-bin.000001
        Pos: 59242
 Event_type: Query
  Server_id: 1
End_log_pos: 59412
       Info: use `zbpblog`; UPDATE `blogs`  SET `real_click` = `real_click` + 1  WHERE  `create_time` <= 1578876861  AND `id` = '32'
*************************** 597. row ***************************
   Log_name: mysql-bin.000001
        Pos: 59412
 Event_type: Query
  Server_id: 1
End_log_pos: 59484
       Info: COMMIT

每次重启服务器,会新创建一个binlog日志(通过flush logs命令),那么这有什么好处呢:
比如昨天有一个binlog文件,今天有一个binlog日志,今天发现昨天有一个误操作,要恢复,就可以针对昨天的日志文件来做恢复。而且这样也不会让一个binlog文件太大。

二进制操作:

flush logs  # 刷新日志文件
show binlog events in "xxx"     #查看日志文件内容
show master status      # 查看当前使用的日志的状态
show master logs        # 查看所有日志文件(相当于查看日志索引文件)
reset master            # 清空所有日志文件(非常危险,不建议)

# 如何使用二进制文件恢复数据(此方法只适用于恢复少量数据)

mysqlbinlog mysql-bin.000001 | mysql -uroot -p

该命令(使用了管道符)会将mysql-bin.000001中所有的MySQL操作都执行一遍;

 

但是我们的误操作为:
delete from user where id = 56;

此时我们要恢复的数据只有一条,不可能为了恢复一条数据而将一整个日志文件都执行一遍。
此时我们就要找出 id为56的这条数据时在啥时候创建的,然后找到相应的binlog日志文件,使用 mysqlbinlog 文件名 去查看创建id为56的数据的起始和结束位置(就是 #at xxx ,结束位置就是下一个事件的起始位置),假如这个位置是 123 166

执行:

mysqlbinlog mysql-bin.000001 --start-position 123 --stop-position 166 | mysql -uroot -p

即可;

还可以使用 --start-datetime=xxx 和 --stop-datetime=xxx 根据时间戳范围去恢复,当然还是要查看二进制文件,看这个事件是什么时间创建的数据。

所以二进制日志文件恢复数据是有局限的:
如果删除的是很久远的数据,要找到这个数据创建或者修改操作实在哪个二进制日志的哪个位置,很麻烦。
如果删除的数据不只是一条,还要找多条数据的创建是在哪几个日志的哪些位置。

所以该方法适用于刚刚发生的或几天内发生的,而且是少量数据的误操作的恢复。
如果是大量数据的误删除,我们只能通过平时备份来恢复。所以平时多做备份才是王道。

PS:二进制日志只记录增删改,不会记录查询语句

关于二进制日志的三种模式(格式):

mysql复制主要有三种方式:基于SQL语句的复制(statement-based replication, SBR),基于行的复制(row-based replication, RBR),混合模式复制(mixed-based replication, MBR)。

对应的,binlog的格式也有三种:STATEMENT,ROW,MIXED。

① STATEMENT模式(SBR)

每一条会修改数据的sql语句会记录到binlog中。优点是并不需要记录每一条sql语句和每一行的数据变化,减少了binlog日志量,节约IO,提高性能。缺点是在某些情况下会导致master-slave中的数据不一致(如sleep()函数, last_insert_id(),以及user-defined functions(udf)等会出现问题)

② ROW模式(RBR)

不记录每条sql语句的上下文信息,仅需记录哪条数据被修改了,修改成什么样了。而且不会出现某些特定情况下的存储过程、或function、或trigger的调用和触发无法被正确复制的问题。缺点是会产生大量的日志,尤其是alter table的时候会让日志暴涨。

③ MIXED模式(MBR)

以上两种模式的混合使用,一般的复制使用STATEMENT模式保存binlog,对于STATEMENT模式无法复制的操作使用ROW模式保存binlog,MySQL会根据执行的SQL语句选择日志保存方式。

SBR 的优点:

历史悠久,技术成熟
binlog文件较小
binlog中包含了所有数据库更改信息,可以据此来审核数据库的安全等情况
binlog可以用于实时的还原,而不仅仅用于复制
主从版本可以不一样,从服务器版本可以比主服务器版本高

SBR 的缺点:

不是所有的UPDATE语句都能被复制,尤其是包含不确定操作的时候。
调用具有不确定因素的 UDF 时复制也可能出问题
使用以下函数的语句也无法被复制:
* LOAD_FILE()
* UUID()
* USER()
* FOUND_ROWS()
* SYSDATE() (除非启动时启用了 --sysdate-is-now 选项)
INSERT ... SELECT 会产生比 RBR 更多的行级锁
复制需要进行全表扫描(WHERE 语句中没有使用到索引)的 UPDATE 时,需要比 RBR 请求更多的行级锁
对于有 AUTO_INCREMENT 字段的 InnoDB表而言,INSERT 语句会阻塞其他 INSERT 语句
对于一些复杂的语句,在从服务器上的耗资源情况会更严重,而 RBR 模式下,只会对那个发生变化的记录产生影响
存储函数(不是存储过程)在被调用的同时也会执行一次 NOW() 函数,这个可以说是坏事也可能是好事
确定了的 UDF 也需要在从服务器上执行
数据表必须几乎和主服务器保持一致才行,否则可能会导致复制出错
执行复杂语句如果出错的话,会消耗更多资源

RBR 的优点:

任何情况都可以被复制,这对复制来说是最安全可靠的
和其他大多数数据库系统的复制技术一样
多数情况下,从服务器上的表如果有主键的话,复制就会快了很多
复制以下几种语句时的行锁更少:
* INSERT ... SELECT
* 包含 AUTO_INCREMENT 字段的 INSERT
* 没有附带条件或者并没有修改很多记录的 UPDATE 或 DELETE 语句
执行 INSERT,UPDATE,DELETE 语句时锁更少
从服务器上采用多线程来执行复制成为可能

RBR 的缺点:

binlog 大了很多
复杂的回滚时 binlog 中会包含大量的数据
主服务器上执行 UPDATE 语句时,所有发生变化的记录都会写到 binlog 中,而 SBR 只会写一次,这会导致频繁发生 binlog 的并发写问题
UDF 产生的大 BLOB 值会导致复制变慢
无法从 binlog 中看到都复制了写什么语句
当在非事务表上执行一段堆积的SQL语句时,最好采用 SBR 模式,否则很容易导致主从服务器的数据不一致情况发生

另外,针对系统库 mysql 里面的表发生变化时的处理规则如下:
如果是采用 INSERT,UPDATE,DELETE 直接操作表的情况,则日志格式根据 binlog_format 的设定而记录
如果是采用 GRANT,REVOKE,SET PASSWORD 等管理语句来做的话,那么无论如何都采用 SBR 模式记录
注:采用 RBR 模式后,能解决很多原先出现的主键重复问题。

所以如果要进行主从复制的话,最好是设定binlog的格式为mixed,可以防止因为binlog格式和mysql版本不一致导致的同步sql失败
只需在配置文件中添加:

binlog_format=mixed     # 默认是statement格式

即可;
============================================

MySQL主从复制:

假如有1个主节点 1个从节 点(一个从节点和多个从节点的操作相同)

步骤如下:
1.配置主节点:
创建用户赋予权限
开启binlog日志

2.配置从节点
配置同步主节点日志
指定主节点的ip,端口,用户
启动从节点

 

配置主节点的步骤 

 

创建用户: 

首先我们要创建用户,指定来访者的IP,指定该用户的权限

现在我们指定用户名为repl,密码 repl,来访ip为 192.168.153.%,即这个网段的IP都能访问

现在我们给该用户赋予权限
grant replication slave on *.* to "repl"@"192.168.153.%" identified by "repl";

意思是对repl这个用户赋予主从复制的权限,范围是所有库和表

 

开启binlog日志

server-id = 1
log-bin=/var/lib/mysql/mysql-bin  #指定二进制文件的文件名的basename

配置从节点的步骤 
配置同步日志: 

server-id=10
relay-log=/var/lib/mysql/relay-bin  #同步日志文件的路径
relay-log-index=/var/lib/mysql/relay-bin.index  #同步日志文件索引的路径

注意,同步日志和从服务器自己的二进制日志是两回事,同步日志只负责记录主节点的日志内容(主节点的操作),不会记录从节点本身的操作

指定主节点的ip,端口,用户:

change master to master_host="主节点主机ip",master_port=3306,master_user="用户",master_password="用户密码",master_log_file="mysql-bin.000001",master_log_pos=0;

# 其中 master_log_file="mysql-bin.000001",master_log_pos=0 这两项不是随便填的,而是要在主节点执行show master status 来查看主节点当前使用的二进制文件名和最后的位置pos

启动从节点:
start slave 

查看从节点状态:
show slave status

如果没有配置成功,看里面的Last_IO_Error这个字段,他会告诉你错误原因

此时你很可能发现主节点连不上,这是因为防火墙以及数据库中配置文件限定了bind-address=127.0.0.1 ,也就是主节点只允许本机连接。

如果是防火墙的问题,关闭防火墙,然后再从服务器中stop slave 再start slave
如果是只允许本机连接,则在bind-address中添加一条从服务器的IP
bind-address=127.0.0.1 从服务器IP

设定bind-address=0.0.0.0 表示允许任何ip连接主服务器,但是这个行为很危险,如果你的数据库密码设置的简单的话很可能被入侵数据库。(修改配置文件后记得重启服务)

 

接下来使用PHP实现主从节点读写分离:

思路很简单:
1.定义好负责写的主节点的ip和众多从节点的ip
2.根据sql语句判断是select还是增删改操作
3.如果是增删改则连接主节点执行操作,否则连接从节点进行操作
4.连接从节点的时候是遵循随机分配,使用mt_rand()函数即可

如果是使用TP5的话更简单,只需要进行配置即可。

==============================================

主主复制:
有两个主节点A B,都有binlog日志,我们可以对A写和读,也可以对B写和读,不像主从复制,主节点负责写,众多从节点负责读。

对节点A写,节点B可以同步节点A的数据,反之亦然

也就是说A是主节点也是从节点,B也是主节点也是从节点;

所以,我们只需要将上面的主从复制的步骤进行两遍即可:

假设现在已经配置成功主主复制了,会出现这么一个问题:

假设A节点往一个空的表 t1 插入5条数据(t1表只有一个主键字段id):

insert into t1 (id) value (null);   # 执行5次

那么B节点也会自动插入5条数据,假设id为1,2,3,4,5;

现在我往B节点插入一条数据
insert into t1 (id) value (null);
此时就会报错说主键重复。

我们查看一下两个主节点的建表语句中的自增id
show create table t1

发现A节点的auto_increment=6
B节点的auto_increment=1

也就是说B节点会复制A节点的数据却无法复制A节点的自增值。如果是在mysql 5.7,那么不存在这个问题,主节点之间会在同步数据的时候也同步自增。

如果版本低于5.7 那么可以通过以下方法解决:
设定 A节点设置自增的步长为2,自增id从1开始

在配置文件中:
auto_increment_increment=2   #步长
auto_increment_offset=1      #从1开始

# 重启服务

设定 B节点设置自增的步长为2,自增id从2开始
在配置文件中:
auto_increment_increment=2   #步长
auto_increment_offset=2      #从2开始

# 重启服务

当然,一开始数据要为空,从空表开始。
这么一来,如果从A的t1表连续插入2条数据,他的id是1,3
此时B的t1也是1,3

再在B插入2条数据,此时查询表得到1,2,3,4

如果有3个主服务器进行主主复制的话,那么步长要设置为3,而offset分别为1,2,3

如果3台节点都是mysql 5.7 版本的,那么就不用这么设置。如果有一台是低于5.7版本的,就要这么设置。

当然,如果是主从复制是不会出现这个问题的,因为从节点只负责读,不负责写入。

PS:由于二进制日志不记录查询语句,所以对表A查询不会导致在表B又查询一次,所以主主复制也是可以进行负载均衡的。

主从复制实操

两台版本相同的mysql节点

从服务器 204.175.124.51
主服务器 54.22.37.2
目标:同步主服务器的hst数据库,但不对其他数据库同步

# 先查看两个服务器是否都开启了binlog日志
show variables like "%log_bin%";

================== 对主服务器的操作 ======================

# 先将hst库备份(在进行同步之前,从节点要先导入hst库,否则在主节点有hst而从节点没有hst的情况下,主节点对hst进行操作,会导致从节点同步操作失败,并且中断之后的同步操作)

# 对主服务器开启binlog

server-id=100   # 指定server id
log-bin=/var/lib/mysql/mysql-bin    # 指定名称
binlog-do-db=hst        # 指定只对hst数据库备份

重启服务

# 查看是否开启binlog日志

show variables like "%log_bin%";

结果如下:
+---------------------------------+--------------------------------+
| Variable_name                   | Value                          |
+---------------------------------+--------------------------------+
| log_bin                         | ON                             |
| log_bin_basename                | /var/lib/mysql/mysql-bin       |
| log_bin_index                   | /var/lib/mysql/mysql-bin.index |
| log_bin_trust_function_creators | OFF                            |
| log_bin_use_v1_row_events       | OFF                            |
| sql_log_bin                     | ON                             |
+---------------------------------+--------------------------------+

结果已开启

# 对用户进行授权(如果用户没有创建会自动创建)

grant replication slave on *.* to "cjq"@"204.175.124.51" identified by "xxxxx";

这里注意下,有人可能会这样写来表示只同步hst这个数据库:

grant replication slave on hst.* to "cjq"@"204.175.124.51" identified by "xxxxx";

结果会报错 Incorrect usage of DB GRANT and GLOBAL PRIVILEGES

所以只能在配置文件限定要同步的库名才行。

当然,主节点可以不设置binlog-do-db选项,但是从节点就要设置replicate-do-db=hst ,表示从节点只会对主节点的hst库进行同步
什么时候会用replicate-do-db而不会用binlog-do-db:
假如有一台主节点A,两台从节点:B,C; B要同步A的d1库,C要同步A的d2库,此时就不适宜用binlog-do-db,而适合用replicate-do-db。

# 查看主节点的二进制日志状态

show master status    
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000002 |      154 | hst          |                  |                   |
+------------------+----------+--------------+------------------+-------------------+

记录下 File 和 Position 这两个字段的值,待会在从服务器进行指向主服务器的时候会用到。

================== 对从服务器的操作 ======================

首先,尝试连接主服务器

mysql -h54.22.37.2 -ucjq -pxxxxx

如果连接没成功,说明主服务器限定了连接的IP只能时主服务器本机,或者是因为开了防火墙。把主节点和从节点的防火墙关了,在主服务器配置文件的[mysqld]下修改bind-address参数为从服务器IP和127.0.0.1即可,记得重启主服务器。
并且重新查看show master status,记录下File和Position字段,因为每重启一次服务,就会生成一个新的binlog日志。

退出连接

连接从节点本机的客户端

# 导入hst库

# 查看是否开启二进制日志:

show variables like "%log_bin%";
+---------------------------------+-------+
| Variable_name                   | Value |
+---------------------------------+-------+
| log_bin                         | ON    |
| log_bin_trust_function_creators | OFF   |
| sql_log_bin                     | ON    |
+---------------------------------+-------+

这里已开启,如果没开启则到配置文件添加
server-id = 99      # 和上面的server-id不同即可
log-bin= 二进制日志的路径和名称
然后重启服务

# 配置同步日志:在配置文件的[mysqld]中添加:

relay-log=/var/lib/mysql/relay-bin               #同步日志文件的路径
relay-log-index=/var/lib/mysql/relay-bin.index   #同步日志的索引文件
slave-skip-errors=all                            #跳过同步sql时发生的报错,以防止因一两句sql错误导致同步中断

重启服务

# 从服务器指向要同步的主服务器

change master to master_host="54.22.37.2",master_user="cjq",master_password="xxxxx",master_log_file="mysql-bin.000002",master_log_pos=154;

# 启动从节点并查看从节点的同步是否已经开启

start slave
show slave status\G;

*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 54.22.37.2
                  Master_User: cjq
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000002
          Read_Master_Log_Pos: 154
               Relay_Log_File: relay-bin.000002
                Relay_Log_Pos: 407
        Relay_Master_Log_File: mysql-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: 154
              Relay_Log_Space: 695
              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: 86
1 row in set (0.00 sec)

如果下面两项:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
都是Yes那么同步成功

如果有No,同步失败,这是可以查看字段Last_IO_Error 和 Last_SQL_Error
Last_IO_Error一般是无法连接,授权问题等报错
Last_SQL_Error一般是在主服务器执行增删改操作时,从服务器无法同步语句的报错

================== 主从复制的注意点和常见报错 ======================

1. 远程连接数据库失败
检查方式: 在主节点授权用户给从节点之后,在从节点服务器上尝试远程连接一下主节点: mysql -h主节点ip -u授权用户名 -p密码
如果连接成功则没问题,否则就有问题

连接失败主要原因有 :
a.主节点的配置文件设置了 bind-address = 127.0.0.1 只允许主机连接;
b.主节点或者从节点设置防火墙

解决方法:
a.在主节点和从节点的配置文件的[mysqld]下修改 bind-address=0.0.0.0 允许任何ip连接;或者 bind-address = 127.0.0.1 其他ip 这样就只允许本机和指定的其他ip连接本服务器的mysql
b.将主从服务器的防火墙都关闭

2. 主从节点的mysql版本不同导致同步sql失败

检查方式和原因:
a.首先,同步sql失败,我们可以在从节点通过 show slave status\G; 查看相关失败信息。

会看到 Slave_SQL_Running: No
然后找到 Last_SQL_Error 字段,会显示错误原因。
如果该字段显示的错误原因不详细,可以查看mysql的错误日志查看详细的原因。

b.mysql版本不同的情况下,并非不能进行主从复制;
此时 高版本作为从节点,低版本作为主节点可以进行同步;反之不行。
原因是 不同版本的mysql,其mysqlbinlog的版本不同,5.5及以下,其mysqlbinlog的版本为3.3,以上版本为3.4;
高版本的mysqlbinlog可以兼容低版本的mysqlbinlog命令,也就是说,高版本的mysqlbinlog命令可以解析低版本mysql的二进制日志,但是反之低版本的mysqlbinlog命令不能解析高版本mysql生成的二进制日志。

而我们知道,主从复制的原理就是从节点同步主节点的二进制日志,然后执行所同步的二进制日志中增删改命令;
当高版本作为从节点,高版本的mysqlbinlog命令就能够解析低版本mysql生成的二进制日志并执行里面的命令从而同步。
当低版本作为主节点,那么就会报错,就是因为无法解析高版本的日志内容。

假如 低版本作为从节点,高版本作为主节点,那么查看错误日志会发现报错:
Error in Log_event::read_log_event(): 'Found invalid event in binary log', data_len: xx, event_type: xx

就是因为mysqlbinlog的版本不同。

所以mysql版本不同导致同步sql失败的本质原因是:mysqlbinlog命令版本不同;像mysql5.6和5.7版本的mysqlbinlog的版本都是3.4 ,那么也不会出现这个问题。

查看mysqlbinlog命令的版本:
mysqlbinlog -V

c.在主从节点不一致的情况下,推荐使用mixed格式的二进制日志文件,在主从节点的配置文件下同时设置 binlog_format=mixed;这样可以防止因为二进制日志格式不同引起的同步sql失败

当然,这样做依旧不能解决mysql版本不同导致同步sql失败这个问题

解决方法:
a.高版本作为从节点,低版本作为主节点
b.如果非要高版本作为主节点,低版本作为从节点,只能升级从节点的mysql版本。

结论:
主从节点尽可能都是相同版本的mysql

3.部分同步的问题

如果从节点只同步主节点的某一个库或者部分库,可以使用一下四个配置:
binlog-do-db=库名            # 在主节点配置,表示只记录指定库的操作到二进制日志,结果是只同步该库
binlog-ignore-db=库名        # 在主节点配置,表示不记录指定库的操作到二进制日志,结果是不同步该库
replicate-do-db=库名         # 在从节点配置,表示只同步某库的binlog日志,结果是只同步该库
replicate-ignore-db=库名     # 在从节点配置,表示不同步某库的binlog日志,结果是不同步该库

replicate-do-db和binlog-do-db只需配置其中一个即可,无需两个服务器同时配置。建议视同replicate-do_db

如果要指定多个数据库,只需重复设置这个选项即可。

4.主从节点数据不一致问题
主从复制目的是让主服务器只做读操作,让从服务器做读操作,减轻数据库的负担。

所以从服务器本身是不应该写入数据的,所以可以在从服务器的配置中写入 read-only参数

注意:
a.read_only=1只读模式,限定的是普通用户进行数据修改的操作,但不会限定具有super权限的用户的数据修改操作
b.一旦设置了read_only,所有库的所有表都不能写,所以对于只同步部分库或者一个库的情况不合适
c.read_only不会影响主从节点的同步

5.从服务器因同步sql失败而停止运行

举个例子,有一种情况:
主节点:A
从节点:B
同步的库:d1

现在 A节点有库d1,d1有一个表t1
此时,B节点进行同步A节点
然后,A节点往d1.t1写入一条数据,B节点同步失败,查看show slave status
原因是,B还没有建立数据库d1

这个时候,由于上面的一个错误,导致A和B直接终止了同步,如果想再同步,就要重新执行 change master to 命令
而由于A做出了操作,所以A的二进制日志做出了改变,所以 master_log_pos 不同了,于是想再同步要做出以下命令

在A节点: show master status;    并记录下 File和Position字段
在B节点: stop slave;   # 必须先停止slave
change master to master_host="A的ip",master_password="xxx",master_user="user",master_log_file="xxx",master_log_pos="xxx";
start slave;

现在A和B又同步了,这个时候,B节点自己创建了d1库,然而还没等B自己创建t1表,A节点又往t1表中写入一条数据。
此时 A,B的同步再一次失败,原因很简单,B没有t1表,怎么能在t1表中插入数据呢。
于是B又要再重复一次上面的步骤才能将A,B再同步起来。

为了解决这个问题,可以在从节点使用 slave-skip-errors 配置
slave-skip-errors=all

表示从服务器跳过同步错误,这样就不会因为一两句错误导致同步断开。

设置了slave-skip-errors=all 之后,即使B没有t1表,A往t1表插入数据,B也不会有报错,而是生成提示记录在错误日志中。

当然,最好的处理是,一开始先将A的d1备份到B之后,再对AB进行同步。
同时也要在配置文件中设置slave-skip-errors=all,重启服务

双管齐下

6. 主节点或者从节点重启,是否要重新执行change master to和start slave命令?
从节点重启会自动执行change master to 和start slave 所以不用自己执行

主节点重启的话,我们知道,每一次重启就会生成一个新的binlog文件,并且使用这个新的binlog文件。
那么是不是从节点要重新执行change master to 来定位到主节点这个新的binlog文件呢
也不用,主节点重启时,主从节点的连接断开,查看show slave status;
Slave_IO_Running: Connecting
表示从节点正在连接主节点。

当主节点重启完成,从节点会重新指向主节点的新的binlog文件和其Position

7.二进制日志文件过多过大怎么办
expire_logs_days        = 10    # 10天前的二进制日志自动删除
max_binlog_size   = 100M        # 超过100M就自动生成新的二进制日志

这是针对二进制日志而不是针对同步日志relay-bin的,relay-bin会自动清理的。

8.start slave时报错:
Slave failed to initialize relay log info structure from the repository

这是由于之前进行过主从复制,没有清干净;

reset slave

然后再start slave即可。

主主复制实操

在上面主从复制的基础之上再进行主主复制

主服务器1 204.175.124.51
主服务器2 54.22.37.2
目标:同步主服务器的test数据库(test库在204.175.124.51上),但不对其他数据库同步

# 已知两台节点都已经开启binlog,而且在上一个实例中主节点1时主节点2的从节点;现在只需将54.22.37.2也变成主节点1的从节点,那么就实现了主主复制

=============== 主节点1的操作 ================

# 修改配置文件

server-id=1
log-bin=/var/lib/mysql/mysql-bin
binlog_format=mixed
relay-log=/var/lib/mysql/relay-bin
relay-log-index=/var/lib/mysql/relay-bin.index
expire_logs_days=7
max_binlog_size=100M
slave-skip-errors=all
replicate-do-db=hst             # 上面这些配置都是上一个实例中做主从复制时设置的

replicate-do-db=test            # 这个时做主主复制时添加的,节点1只同步节点2的test库

# 授权用户给节点2:

grant replication slave on *.* to "repl"@"54.22.37.2" identified by "xxxxx"

# 重启服务

# 再进入客户端

show master status;  记录下File和position字段
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000008 |     4498 |              |                  |
+------------------+----------+--------------+------------------+

 

=============== 主节点2的操作 ================

# 尝试远程连接节点1,连接成功再做下面的操作

# 先将test库备份到主节点2上面# 修改主节点2的配置文件,启动同步日志

server-id=86                                    # server-id
expire_logs_days    = 10                        # 二进制日志过期天数
max_binlog_size   = 100M                        # 二进制日志最大的大小
log-bin=/var/lib/mysql/mysql-bin                # 开启二进制日志,供从节点复制
binlog_format=mixed                             # 二进制日志使用statement和row的混合模式

replicate-do-db=test                            # 节点2作为从节点只同步test库
relay-log=/var/lib/mysql/relay-bin              # 开启同步日志
relay-log-index=/var/lib/mysql/relay-bin.index  # 指定同步日志的索引文件
slave-skip-errors=all                           # 跳过同步过程中的sql错误

# 重启服务

# 进入客户端

show master status;  记录下File和position字段
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000006 |   997126 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+

# 开始同步节点1

stop slave;
reset slave;
change master to master_host="204.175.124.51",master_user="repl",master_password="xxxxx",master_log_file="mysql-bin.000006",master_log_pos=4498;
start slave 

# 查看slave状态

show slave status\G;

*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 204.175.124.51
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000008
          Read_Master_Log_Pos: 4498
               Relay_Log_File: relay-bin.000006
                Relay_Log_Pos: 4695
        Relay_Master_Log_File: mysql-bin.000008
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: test
          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: 4498
              Relay_Log_Space: 5195
              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:
             Master_Info_File: /var/lib/mysql/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:
1 row in set (0.00 sec)

====================== 回到节点1 =====================

之前设置了节点1配置(添加了一个要同步的库test),应该不用再对节点1执行change master to 命令,因为重启节点1服务的时候应该会自动重新同步节点2的,为了确保节点1重新同步了节点2,可以查看

show slave status;

Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB: test,hst

如果Slave_IO_Running,Slave_SQL_Running都是Yes,而且Replicate_Do_DB为test,hst,那说明已经同步好了
不用再同步了。不然的话还得再同步一次。

接下来进行测试,就是在节点1和节点2都插入一些数据看看是不是都有相应变化。

此时 对于test库时主主复制,对于hst库是主从复制,即节点1同步节点2的hst库。

免责声明:
1. 本站资源转自互联网,源码资源分享仅供交流学习,下载后切勿用于商业用途,否则开发者追究责任与本站无关!
2. 本站使用「署名 4.0 国际」创作协议,可自由转载、引用,但需署名原版权作者且注明文章出处
3. 未登录无法下载,登录使用金币下载所有资源。
IT小站 » MYSQL教程优化篇:数据库多节点之Mysql主从复制,主主复制

常见问题FAQ

没有金币/金币不足 怎么办?
本站已开通每日签到送金币,每日签到赠送五枚金币,金币可累积。
所有资源普通会员都能下载吗?
本站所有资源普通会员都可以下载,需要消耗金币下载的白金会员资源,通过每日签到,即可获取免费金币,金币可累积使用。

发表评论