工作需要,笔记之用。文章很长,倒一杯茶慢慢看。

数据库的应用场景颇多,如 数据库双机同步,一主多从,多主多从,多主一从等;下文记录多主一从的配置及测试。

大多数复制场景中是一主或者一主多从。这种拓扑用于高可用性场景,读写分离。主机负责写入数据,丛集负责读数据,横向扩展读取程序。但是,多主一从是写入多个数据库实例,最后合并成一个结果。

多主一从使得从机从各主机同步接收业务信息(transactions),这样可以一部服务器为多个主机服务器备份,合并数据表,联合数据。(无去重)

    

MySQL 版本:5.7.10

配置机器:两主一从

1,从机配置,保证从机的仓库都存在一个表;

[mysqld]
master-info-repository=TABLE
relay-log-info-repository=TABLE

确保无误后,可以检查一下,如下显示则配置正常:

mysql> SHOW VARIABLES LIKE '%repository%';
+---------------------------+-------+
| Variable_name | Value |
+---------------------------+-------+
| master_info_repository | TABLE |
| relay_log_info_repository | TABLE |
+---------------------------+-------+
2 rows in set (0.00 sec)

接下来修改 server_id ,这里用了ip的后三位:

[mysqld]
server-id=141

输入下面代码可以捡测:

mysql> SHOW VARIABLES WHERE VARIABLE_NAME = 'server_id';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 141 |
+---------------+-------+
1 row in set (0.00 sec)  

注意:MySQL 5.7 安装后,请在错误日志里面去找密码,如下:

# grep root error.log
2015-12-09T05:34:01.639797Z 1 [Note] A temporary password is generated for root@localhost: T<e-hd0cgI!d  

接下来你肯定需要用到这个密码,所以最好修改一下,示例改为了“password”:

ALTER USER 'root'@'localhost' IDENTIFIED BY 'password';</font>

  

多主一从最关键的工作就是保证在你的两(或多)个源数据里面不要有相同的主键,特别是当你在用AUTO_INCREMENT 这一列时,如果有两个一样的主键可以想象同步到从机时数据就会紊乱。这里有一个可替代的方法作为参考。在配置时最好关掉GTID,不然在mysql initialize 初始化时会遇到一个问题,并且这些记录会被复制到从机上去。假设你最开始启动了GTID,配置完成后,然后你试图在从机上复制。当你检查主机的状态时(输入 SHOW MASTER STATUS), 你会看到这样的结果,主机上138个执行记录,现在会被复制到从机上:

mysql> SHOW MASTER STATUS\G
*************************** 1. row ***************************
File: mysql-bin.000002
Position: 1286
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 73fdfd2a-9e36-11e5-8592-00a64151d129:1-138
1 row in set (0.00 sec)

而在从机上 SHOW SLAVE STATUS 你会看到一些错误:

Last_Error: Error 'Can't create database 'mysql'; database exists' on query. Default database: 'mysql'. Query: 'CREATE DATABASE mysql;

以及 RETRIEVED GTID SET  会显示 已取回138个记录复制到了从机:

mysql> SHOW SLAVE STATUS\G
...
Retrieved_Gtid_Set: 73fdfd2a-9e36-11e5-8592-00a64151d129:1-138
..  

当然,如果配置完成前关掉了GTID那就不会有这些错误了。

三台机器配置完成之后,输入 SHOW MASTER STATUS 显示如下:

mysql> SHOW MASTER STATUS\G
*************************** 1. row ***************************
File: mysql-bin.000002
Position: 398
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set:
1 row in set (0.00 sec)

到此,整个架构已经创建,下面进行一些测试。如图,创建一个漫画收藏数据库:

从机

CREATE DATABASE `comicbookdb`;
use comicbookdb;
CREATE TABLE `comics` (
`comic_id` int(9) NOT NULL AUTO_INCREMENT,
`comic_title` varchar(60) NOT NULL,
`issue_number` decimal(9,0) NOT NULL,
`pub_year` varchar(60) NOT NULL,
`pub_month` varchar(60) NOT NULL,
PRIMARY KEY (`comic_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1;  

在主机和从机上都可以运行同样的SQL 语句。(因为在主机的一些列上我们用了AUTO_INCREMENT ,你可能觉得从机上就不用AUTO_INCREMENT 了。但是因为我们不会对从机做任何写入操作,所以你仍旧可以运行同样的语句,之后再去修改主键。后文会有详述。)

当数据从多个主机复制合并到一个从机时,复制程序将会处理 AUTO_INCREMENT 的问题。

下面在从机上创建表时对于comic_id 列时,没有声明 AUTO_INCREMENT出现了错误,

mysql> SHOW SLAVE STATUS\G...
Last_SQL_Error: Error 'Field 'comic_id' doesn't have a default value' on query. Default database: 'comicbookdb'. Query: 'INSERT INTO COMICS (comic_title, issue_number, pub_year, pub_month) VALUES('Fly Man','5','2014','03')'
...

为了处理comic_id的主键问题,最方便的方法就是在主机中配置 auto_increment_increment ,在my.cnf  或者是my.ini 中:

[mysqld]
auto_increment_increment = 2  

加这个变量需要重启mysql服务,但是你也可以直接在命令行操作,如下:

mysql> SET @@auto_increment_increment=2;
Query OK, 0 rows affected (0.00 sec)

验证一下:

mysql> SHOW VARIABLES WHERE VARIABLES_NAME = 'auto_increment_increment';
+-----------------------------+-------+
| Variable_name | Value |
+-----------------------------+-------+
| auto_increment_increment | 2 |
+-----------------------------+-------+
1 row in set (0.00 sec)  

如上 每个主键的增量就是2,只要每个主机的初始值不同就可以了。但不可以简单的设 0 或 1,因为如果是0的话,它会默认恢复到1,这样会引起冲突。所以我们设一个较大的值,最低位设置为 0 和 1,代码如下:

主机 # 1

CREATE DATABASE `comicbookdb`;
use comicbookdb;
CREATE TABLE `comics` (
`comic_id` int(9) NOT NULL AUTO_INCREMENT,
`comic_title` varchar(60) NOT NULL,
`issue_number` decimal(9,0) NOT NULL,
`pub_year` varchar(60) NOT NULL,
`pub_month` varchar(60) NOT NULL,
PRIMARY KEY (`comic_id`)
) ENGINE=InnoDB AUTO_INCREMENT=100000;

主机 # 2

CREATE DATABASE `comicbookdb`;
use comicbookdb;
CREATE TABLE `comics` (
`comic_id` int(9) NOT NULL AUTO_INCREMENT,
`comic_title` varchar(60) NOT NULL,
`issue_number` decimal(9,0) NOT NULL,
`pub_year` varchar(60) NOT NULL,
`pub_month` varchar(60) NOT NULL,
PRIMARY KEY (`comic_id`)
) ENGINE=InnoDB AUTO_INCREMENT=100001;

(这里的初始值在建表的时候设置,同样也可以在配置里面通过 auto_increment_offset 进行设置。)

建表完毕,在所有主机上启动 GTID。这里从机也启动了GTID, 以防之后在这部从机之下加另一台从机。启动GTID,需要修改配置:

[mysqld]
auto_increment_increment = 2
gtid-mode = on
enforce-gtid-consistency = 1

重启每个server之后,可以检查一下每一台主机状态,状态一致:

mysql> SHOW MASTER STATUS\G
*************************** 1. row ***************************
File: mysql-bin.000005
Position: 154
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set:
1 row in set (0.00 sec)

接下来对所有的主从都做了一个服务器重置(reset master),非必要。重置会清空所有的binnary log ,并且新建一个二值日志。

mysql> RESET MASTER;
Query OK, 0 rows affected (0.00 sec) mysql> SHOW MASTER STATUS\G
*************************** 1. row ***************************
File: mysql-bin.000001
Position:
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set:
1 row in set (0.00 sec) 

可以见到新的 binary log(mysql-bin.000001),开始位置是154.  我们往主机插入一些数据,然后再看看主机状态。

主机 #1

mysql> INSERT INTO COMICS (comic_title, issue_number, pub_year, pub_month) VALUES('Fly Man','1','2014','01');
Query OK, 1 row affected (0.02 sec) mysql> SHOW MASTER STATUS\G
*************************** 1. row ***************************
File: mysql-bin.000001
Position: 574
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 63a7971c-b48c-11e5-87cf-f7b6a723ba3d:1
1 row in set (0.00 sec)

可见 插入的 GTID 是 63a7971c-b48c-11e5-87cf-f7b6a723ba3d:1 ,冒号前面部分是主机的UUID,这个信息可以在data目录下的auto.cnf里面查到。

主机 #1

# cat auto.cnf
[auto]
server-uuid=63a7971c-b48c-11e5-87cf-f7b6a723ba3d  

再插入一行数据:

主机 #1

mysql> INSERT INTO COMICS (comic_title, issue_number, pub_year, pub_month) VALUES('Fly Man','2','2014','02');
Query OK, 1 row affected (0.05 sec) mysql> show master status\G
*************************** 1. row ***************************
File: mysql-bin.000001
Position: 994
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 63a7971c-b48c-11e5-87cf-f7b6a723ba3d:1-2
1 row in set (0.00 sec) mysql> select * from comics;
+----------+-------------+--------------+----------+-----------+
| comic_id | comic_title | issue_number | pub_year | pub_month |
+----------+-------------+--------------+----------+-----------+
| 100001 | Fly Man | 1 | 2014 | 01 |
| 100003 | Fly Man | 2 | 2014 | 02 |
+----------+-------------+--------------+----------+-----------+
2 rows in set (0.00 sec)  

会发现这个数值变成了2,那么我们再在第二号主机插入两行数据,并且查看状态:

主机 #2

mysql> INSERT INTO COMICS (comic_title, issue_number, pub_year, pub_month) VALUES('Fly Man','3','2014','03');
mysql> INSERT INTO COMICS (comic_title, issue_number, pub_year, pub_month) VALUES('Fly Man','4','2014','04'); mysql> show master status\G
*************************** 1. row ***************************
File: mysql-bin.000005
Position: 974
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 75e2e1dc-b48e-11e5-83bb-1438deb0d51e:1-2
1 row in set (0.00 sec) mysql> select * from comics;
+----------+-------------+--------------+----------+-----------+
| comic_id | comic_title | issue_number | pub_year | pub_month |
+----------+-------------+--------------+----------+-----------+
| 100002 | Fly Man | 3 | 2014 | 03 |
| 100004 | Fly Man | 4 | 2014 | 04 |
+----------+-------------+--------------+----------+-----------+
2 rows in set (0.00 sec)  

主机#2有不同的UUID, 这也是我们分辨GTID对应于哪一步主机。那我们现在已经有两组GTID的会复制到从机上。当然,从机也有自己的UUID.

主机#1 跟主机#2的GTID设置:

63a7971c-b48c-11e5-87cf-f7b6a723ba3d:1-2
75e2e1dc-b48e-11e5-83bb-1438deb0d51e:1-2

  

在此,通常我会确认下从机是没有运行的:

从机

mysql> show slave status\G
Empty set (0.00 sec)  

不同于平时的复制,在多主复制中,你需要为每一台主机创建一个通道,为这个通道命名。这里称之为 “master-142”(主机-142)和 “master-143”(主机143)去匹配server_id(就像IP一样)。接下来就演示如何开启主机#1(server_id=142)的数据复制。

从机

mysql> CHANGE MASTER TO MASTER_HOST='192.168.1.142', MASTER_USER='replicate', MASTER_PASSWORD='password', MASTER_AUTO_POSITION = 1 FOR CHANNEL 'master-142';
Query OK, 0 rows affected, 2 warnings (0.23 sec)

这里有两个警告,可以忽略。

mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
Level: Note
Code: 1759
Message: Sending passwords in plain text without SSL/TLS is extremely insecure.
*************************** 2. row ***************************
Level: Note
Code: 1760
Message: Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.
2 rows in set (0.00 sec)  

现在我们可以开启从机通道“master-142”:

mysql> START SLAVE FOR CHANNEL 'master-142';
Query OK, 0 rows affected (0.03 sec)  

这个命令同时启动了SQL_THREAD 跟 IO_THREAD。将来你会考虑停止一个或者多个线程,所以这里对应有一些语法,如何指定一个需要修改的通道:

START SLAVE SQL_THREAD FOR CHANNEL 'master-142';
START SLAVE IO_THREAD FOR CHANNEL 'master-142';  

也可以发一个简单的命令“START SLAVE” 为需要执行复制操作的通道来开启这两个线程。从机启动,可以见到GTID被取出并且开始写入数据库。

mysql> SHOW SLAVE STATUS FOR CHANNEL 'master-142'\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.1.142
...
Master_UUID: 63a7971c-b48c-11e5-87cf-f7b6a723ba3d
...
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
...
Retrieved_Gtid_Set: 63a7971c-b48c-11e5-87cf-f7b6a723ba3d:1-2
Executed_Gtid_Set: 63a7971c-b48c-11e5-87cf-f7b6a723ba3d:1-2
Auto_Position: 1
...
Channel_Name: master-142

检查从机,可以见到相关数据:  

mysql> select * from comics;
+----------+-------------+--------------+----------+-----------+
| comic_id | comic_title | issue_number | pub_year | pub_month |
+----------+-------------+--------------+----------+-----------+
| 100001 | Fly Man | 1 | 2014 | 01 |
| 100003 | Fly Man | 2 | 2014 | 02 |
+----------+-------------+--------------+----------+-----------+
2 rows in set (0.00 sec)

主机#1 完成之后,我们开始配置主机#2

CHANGE MASTER TO MASTER_HOST='192.168.1.143', MASTER_USER='replicate', MASTER_PASSWORD='password', MASTER_AUTO_POSITION = 1 FOR CHANNEL 'master-143';  

然后再次确认从机状态:

从机

mysql> SHOW SLAVE STATUS FOR CHANNEL 'master-143'\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.1.143
...
Master_UUID: 75e2e1dc-b48e-11e5-83bb-1438deb0d51e
...
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
...
Retrieved_Gtid_Set: 75e2e1dc-b48e-11e5-83bb-1438deb0d51e:1-2
Executed_Gtid_Set: 63a7971c-b48c-11e5-87cf-f7b6a723ba3d:1-2,
75e2e1dc-b48e-11e5-83bb-1438deb0d51e:1-2,
Auto_Position: 1
...
Channel_Name: master-143  

我们可以见到从机已经获取到两个GTID,并且已经在执行。再看看数据库表的内容,也能够发现四行数据都已经合并到了从机

mysql> select * from comics;
+----------+-------------+--------------+----------+-----------+
| comic_id | comic_title | issue_number | pub_year | pub_month |
+----------+-------------+--------------+----------+-----------+
| 100001 | Fly Man | 1 | 2014 | 01 |
| 100002 | Fly Man | 3 | 2014 | 03 |
| 100003 | Fly Man | 2 | 2014 | 02 |
| 100004 | Fly Man | 4 | 2014 | 04 |
+----------+-------------+--------------+----------+-----------+
4 rows in set (0.01 sec)  

复制过程处理了自增 AUTO_INCREMENT 的值。如果检查一下sql语句执行复制的原话,你会有所发现:

INSERT INTO COMICS (comic_title, issue_number, pub_year, pub_month) VALUES('Fly Man','1','2014','01')
INSERT INTO COMICS (comic_title, issue_number, pub_year, pub_month) VALUES('Fly Man','2','2014','02');
INSERT INTO COMICS (comic_title, issue_number, pub_year, pub_month) VALUES('Fly Man','3','2014','03');
INSERT INTO COMICS (comic_title, issue_number, pub_year, pub_month) VALUES('Fly Man','4','2014','04');  

在主机上生成的自增值会随着声明传递给从机。检查主机上的binary log, 来到comic_id 这一列 “SET INSERT_ID = 100001”,整段会随着sql语句一起传给从机

从机

# mysqlbinlog mysql-bin.000001
...
# at 349
#160106 21:08:01 server id 142 end_log_pos 349 CRC32 0x48fb16a2 Intvar
SET INSERT_ID=100001/*!*/;
#160106 21:08:01 server id 142 end_log_pos 543 CRC32 0xbaf55210 Query thread_id=1exec_time=0 error_code=0
use `comicbookdb`/*!*/;
SET TIMESTAMP=1452132481/*!*/;
INSERT INTO COMICS (comic_title, issue_number, pub_year, pub_month) VALUES('Fly Man','1','2014','01')
/*!*/;
...

整个讲解完毕,请大家多多指教。

测试结果:

还有一个方法,利用MyISAM的简单文件存储的特点来实现,可以参考链接:http://blog.csdn.net/lmocm/article/details/42125319

参考:

1,MySQL 5.7 Multi-Source Replication – Automatically Combining Data From Multiple Databases Into One

https://mysqlhighavailability.com/mysql-5-7-multi-source-replication-automatically-combining-data-from-multiple-databases-into-one/

2, MySQL Multi-Source Replication Overview

https://dev.mysql.com/doc/refman/5.7/en/replication-multi-source-overview.html

3,  MySQL多主一从的实现(MyISAM的简单文件存储)

http://blog.csdn.net/lmocm/article/details/42125319

MySQL 5.7 多主一丛同步的数据库配置(将多处数据源合并到一点)的更多相关文章

  1. mysql性能优化学习笔记(6)数据库配置优化&硬件优化

    一.操作系统配置优化:          1. 网络方面,修改/etc/sysctl.conf文件,增加tcp支持的队列数,减少断开连接时,资源的回收.          2. 打开文件数的限制.修改 ...

  2. mysql数据库配置主从同步

    MySQL主从同步的作用 .可以作为一种备份机制,相当于热备份 .可以用来做读写分离,均衡数据库负载 MySQL主从同步的步骤 一.准备操作 .主从数据库版本一致,建议版本5.5以上 .主从数据库数据 ...

  3. MySQL 5.7 深度解析: 半同步复制技术

    复制架构衍生史 在谈这个特性之前,我们先来看看MySQL的复制架构衍生史. MySQL的复制分为四种: 普通的replication,异步同步. 搭建简单,使用非常广泛,从mysql诞生之初,就产生了 ...

  4. MySQL和MsSQL实时自动同步---SyncNavigator 数据库同步软件

    需要MySQL数据库支持的狐友们有福了,MySQL和MsSQL实时自动同步---SyncNavigator 数据库同步软件   使用SyncNavigator轻松实现数据库异地同步.断点续传.异构同步 ...

  5. MySQL数据库主从同步安装与配置总结

    MySQL的主从同步是一个很成熟的架构,优点为: ①在从服务器可以执行查询工作(即我们常说的读功能),降低主服务器压力: ②在从主服务器进行备份,避免备份期间影响主服务器服务: ③当主服务器出现问题时 ...

  6. mysql用户权限分配及主从同步复制

    赋予wgdp用户查询权限: grant select on wg_dp.* to 'wgdp'@'%' IDENTIFIED BY 'weigou123'; grant all privileges ...

  7. Mysql高可用架构(主从同步)

    做高可用的优势 1.成本低 2.解决单点故障 3.不容易遇到性能瓶颈 一 .Mysql主从同步架构搭建案例 优点如下:·在业务繁忙阶段,在从服务器上可以执行查询工作(即我们常说的读写分离),降低主服务 ...

  8. Linux MySQL数据库文件同步及数据库备份

    Mysql数据库链接 mysql -uroot -p -hdatacenter.jiaofukeyan.com -P33069 1.文件同步 rsync -avz --delete root@(需要同 ...

  9. mysql 主从 同步原理及配置

    一.在mssql 里头实现同步镜像,只能主库用而镜像库不能同时用,而mysql 主从同步可以实现 数据库的读写分离,主库负责 update insert delete ,从库负责select 这样一来 ...

随机推荐

  1. text和submit框的border问题

    A1:在input框中,为type为text的输入框增加边框,它会在内容区域外额外增添,但是在submit框中,它会占用内容区块的一部分作为边框 <style type="text/c ...

  2. Linux下passwd和shadow文件内容详解

    一./etc/passwd /etc/passwd 文件是一个纯文本文件,每行采用了相同的格式: name:password:uid:gid:comment:home:shell name 用户登录名 ...

  3. tomcat监控(二)

    标签: linux 笔者Q:972581034 交流群:605799367.有任何疑问可与笔者或加群交流 这里介绍二种监控Tomcat的方法 使用windows版本的jdk监控 使用zabbix监控 ...

  4. ssh密钥分发与ansible

    笔者Q:972581034 交流群:605799367.有任何疑问可与笔者或加群交流 当我们公司的服务器达到几十台或几百台或更高的时候,利用批量管理工具管理系统是我们要做的 常用的批量管理工具有ans ...

  5. 循环while 、do…while 、for

    循环while .do-while .for 循环结构的选择? - 当对一个条件进行一次判断时,用if语句: - 当对一个条件进行多次判断时,用while语句: 1.While 循环条件成立就会执行循 ...

  6. Sql Server的艺术(四) SQL多表查询

    表的基本连接 SQL的一个重要特性就是能通过JOIN关键词,从多个交叉表中查询.分析数据. 连接表的目的 在关系数据库中,数据表设计的一个重要原则就是要避免冗余性. 减少了冗余信息,节省了数据库存储空 ...

  7. Java - 双冒泡法排序

    最开始的代码 我采用的是我原来进行快速排序所用的方法,一直做不出来. 为什么我会采用原来快速排序的方法?因为我的记忆中好像就是这样的,因此我根据记忆中的快速排序在进行改变,然而,却无法真正的写出双冒泡 ...

  8. oracle case when及decode的用法

    case ... when 语句 1) CASE column_name WHEN value1 THEN resutl1,... [ ELSE result ] END select name , ...

  9. html5学习之旅第一篇

    什么是 HTML5? HTML5 是下一代 HTML 标准. HTML , HTML 4.01的上一个版本诞生于 1999 年.自从那以后,Web 世界已经经历了巨变. HTML5 仍处于完善之中.然 ...

  10. cordova+vue 项目打包成Android(apk)应用

    现在使用vue开发的项目越来越多,使用vue开发的移动端打包就成了最大的问题.现在前端打包方案有好多种,但是综合来说,我比较喜欢用cordova来进行Android和ios的打包,配置完成之后,每次只 ...