概述

  • 一般应用对数据库而言都是“ 读多写少 ”,也就说对数据库读取数据的压力比较大,有一个思路就是采用数据库集群的方案,做 主从架构 、进行 读写分离 ,这样同样可以提升数据库的并发处理能力。但并不是所有的应用都需要对数据库进行主从架构的设置,毕竟设置架构本身是有成本的。
  • 如果我们的目的在于提升数据库高并发访问的效率,那么首先考虑的是如何 优化SQL和索引 ,这种方式简单有效;其次才是采用 缓存的策略 ,比如使用 Redis将热点数据保存在内存数据库中,提升读取的效率;最后才是对数据库采用 主从架构 ,进行读写分离。

主从复制的作用

  1. 可以实现读写分离
    • 主库Master负责数据的写入,从库Slave负责数据的读取
    • 主库更新的时候,会通过主从复制将变更同步到从库中。
    • 对于“读多写少”,读写分离可以实现更高的并发访问。通过负载均衡,将请求分发到多个服务器上。同时由于读写分离,减少了锁表的影响。
  2. 进行数据备份:通过主从复制将主库上的数据复制到从库上,相当于是一种热备份机制
  3. 实现数据库的高可用性:当主库出现故障,可以切换到从库上,保障服务的正常运行。

主从复制原理

  • Slave 会从 Master 读取 binlog 来进行数据同步。

原理剖析

  • 涉及到的3个线程,1个主库线程,2个从库线程

    • 二进制日志转储线程 (Binlog dump thread)是一个主库线程。当从库线程连接的时候, 主库可以将二进制日志发送给从库,当主库读取事件(Event)的时候,会在 Binlog 上 加锁 ,读取完成之后,再将锁释放掉。
    • 从库 I/O 线程连接到主库,向主库发送请求更新 Binlog。这时从库的 I/O 线程就可以读取到主库的二进制日志转储线程发送的 Binlog 更新部分,并且拷贝到本地的中继日志 (Relay log)。
    • 从库 SQL 线程读取从库中的中继日志,并且执行日志中的事件,将从库中的数据与主库保持同步。

      过程如下,其中也包含上述3个线程

      主从复制 - 图1

  • 注意:主从复制需要保证binlog开启。默认情况下从服务器会执行所有主服务器中保存的事件,也可以通过配置,只执行特定的事件。

三个步骤

  • 步骤1: Master 将写操作记录到二进制日志( binlog )。
  • 步骤2: Slave 将 Master 的binary log events拷贝到它的中继日志( relay log );
  • 步骤3: Slave 重做中继日志中的事件,将改变应用到自己的数据库中。 MySQL复制是异步的且串行化的,而且重启后从 接入点 开始复制。

主从复制最大的问题:延时

主从复制基本原则

  • 每个 Slave 只有一个 Master
  • 每个 Slave 只能有一个唯一的服务器ID
  • 每个 Master 可以有多个 Slave

一主一从架构搭建

  • 主从都在/etc/my.cnf配置文件中进行配置,不要使用临时配置
  • 主从配置项全都在配置文件的[mysqld]节点下,全都使用小写字母

主机配置

  • 必选
    1. #[必须]主服务器唯一ID
    2. server-id=1
    3. #[必须]启用二进制日志,指明路径。比如:自己本地的路径/log/mysqlbin
    4. log-bin=/usr/local/etc/mysqlbinlog/binlog
  • 可选
    #[可选] 0(默认)表示读写(主机),1表示只读(从机) 
    read-only=0 
    #设置日志文件保留的时长,单位是秒 
    binlog_expire_logs_seconds=6000 
    #控制单个二进制日志大小。此参数的最大和默认值是1GB 
    max_binlog_size=200M 
    #[可选]设置不要复制的数据库 
    binlog-ignore-db=test 
    #[可选]设置需要复制的数据库,默认全部记录。比如:binlog-do-db=mytest_master_slave 
    binlog-do-db=需要复制的主数据库名字 
    #[可选]设置binlog格式 
    binlog_format=STATEMENT
    
  • 主库配置

    主从复制 - 图2

    修改配置文件后需要重启mysql

从机配置

  • 必选
    #[必须]从服务器唯一ID 
    server-id=2
    
  • 可选
    #[可选]启用中继日志 
    relay-log=mysql-relay
    
  • 从机配置

    主从复制 - 图3

    重启mysql服务

主机建立账户并授权

  • 创建账户并授权Slave ```

    创建账户

    CREATE USER ‘slave1’@’%’ IDENTIFIED BY ‘123456’;

    授权主从复制

    GRANT REPLICATION SLAVE ON . TO ‘slave1’@’%’;

    需要执行 否则报下图的错

    ALTER USER ‘slave1’@’%’ IDENTIFIED WITH mysql_native_password BY ‘123456’;

flush privileges;



-  到这里,主机的配置就结束了,查看主机的状态信息 
> `show master status ;`
>  
> ![](https://blog.chasingwind.top/8pCSRP.png#crop=0&crop=0&crop=1&crop=1&id=XX228&originHeight=582&originWidth=2394&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)



<a name="c6d9266b"></a>
### 从机:配置需要复制的主机

-  配置从机复制的主机信息

CHANGE MASTER TO MASTER_HOST=’主机的IP地址’, MASTER_USER=’主机用户名’, MASTER_PASSWORD=’主机用户名的密码’, MASTER_LOG_FILE=’主机binlog’, MASTER_LOG_POS=具体值; ```

  • 启动Slave进行同步:START SLAVE

    如果如下报错

    主从复制 - 图4

  • 查看同步状态

    主从复制 - 图5

    两个都是yes表示成功

测试

  • 首先,在主机中创建需要同步的数据库

    主从复制 - 图6

    主从复制 - 图7

  • 主机创建表

    主从复制 - 图8

  • 主机添加记录

    主从复制 - 图9

停止主从同步停止主从同步命令(从机):stop slave;

  • 停止主从同步命令(从机):stop slave;

主机配置项binlog_format

  • 用于设置binlog格式
    • binlog_format=STATEMENT:基于SQL语句的复制(statement-based replication, SBR),默认格式
      • binlog中记录的是每一条修改的SQL语句
      • 优点
        • 历史悠久,技术成熟
        • 不需要记录每一行的变化,减少了binlog日志量,文件较小
        • binlog中包含了所有数据库更改信息,可以据此来审核数据库的安全等情况,可以通过SQL语句查看究竟做了什么操作
        • binlog可以用于实时的还原,而不仅仅用于复制
        • 主从版本可以不一样,从服务器版本可以比主服务器版本高
      • 缺点
        • 不是所有的UPDATE语句都能被复制,尤其是包含不确定操作的时候,比如date()LOAD_FILE()UUID()USER()FOUND_ROWS()SYSDATE()
          (除非启动时启用了 —sysdate-is-now 选项)
    • binlog_format=ROW:基于行的复制(row-based replication, RBR)
      • MySQL5.5开始支持,仅记录哪条数据被修改了,修改成什么样了。
      • 优点:
        • 任何情况都可以被复制,这对复制来说是最安全可靠的。(比如:不会出现某些特定情况下的存储过程、function、trigger的调用和触发无法被正确复制的问题
        • 相对于SBR来说,复制以下几种语句时的行锁更少:INSERT … SELECT、包含 AUTO_INCREMENT 字段的 INSERT、没有附带条件或者并没有修改很多记录的 UPDATE 或 DELETE 语句
        • 执行 INSERT,UPDATE,DELETE 语句时锁更少
        • 从服务器上采用 多线程 来执行复制成为可能
      • 缺点:
        • binlog 大了很多
        • 复杂的回滚时 binlog 中会包含大量的数据
        • 主服务器上执行 UPDATE 语句时,所有发生变化的记录都会写到 binlog 中,而 SBR 只会写一次,这会导致频繁发生 binlog 的并发写问题
        • 无法从 binlog 中看到都复制了些什么语句
    • binlog_format=MIXED:混合模式复制(mixed-based replication, MBR)
      • 从5.1.8版本开始,MySQL提供了Mixed格式,实际上就是Statement与Row的结合
      • 在Mixed模式下,一般的语句修改使用statment格式保存binlog。如一些函数,statement无法完成主从复制的操作,则采用row格式保存binlog
      • MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志形式,也就是在Statement和Row之间选择一种。

其他

  • 以上我们搭建了一主一从架构,但是并不具备读写分离的功能。
  • 通常,可以使用数据库中间件来实现读写分离,比如MyCat

    主从复制 - 图10

主从同步数据一致性问题

  • 主从数据不一致的原因主要是主从延迟导致的,主要有两方面:
    1. 主从同步是通过binlog二进制文件进行的,在网络传输过程中存在着延迟
    2. 从库接收完binlog后到执行完事务之间的时间差。
  • 如何减少主从延迟?
    • 降低多线程大事务并发的概率,优化业务逻辑
    • 优化SQL,避免慢SQL, 减少批量操作 ,建议写脚本以update-sleep这样的形式完成。
    • 提高从库机器的配置 ,减少主库写binlog和从库读binlog的效率差。
    • 尽量采用 短的链路 ,也就是主库和从库服务器的距离尽量要短,提升端口带宽,减少binlog传输的网络延时。
    • 实时性要求的业务读强制走主库,从库只做灾备,备份。

解决一致性问题

  • 这里的一致性是最终一致性
  • 读写分离情况下,解决主从同步中数据不一致的问题, 就是解决主从之间数据复制方式的问题,如果按照数据一致性从弱到强 来进行划分,有以下 3 种复制方式。

异步复制

  • 默认场景,数据一致性最弱
  • 主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理。这样就会有一个问题,主库如果crash掉了,此时主库上已经提交的事务可能并没有传到从上,如果此时强行将从提升为主,可能导致新主上的数据不完整。

    主从复制 - 图11

半同步复制

  • MySQL5.5之后开始支持
  • 主库在执行完客户端提交的事务之后不立即将结果返回给客户端。而是至少有一个(可配置)从库接收到Binlog,并写入中继日志中,再返回给客户端。
  • 相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,这个延迟最少是一个TCP/IP往返的时间。所以,半同步复制最好在低延时的网络中使用。
  • MySQL5.7中增加了参数rpl_semi_sync_master_wait_for_slave_count设置应答的从库的个数

    主从复制 - 图12

组复制

  • 异步复制和半同步复制都无法保证数据的一致性问题(保证最终一致性)
  • 而对实时性要求更高的场景,可以采用组复制技术((MySQL Group Replication,MGR),采用Paxos协议的状态机复制。
  • 首先我们将多个节点共同组成一个复制组,在 执行读写(RW)事务 的时候,需要通过一致性协议层(Consensus 层)的同意,也就是读写事务想要进行提交,必须要经过组里“大多数人”(对应 Node 节点)的同意,大多数指的是同意的节点数量需要大于 (N/2+1),这样才可以进行提交,而不是原发起方一个说了算。
  • 而针对 只读(RO)事务 则不需要经过组内同意,直接 COMMIT 即可。
  • 在一个复制组内有多个节点组成,它们各自维护了自己的数据副本,并且在一致性协议层实现了原子消息和全局有序消息,从而保证组内数据的一致性。

    主从复制 - 图13