美文网首页
MySQL 主从复制

MySQL 主从复制

作者: wayyyy | 来源:发表于2022-04-08 01:46 被阅读0次

    MySQL 的主从复制又叫 replication、AB 复制。至少需要两个 MySQL 服务(可以是同一台机器,也可以是不同机器之间进行)。

    比如A服务器做主服务器,B服务器做从服务器,在A服务器上进行数据的更新,通过 binlog 日志记录同步到B服务器上,并重新执行同步过来的 binlog 数据,从而达到两台服务器数据一致。

    • 作用
      • 可以实时灾备,用于故障切换;
      • 读写分离,提供查询服务,实现负载均衡;
    MySQL 主备的基本原理
    image.png

    备库 B 跟主库 A 之间维持了一个长连接。主库 A 内部有一个线程,专门用于服务备库 B 的这个长连接。一个事务日志同步的完整过程是这样的:

    • 在备库 B 上通过 change master 命令,设置主库 A 的 IP、端口、用户名、密码,以及要从哪个位置开始请求 binlog,这个位置包含文件名和日志偏移量。
    • 在备库 B 上执行 start slave 命令,这时候备库会启动两个线程,就是图中的 io_thread 和 sql_thread。其中 io_thread 负责与主库建立连接。
    • 主库 A 校验完用户名、密码后,开始按照备库 B 传过来的位置,从本地读取 binlog,发给 B。
    • 备库 B 拿到 binlog 后,写到本地文件,称为中转日志(relay log)。
    • sql_thread 读取中转日志,解析出日志里的命令,并执行。
    binlog 的 三种格式对比
    复制拓扑
    数据复制策略
    • 异步复制
      主库将事务写入到 Binlog 文件中,并通知 dump 线程发送这些新的 Binlog,然后主库就会继续处理提交操作,所以此时无法保证这些 Binlog 已经成功传到任何一个从库节点上。

      假设如果此时主库上已经提交的事务因为某些原因未传送到从库,同时主库发生宕机,且在此时从库提升为主库,就会导致新主库数据缺失,从而造成主从数据不一致的情况发生。该复制模式下必然存在此问题。

    • 半同步复制
      主库需要等待至少一个从库节点收到日志事件,并刷新 Binlog 到 Relay Log 文件中的 ACK 确认消息后,才能提交并返回,主库一般不需要等待所有从库的 ACK。注意 :该 ACK 是确保日志传送到从库并写入到中继日志,而不是从库已经完成重放、将数据写入完成。详见图 1。

      image.png

      相对于异步复制,半同步复制提高了数据的安全性,同时也带来了一定程度的延迟,这个延迟最少是一个 TCP/IP 往返的时间。所以,半同步复制最好在低延时的网络中使用。

      一般在实际生产中,我们把同城或同机房,网络延迟小的(2ms 内)设置为半同步,对于异地延迟在 10ms 以上的,采用异步复制的方式。并且一般将 rpl_semi_sync_master_wait_for_slave_count 设置为1,在数据一致性的情况下,可以最大程度保证主库事务处理的性能

    • 同步复制
      主库执行完一个事务,且所有从库都执行了该事务才返回给客户端。因为需要等待所有从库才能返回,所以事务的时间会被拉长,从而性能必然会受到较大影响,一般不建议采用此方案。

    主从异步复制实践

    从上面复制的机制上可以知道,在复制进行前,slave上 必须具有 master 上部分完整内容作为复制基准数据。例如, master 上有数据库A,二进制日志已经写到了 pos1 位置,那么在复制进行前,slave 上必须要有数据库A,且如果要从 pos1 位置开始复制的话,就必须要有和 master上 pos1 之前完全一致的数据。

    如果不满足这样的一致性条件,那么在 relay log 的时候将不知道如何进行应用而导致数据混乱。也就是说,复制是基于 binlog 的 position 进行的,复制之前必须保证position一致。

    下面是用 mysql docker来实践主从异步复制。

    • 创建主服务器:

      # mkdir -p master/conf master/data master/logs
      # docker run --name mysql-master \
      -p 4306:3306 \
      -v `pwd`/master/conf:/etc/mysql/conf.d \
      -v `pwd`/master/data:/var/lib/mysql \
      -v `pwd`/master/logs:/var/log/mysql \
      -e MYSQL_ROOT_PASSWORD=123456 \
      -d mysql
      
    • 创建备服务器:

      # mkdir -p backup/conf backup/data backup/logs
      # docker run --name mysql-slave
      -p 5306:3306 \
      -v `pwd`/slave/conf:/etc/mysql/conf.d \
      -v `pwd`/slave/data:/var/lib/mysql \
      -v `pwd`/slave/logs:/var/log/mysql \
      -e MYSQL_ROOT_PASSWORD=123456 \
      -d mysql
      
    • 模拟数据
      给主数据库创建数据库表并插入几条模拟的数据:

    • 配置文件修改:
      主服务器:

      [mysqld]
      ...
      server-id = 1
      binlog-do-db = TestDB  # 要同步的数据库
      log-bin = /var/log/mysql/binlogs/mysql-bin # 要生成二进制日志文件 主服务器一定要开启,这里注意要给mysql权限
      expire_logs_days=7  # 7天过期
      

      备服务器:

      [mysqld]
      ...
      server-id = 2
      binlog-do-db = TestDB  # 要同步的数据库
      

      可以使用如下命令查看本数据库的 server_id

      mysql> show variables like 'server_id';
      
    • 创建主服务器复制用户相关权限用于从服务器连接

      mysql> create user 'slave'@'%' identified by '123456';  // 创建用户
      mysql> grant replication slave,replication client on *.* to 'slave'@'%';  // 设置用户权限
      mysql> flush privileges;  // 刷新权限
      mysql> show grants for 'slave'@'%';  // 查看用户权限
      

      这一步可以在从服务器上连接登录主服务器验证。

    • 数据同步
      前面提到同步需要从服务器的起点和主服务器一致,所以这里把主服务器数据导出来给从服务器

      mysql> FLUSH TABLES WITH READ LOCK;
      

      FLUSH TABLES WITH READ LOCK 执行后所有库所有表都被锁定只读,这时候,写操作将会被阻塞,读操作可顺利进行。
      将数据导出再导入到从数据库后,执行如下命令,解除数据库的锁定。

      mysql> UNLOCK TABLES;
      
    • 从服务器开启复制
      主服务器查看 binlog 状态:

      mysql> show master status;
      

      连接主服务器及设置复制的起始节点:

      mysql> change master to master_host='172.17.0.3', \
      master_port=3306, \
      master_user='slave', \
      master_password='123456', \
      master_log_file='mysql-bin.000001', \
      master_log_pos=769;
      

      这里的 master_log_file master_log_pos 和上面在主服务器上查询的结果要一一对应。

      从服务器启动复制:

      mysql> start slave;
      

      查看复制状态:

      mysql> show slave status\G
      

      重点查看 Slave_IO_RUNNING: Yes 和 Slave_SQL_RUNNING: Yes
      如果这一步出错,那么需要使用,重置状态。

      mysql> reset slave
      

    参考资料
    1、https://mp.weixin.qq.com/s/Hr90StLHodObkDk4YoqM_A

    相关文章

      网友评论

          本文标题:MySQL 主从复制

          本文链接:https://www.haomeiwen.com/subject/tikzrltx.html