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主服务器写入一段数据, MySQL主服务器将写入的数据发送给MySQL从服务器, 然后直接返回客户端OK, 可能从服务器的数据会和主服务不一致 
    半同步复制:指的是客户端连接到
    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/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)

配置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启动
###
######省略##################

配置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)

验证

因为在我的环境中,即使是半同步复制,也是直接就完成,看不出效果,所以我们故意将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