MySQL主从复制及读写分离

MySQL Replication 概述

mysql在互联网领域用的如此广泛很大一部分原因是是源于它的replication机制,简单实用,几台PC机子,很容易提高性能,乃中小网站必备良方。

首先什么情况下要扩展数据库,建个网站,建个数据库,某一天网站火了,访问量暴增,意味着从你服务器上读网页的连接多了,IO瓶颈来了,自然想多加几台机子来分担压力,但是数据还要跟源主机上的数据库内数据保持一致,这时候就是开始扩展数据库的时候,replication就开始派上用场了。

MySQL Replication 俗称MySQL AB复制或主从复制,是MySQL官方推荐的数据同步技术。数据同步基本过程为从数据库会实时去读取主数据库的二进制日志文件,按照日志中记录对从库进行同样的操作,以达到数据同步效果。

MySQL Replication优点:

  • 通过增加从服务器来提高数据库平台的可靠性能。在主服务器上执行写入和更新,在从服务器上向外提供读功能,可以动态地调整从服务器的数量,从而调整数据库平台的高性能。
  • 提高数据安全性,因为数据已复制到从服务器,主数据库数据异常时,可以将从服务器复制进程终止来达到保护数据完整性的特点。
  • 在主服务器上生成实时数据,而在从服务器上分析这些数据,从而缓解主服务器的性能。

MySQL复制类型

 异步复制(Asynchronous replication

主库在执行完客户端提交的事务后会立即给客户端返回执行成功的消息,并不关心从库是否已经接收并处理,不太靠谱,因为这样会有一个问题,主库如果down掉了,此时主库上已经提交的事务可能并没有传到从库服务器上,如果此时,强行将从库提升为主库,可能会导致新主库上的数据不完整。MySQL默认的复制即是异步的,默认情况下MySQL5.5/5.6/5.7和mariaDB10.0/10.1的复制功能是异步的。

客户端事务在存储引擎层提交后,在得到从库确认的过程中,主库宕机了。此时可能的情况有两种:

  • 事务还没发送到从库上

此时,客户端会收到事务提交失败的信息,客户端会重新提交该事务到新的主上,当宕机的主库重新启动后,以从库的身份重新加入到该主从结构中,会发现,该事务在从库中被提交了两次,一次是之前作为主的时候,一次是被新主同步过来的。

  • 事务已经发送到从库上

此时,从库已经收到并应用了该事务,但是客户端仍然会收到事务提交失败的信息,重新提交该事务到新的主上。

全同步复制(Fully synchronous replication

当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会收到严重的影响,返回客户端的响应速度也会被拖慢。但相对来说比较安全,比较靠谱。

半同步复制(Semisynchronous replication

MySQL5.5由谷歌(Google)贡献的补丁才开始支持半同步复制(semi Replication)模式,介于异步复制和全同步复制之间,它相当于是同步和异步的一个中和的复制方式。主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到relay log中才返回给客户端。相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,这个延迟最少是一个TCP/IP往返的时间。所以,半同步复制最好在低延时的网络中使用。当出现超时情况时,源主服务器会暂时切换到异步复制模式,直到至少有一台设置为半同步复制模式的从服务器及时收到信息为止。

MySQL 的复制方法

●  基于 SQL 语句的复制(statement-based replication, SBR):在主服务器上执行的SQL语句,在从服务器上执行同样的SQL语句,效率比较高。

(1) 优点:
  历史悠久,技术成熟。
  产生的binlog文件较小,比较节省空间。
  binlog中包含了所有数据库更改信息,可以据此来审核数据库的安全等情况。
  binlog可以用于实时的还原,而不仅仅用于复制。
  主从版本可以不一样,从服务器版本可以比主服务器版本高。
(2) 缺点:
  不是所有的UPDATE语句都能被复制,尤其是包含不确定操作的时候。
  调用具有不确定因素的 UDF 时复制也可能出问题
  使用以下函数的语句也无法被复制:
* LOAD_FILE()
* UUID()
* USER()
* FOUND_ROWS()
* SYSDATE() (除非启动时启用了 --sysdate-is-now 选项)
INSERT ... SELECT 会产生比 RBR 更多的行级锁

●  基于行的复制(row-based replication, RBR):主服务器把表的行变化作为事件写入到二进制日志中,主服务器把代表了行变化的事件复制到从服务中。

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

(2)缺点:
  binlog 文件太大
  复杂的回滚时 binlog 中会包含大量的数据
  主服务器上执行 UPDATE 语句时,所有发生变化的记录都会写到 binlog 中,而 SBR 只会写一次,这会导致频繁发生 binlog 的并发写问题
  UDF 产生的大 BLOB 值会导致复制变慢
  无法从 binlog 中看到都复制了写什么语句,无法进行审计。

●  基于global transaction identifiers(GTIDs)来进行事务复制。当使用GTIDs时可以大大简化复制过程,因为GTIDs完全基于事务,只要在主服务器上提交了事务,那么从服务器就一定会执行该事务。

GTID比传统复制的优势:

1、更简单的实现failover,不用以前那样在需要找log_file和log_Pos。

2、更简单的搭建主从复制。

3、比传统复制更加安全。

4、GTID是连续没有空洞的,因此主从库出现数据冲突时,可以用添加空事物的方式进行跳过。

●  混合模式复制(mixed-based replication, MBR):先采用基于SQL语句的复制,一旦发现基于SQL语句无法精确复制时,再采用基于行的复制。

是 SBR 和 RBR 的折中

复制的工作过程和原理

一主多从

==前提是作为主服务器角色的数据库服务器必须开启二进制日志==

  1. 主服务器上面的任何修改都会通过自己的 I/O tread(I/O 线程)保存在二进制日志 Binary log 里面。

  2. 从服务器上面也启动一个 I/O thread,通过配置好的用户名和密码, 连接到主服务器上面请求读取二进制日志,然后把读取到的二进制日志写到本地的一个Realy log(中继日志)里面。

  3. 从服务器上面同时开启一个 SQL thread 定时检查 Realy log(这个文件也是二进制的),如果发现有更新立即把更新的内容在本机的数据库上面执行一遍。

主从从

 

MySQL 主从异步复制

环境配置:

master             server1                  192.168.200.112

slave                server2                  192.168.200.113

slave                server3                  192.168.200.114

所有机器上的操作

[root@localhost ~]# systemctl stop firewalld

[root@localhost ~]# setenforce 0

[root@localhost ~]# iptables -F

在MySQL Master 上的配置NTP时间同步服务器

[root@localhost ~]# yum -y install ntp

[root@localhost ~]# vim /etc/ntp.conf        #添加两行

server 127.127.1.0

fudge 127.127.1.0 stratum 8

启动NTP服务

[root@localhost ~]# systemctl enable ntpd

[root@localhost ~]# systemctl start ntpd

在2个Slave节点上配置与Master进行时间同步

[root@localhost ~]# yum -y install ntpdate

[root@localhost ~]# /usr/sbin/ntpdate 192.168.200.111

 2 Jan 00:48:18 ntpdate[27781]: adjust time server 192.168.200.111 offset 0.301932 sec

  

配置mysql master服务器

1、在/etc/my.cnf 中修改或者增加如下内容:

[root@mysql-master ~]# vim /etc/my.cnf

[mysqld]

server-id=1

log-bin=mysql-binlog

log-slave-updates=true

重启mysql服务器

[root@mysql-master ~]# systemctl start mariadb

[root@mysql-master ~]# netstat -lnpt | grep :3306

tcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN      2196/mysqld

2、创建Replication用户

[root@mysql-master ~]# mysql -u root

MariaDB [(none)]> grant replication slave on *.* to 'myslave'@'192.168.200.%' identified by '123456';

Query OK, 0 rows affected (0.00 sec)

 

MariaDB [(none)]> flush privileges;

Query OK, 0 rows affected (0.00 sec)

3、获得Master DB的相关信息

MariaDB [(none)]> show master status;

+---------------------+----------+--------------+------------------+

| File                | Position | Binlog_Do_DB | Binlog_Ignore_DB |

+---------------------+----------+--------------+------------------+

| mysql-binlog.000002 |      479 |              |                  |

+---------------------+----------+--------------+------------------+

1 row in set (0.00 sec)

供Slave连接时使用,记录下File和Position的值。

4、备份Master原有数据

如果在生产环境中Master服务器已经运行一段时间,或者Master服务器上已经存在数据,为了保证所有数据的一致性,需要先将Master的数据全部导给Slave服务器。

备份的方法有很多,可以直接备份数据文件,也可以使用mysqldump工具。全新搭建的环境不存在数据备份的问题。

[root@mysql-master ~]# mysqldump -u root --all-databases > /root/alldbbackup.sql

[root@mysql-master ~]# scp /root/alldbbackup.sql root@192.168.200.113:/root/

[root@mysql-master ~]# scp /root/alldbbackup.sql root@192.168.200.114:/root/

5、在 MySQL Slave 上的配置

导入Master的备份脚本

[root@localhost ~]# mysql -u root -p < /root/alldbbackup.sql

从库连接主库进行测试,如果连接成功说明主库配置成功

[root@localhost ~]# mysql -u myslave -p123456 -h 192.168.200.111

修改MySQL配置文件

[root@localhost ~]# vim /etc/my.cnf

server-id = 2

relay-log=relay-log-bin

relay-log-index=slave-relay-bin.index

配置多个从服务器时依次设置server-id号

修改完后重启数据库

[root@localhost ~]# systemctl start mariadb

在 Slave服务器授权,启动从库,进行主从库数据同步

[root@localhost ~]# mysql -u root

MariaDB [(none)]> stop slave;

MariaDB [(none)]> CHANGE MASTER TO

MASTER_HOST='192.168.200.112',

MASTER_USER='myslave',

MASTER_PASSWORD='123456',

MASTER_LOG_FILE='mysql-binlog.000002',

MASTER_LOG_POS=479;

MariaDB [(none)]> start slave;

MariaDB [(none)]> show slave statusG;    #查看线程是否连接

 

参数说明:

CHANGE MASTER TO

MASTER_HOST='master_host_name',                    //主服务器的IP地址

MASTER_USER='replication_user_name',              //主服务器授权的用户

MASTER_PASSWORD='replication_password',      //主服务器授权的密码

MASTER_LOG_FILE='recorded_log_file_name',    //主服务器二进制日志的文件名

MASTER_LOG_POS=recorded_log_position;          //日志文件的开始位置

6、测试复制是否成功

在Master 服务器上创建一个数据库或者表,到 Slave 服务器上查看,如果配置成功就可以成功同步。

主库查看当前存在的库

MariaDB [(none)]> show databases;

+--------------------+

| Database           |

+--------------------+

| information_schema |

| mysql              |

| performance_schema |

| test               |

+--------------------+

4 rows in set (0.01 sec)

从库查看当前存在库

MariaDB [(none)]> show databases;

+--------------------+

| Database           |

+--------------------+

| information_schema |

| mysql              |

| performance_schema |

| test               |

+--------------------+

4 rows in set (0.01 sec)

说明两者中的数据保持了一致性

主库服务器创建库和表:

MariaDB [(none)]> create database sampdb;

Query OK, 1 row affected (0.00 sec)

 

MariaDB [(none)]> use sampdb;

Database changed

MariaDB [sampdb]> create table new(name char(20),phone char(20));

Query OK, 0 rows affected (0.21 sec)

从库验证:

MariaDB [(none)]> show databases;

+--------------------+

| Database           |

+--------------------+

| information_schema |

| mysql              |

| performance_schema |

| sampdb             |

| test               |

+--------------------+

5 rows in set (0.00 sec)

 

MariaDB [(none)]> use sampdb

Database changed

 

MariaDB [sampdb]> show  tables;

+------------------+

| Tables_in_sampdb |

+------------------+

| new              |

+------------------+

1 row in set (0.00 sec)

说明主从数据库创建成功。

报以下错误的解决方法:

ERROR 1201(HY000):Could now initialize master info structure; more error messages can be found in the MySQL error log

 

stop slave;

reset slave;

CHANGE MASTER TO

MASTER_HOST='192.168.200.111',

MASTER_USER='myslave',

MASTER_PASSWORD='123456',

MASTER_LOG_FILE='mysql-binlog.000003',

MASTER_LOG_POS=477;

start slave;

数据不同步解决办法:

MySQL [(none)]> stop slave;

Query OK, 0 rows affected (0.00 sec)

 

MySQL [(none)]> set global sql_slave_skip_counter=1;

Query OK, 0 rows affected (0.00 sec)

 

MySQL [(none)]> start slave;

Query OK, 0 rows affected (0.00 sec)

 
原文地址:https://www.cnblogs.com/2567xl/p/11680315.html