MySQL主从复制:半同步、异步

大纲

  • 前言

  • 如何对MySQL进行扩展?

  • MySQL Replication WorkFlow

  • MySQL主从复制模式

  • 实战演练

    • MySQL异步复制实现

    • MySQL半同步复制实现

  • 实验中的思考

  • 总结

前言

本篇我们介绍MySQL Replication的相关内容, 我们首先介绍MySQL CLuster的实现原理和如何一步步构建一个MySQL Replication Cluster

看懂本文需要了解: MySQL基本操作,MySQL日志类型及其作用

如何对MySQL进行扩展?

大家之前应该了解; 在单台服务器性能不足时, 有两种方式进行扩展

  • Scale Up: 垂直扩展,指的是提升单台服务器的性能(硬件)来提升服务的性能 

  • Scale Out: 水平扩展,指的是添加服务器通过负载均衡的方式来分担单台服务器的负载, 从而提升服务的性能

我们可以通过LVS, HAProxy, Nginx等软件实现一些服务的负载均衡, 但是如果要用到MySQL上就会有一些问题, 如下

  • 如何保证多台MySQL服务器数据的一致性

  • 如何保证多台服务器同时提交事务导致数据的完整性

  • 如何保证一台服务器宕机时, 其的事务能够正常提交或ROLLBACK

我们需要考虑的问题比扩展WEB服务多太多了, 毕竟大家都知道, 数据无价, 在很多重要场景中, 数据不能有半点闪失, 但是MySQL对这方面做得并不是很好, 而Oracle数据库在这方面特别的厉害, 所以众多的银行都采用Oracle数据库存储客户的账户信息等, 这么说, 难道我们就不用MySQL了么? 这显然不可能, 我们首先来介绍一下MySQL Replication Cluster的几种同步数据方式

  • Synchronous Replication 同步复制

  • Asynchronous Replication 异步复制

  • Semisynchronous Replication 半同步复制

    同步复制: 指的是客户端连接到MySQL主服务器写入一段数据,MySQL主服务器同步给MySQL从服务器,需要等待从服务器发出同步完成的响应才返回客户端OK,这其中等待同步的过程是阻塞的, 如果有N台从服务器, 效率极低。
    异步复制: 指的是客户端连接到MySQL主服务器写入一段数据,在主库写入Binlog日志后即可成功返回客户端OK,无需等待Binlog日志传送给从库。在一段时间内MySQL主服务器会将写入的数据发送给MySQL从服务器,在这段时间之内,从服务器的数据可能会和主服务不一致,一旦主库宕机,有可能会丢失日志。
    半同步复制: 指的是客户端连接到MySQL主服务器写入一段数据,MySQL主服务器只将数据同步复制给其中一台从服务器,半同步复制给其他的从服务器,来达到其中一台从服务器完全同步的效果。

MySQL Replication WorkFlow

Master/Slave工作流程

blob.png

建立主从关系后, 主服务器如果有数据修改之后, BINLOG会更新, 从服务通过IO-Thread读取主服务器的BINLOG到本地的Relay Log, 再通过SQL-Thread对其进行重放(replay), 从而同步到本地

MySQL主从复制模式

在不同的业务模型中我们可以采用不同的MySQL主从复制架构, 一般分为以下两种

  • Master/Slave: 指的是一主多从模式, 这种模式下可以有效的分担读请求, 但是写请求并不能完成负载分担、从节点可能数据不一致, 并且如果Master宕机了客户端就无法进行写操作了, 还是有很多问题的

  • Master/Master: 指的是多主模式, 这种模式中的多台MySQL服务器都是Master, 也就意味着都可以进行读写操作, 但是有着更为严重的问题, 多个客户端同时写入数据时由于复制延迟可能到导致数据冲突等严重问题

常用的架构图

我们可以使用keepalived实现Master高可用, 并且使用半同步模式实现数据的完全同步

blob.png

上面那种方式太占用Master的带宽, 我们可以让一台Slave扮演为Master, 为其他Slave同步数据

blob.png

实战演练

MySQL异步复制实现

实验拓扑

blob.png

环境部署

我们需要在各台服务器上安装MySQL, 这里使用的是rpm包安装, 版本为5.1

[root@node1 ~]# yum install mysql-server -y
[root@node2 ~]# yum install mysql-server -y
[root@node3 ~]# yum install mysql-server -y

配置文件

node1配置文件(master)

    [mysqld]
    datadir=/var/lib/mysql
    socket=/var/lib/mysql/mysql.sock
    user=mysql
    # Disabling symbolic-links is recommended to prevent assorted security risks
    symbolic-links=0
    innodb_file_per_table = 1
    log_bin=master-log    #开启二进制日志
    log_bin_index=1
    server_id=1           #设置serverid

    [mysqld_safe]
    log-error=/var/log/mysqld.log
    pid-file=/var/run/mysqld/mysqld.pid

node2配置文件(slave1)

    [mysqld]
    datadir=/var/lib/mysql
    socket=/var/lib/mysql/mysql.sock
    user=mysql
    # Disabling symbolic-links is recommended to prevent assorted security risks
    symbolic-links=0
    relay-log=relay-log         #开启relay日志
    innodb_file_per_table = 1
    read-only = 1               #设置只读
    server_id = 2               #设置serverid

    [mysqld_safe]
    log-error=/var/log/mysqld.log
    pid-file=/var/run/mysqld/mysqld.pid

node3配置文件(slave2)

    [mysqld]
    datadir=/var/lib/mysql
    socket=/var/lib/mysql/mysql.sock
    user=mysql
    # Disabling symbolic-links is recommended to prevent assorted security risks
    symbolic-links=0
    relay-log=relay-log
    innodb_file_per_table = 1
    read-only = 1
    server_id = 3

    [mysqld_safe]
    log-error=/var/log/mysqld.log
    pid-file=/var/run/mysqld/mysqld.pid


##启动mysql
##注意: serverid一定不能相同

配置Master

Slave节点进行同步需要通过一个特定权限的用户进行, 所以我们需要创建一个用户并赋予REPLICATION SLAVE, REPLICATION CLIENT权限

mysql> GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'rpuser'@'%' IDENTIFIED BY 'passwd';
Query OK, 0 rows affected (0.00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW MASTER STATUS;   我们要记录下pos的数值
+-------------------+----------+--------------+------------------+
| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+-------------------+----------+--------------+------------------+
| master-log.000005 |      523 |              |                  |
+-------------------+----------+--------------+------------------+
1 row in set (0.00 sec)

注意这里的语句: GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'rpuser'@'%' IDENTIFIED BY 'passwd';

我们来逐步分析下:

GRANT 授予

REPLICATION SLAVE权限:
The REPLICATION SLAVE privilege should be granted to accounts that are used by slave servers to connect to the current server as their master. Without this privilege, the slave cannot request updates that have been made to databases on the master server. 
REPLICATION SLAVE是一个必须而基本的权限,它直接授予slave服务器以该账户连接master后可以执行replicate操作的权利。

REPLICATION CLIENT权限:

The REPLICATION CLIENT  privilege enables the use of SHOW MASTER STATUS and SHOW SLAVE STATUS. 
REPLICATION CLIENT 使得用户可以使用SHOW MASTER STATUS和SHOW SLAVE STATUS命令,也就是说这个权限是用于授予账户监视Replication状况的权力。

一般来说,我们会单独在主服务器创建一个专门用于Replication的账户。这个账户必须具有REPLICATION SLAVE权限,除此之外没有必要添加不必要的权限,保证该用户的职责单一。

ON *.*: 权限加载到的对象上,*.*表示所有;

TO: 把权限赋予给谁;

'rpuser'@'%' INDENTIFIED BY 'passwd': 用户名为rpuser的用户,且该用户的服务器ip为%,这里用了通配符,就是不限ip,如果是@'192.168.1.%'则表示192.168.1.1到192.168.1.255之间的所有ip,用'passwd'作为密码进行校验。

这样做的好处:一方面使用同一账户对Replication进行监视管理会很方便,不必区分slave,master,另一方面,repl账户在slave和master上的配制是一样的,这样如果我们切换slave和master,账户不需要做任何改动。

配置Slave(1)

mysql> CHANGE MASTER TO
    -> MASTER_HOST='172.16.1.2',
    -> MASTER_USER='rpuser',
    -> MASTER_PASSWORD='passwd',
    -> MASTER_LOG_FILE='master-log.000005',
    -> MASTER_LOG_POS=523;
Query OK, 0 rows affected (0.01 sec

mysql> SHOW SLAVE STATUS\G;  #查看相应信息
#########省略##################
Master_Log_File: master-log.000005
Read_Master_Log_Pos: 523
Relay_Log_File: relay-log.000001
Relay_Log_Pos: 4
Relay_Master_Log_File: master-log.000005
Slave_IO_Running: No    #IO-thread没有启动
Slave_SQL_Running: No   #SQL-thread没有启动
#########省略##################

mysql> START SLAVE;  #启动sql-thread和io-thred

mysql> SHOW SLAVE STATUS\G;  #查看相应信息 
Query OK, 0 rows affected (0.00 sec)
#########省略##################
Master_Log_File: master-log.000005
Read_Master_Log_Pos: 523
Relay_Log_File: relay-log.000002
Relay_Log_Pos: 252
Relay_Master_Log_File: master-log.000005
Slave_IO_Running: Yes     #IO-thread启动
Slave_SQL_Running: Yes    #SQL-thread启动
#########省略##################

注意这里的几项配置:

    -> MASTER_HOST='172.16.1.2',
    -> MASTER_USER='rpuser',
    -> MASTER_PASSWORD='passwd',

必须和MASTER上执行的命令配置相符。

配置slave(2)

过程和配置slave(1)相同, 不做演示

测试主从复制

##在主服务器上创建数据库和表

[root@node1 ~]# mysql

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| test               |
+--------------------+
3 rows in set (0.00 sec)

mysql> CREATE DATABASE replication;
Query OK, 1 row affected (0.00 sec)

mysql> USE replication;
Database changed
mysql> CREATE TABLE t1 (id int unsigned auto_increment primary key, name char(30));
Query OK, 0 rows affected (0.01 sec)

mysql> DESC t1;
+-------+------------------+------+-----+---------+----------------+
| Field | Type             | Null | Key | Default | Extra          |
+-------+------------------+------+-----+---------+----------------+
| id    | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| name  | char(30)         | YES  |     | NULL    |                |
+-------+------------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

mysql> SHOW MASTER STATUS;
+-------------------+----------+--------------+------------------+
| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+-------------------+----------+--------------+------------------+
| master-log.000005 |      765 |              |                  |
+-------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
##在slave服务器测试

[root@node2 ~]# mysql

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| replication        |
| test               |
+--------------------+
4 rows in set (0.00 sec)

mysql> use replication;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> DESC t1;
+-------+------------------+------+-----+---------+----------------+
| Field | Type             | Null | Key | Default | Extra          |
+-------+------------------+------+-----+---------+----------------+
| id    | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| name  | char(30)         | YES  |     | NULL    |                |
+-------+------------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

mysql> SHOW SLAVE STATUS\G;

#########省略##################
Master_Log_File: master-log.000005
Read_Master_Log_Pos: 765   #已经同步
Relay_Log_File: relay-log.000002
Relay_Log_Pos: 494
Relay_Master_Log_File: master-log.000005
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
#########省略##################

 

MySQL半同步复制实现

由于MySQL半同步复制在MySQL5.5以后才以插件的形式进行提供, 所以这里我们的MySQL要换成5.5版本的MariaDB

由于很多步骤和上面重复,我就不写出来了,先配置成M/S然后再按照我下面操作

实验拓扑

blob.png

配置master

半同步的插件在/usr/lib64/mysql/plugin下, master用的semisync_master.so,slave用的semisync_slave.so,

MariaDB [(none)]> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> SET GLOBAL rpl_semi_sync_master_enabled = 1;
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> SET GLOBAL rpl_semi_sync_master_timeout = 2000;   #设置超时时间为2S
Query OK, 0 rows affected (0.00 sec)

配置slave

MariaDB [(none)]> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> SET GLOBAL rpl_semi_sync_slave_enabled=1;
Query OK, 0 rows affected (0.00 sec)

正常情况下,这两个PLUGIN在master和slave上都应该被标准安装,谁知道哪天备库会不会被切换成主库呢。

安装完PLUGIN后,我们可以根据拓扑结构来定义主库和备库的配置,主要包括以下几个配置项;
1.rpl_semi_sync_master_enabled  
  —控制主库上是否开启semisync, 打开或关闭,立刻生效
2.rpl_semi_sync_slave_enabled     
  —控制备库是否开启semisync,
当主库打开semisync时,则必须至少要有一个链接的备库是打开semisync的,否则主库线程每次都会去等待,直至超时;因此如果想关闭semisync必须要先关闭主库配置,再关闭备库配置
3.rpl_semi_sync_master_timeout    
  —控制主库上客户端的等待时间,当超过这么长时间等待后,客户端返回,同步复制退化成原生的异步复制
  单位为毫秒,默认值为10000,即10秒  
4.rpl_semi_sync_master_wait_no_slave
默认打开,表示当备库起来后,并跟上主库时,自动切换到同步模式,如果关闭,即使备库起来并跟上了,也不会启用半同步;
5.rpl_semi_sync_master_trace_level 以及 rpl_semi_sync_slave_trace_level
— 输出监控信息的级别,详细点击见文档,不同的级别,可能输出更详细的信息,用于DEBUG
运行状态变量也比较丰富,不细说了,网上介绍的很多,官方文档也很详细

验证

因为在我的环境中,即使是半同步复制,也是直接就完成,看不出效果,所以我们故意将slave节点关闭
[root@node2 ~]# service mysql stop



##master创建数据库
MariaDB [(none)]> CREATE DATABASE TEST3;
Query OK, 1 row affected (2.00 sec)        #等待2s, 超时不再等待,直接创建

MariaDB [(none)]> CREATE DATABASE TEST4;
Query OK, 1 row affected (0.00 sec)


##完成,这个可能不是特别直观,但是由于我这边环境实在做不出效果,望大家理解

实验中的思考

  • 如果主从服务器数据相差较大, 最好先使用主服务器的二进制日志在从服务器上replay一遍, 然后再进行同步

总结

这篇其实还打算写SSL复制的,但是因为时间比较紧,就没有写了,总体来说不是特别的满意,有很多地方没有说明白,还望大家谅解。

作者水平很低, 如果有错误及时指出, 如果你觉得本文写的好请点一波赞~(≧▽≦)/~ 
作者: AnyISaIln QQ: 1449472454 
感谢: MageEdu

猜你喜欢

转载自my.oschina.net/u/3412738/blog/1801192