介绍
MySQL复制可靠地将数据和操作从一个数据库镜像到另一个数据库。 常规复制涉及主服务器,配置为接受数据库写入操作,辅助服务器将主服务器日志中的操作复制并应用到自己的数据集。 这些辅助服务器可用于读取,但通常无法执行数据写入。
组复制是实现更灵活,容错的复制机制的一种方法。 这个过程涉及到建立一个服务器池,每个服务器都涉及确保数据被正确复制。 如果主服务器遇到问题,成员选举可以从组中选择一个新的主服务器。 这允许剩余的节点继续运行,即使面对问题。 会员协商,故障检测和消息传递是通过实现Paxos协调算法来提供的 。
在本教程中,我们将使用一组三个Ubuntu 16.04服务器来设置MySQL组复制。 该配置将介绍如何操作单个主要或多个主复制组。
先决条件
要遵循,您将需要一组三个Ubuntu 16.04服务器。 在每个这些服务器上,您将需要设置具有sudo
权限的非root用户并配置基本的防火墙。 我们将使用Ubuntu 16.04的初始服务器设置指南来满足这些要求,并使每个服务器处于就绪状态。
Ubuntu的默认存储库中的MySQL版本不包括我们需要的组复制插件。 幸运的是,MySQL项目为包含此组件的最新MySQL版本维护了自己的存储库。 按照我们的指导, 在Ubuntu 16.04上安装最新的MySQL,以便在每个服务器上安装一组可复制的MySQL版本。
生成一个UUID来识别MySQL组
在打开MySQL配置文件以配置组复制设置之前,我们需要生成一个UUID,我们可以使用它来标识我们将要创建的MySQL组。
在mysqlmember1上 ,使用uuidgen
命令为该组生成一个有效的UUID:
uuidgen
Output959cf631-538c-415d-8164-ca00181be227
复制您收到的值。 在为我们的服务器池配置组名时,我们将不得不参考这一点。
在MySQL配置文件中设置组复制
现在我们准备修改MySQL的配置文件了。 在每个MySQL服务器上打开主MySQL配置文件:
sudo nano /etc/mysql/my.cnf
默认情况下,此文件仅用于从子目录中提取其他文件。 我们必须在我们自己的配置下添加!includedir
这允许我们轻松地覆盖所包含的文件中的任何设置。
要开始,通过添加一个[mysqld]
头来为MySQL服务器组件打开一个部分。 在这之下,我们将粘贴我们需要的组复制设置。 松散前缀允许MySQL优雅地处理无法正常识别的选项,而不会失败。 我们需要稍后填写并定制许多这些设置:
. . .
!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/
[mysqld]
# General replication settings
gtid_mode = ON
enforce_gtid_consistency = ON
master_info_repository = TABLE
relay_log_info_repository = TABLE
binlog_checksum = NONE
log_slave_updates = ON
log_bin = binlog
binlog_format = ROW
transaction_write_set_extraction = XXHASH64
loose-group_replication_bootstrap_group = OFF
loose-group_replication_start_on_boot = OFF
loose-group_replication_ssl_mode = REQUIRED
loose-group_replication_recovery_use_ssl = 1
# Shared replication group configuration
loose-group_replication_group_name = ""
loose-group_replication_ip_whitelist = ""
loose-group_replication_group_seeds = ""
# Single or Multi-primary mode? Uncomment these two lines
# for multi-primary mode, where any host can accept writes
#loose-group_replication_single_primary_mode = OFF
#loose-group_replication_enforce_update_everywhere_checks = ON
# Host specific replication configuration
server_id =
bind-address = ""
report_host = ""
loose-group_replication_local_address = ""
我们将上面的配置分为四个部分。 现在我们来看看吧。
锅炉组复制设置
第一部分包含无需修改的组复制所需的常规设置:
. . .
# General replication settings
gtid_mode = ON
enforce_gtid_consistency = ON
master_info_repository = TABLE
relay_log_info_repository = TABLE
binlog_checksum = NONE
log_slave_updates = ON
log_bin = binlog
binlog_format = ROW
transaction_write_set_extraction = XXHASH64
loose-group_replication_bootstrap_group = OFF
loose-group_replication_start_on_boot = OFF
loose-group_replication_ssl_mode = REQUIRED
loose-group_replication_recovery_use_ssl = 1
. . .
这些设置打开全局事务ID,配置组复制所需的二进制日志记录,并为组配置SSL。 该配置还设置了一些有助于恢复和引导的其他项目。 您不需要修改本节中的任何内容,因此您可以在粘贴后继续操作。
共享组复制设置
第二部分设置组的共享设置。 我们必须定制一次,然后在每个节点上使用相同的设置。 这包括该组的UUID,可接受成员的白名单,以及联系人以获取初始数据的种子成员。
将loose-group_replication_group_name
设置为您以前使用uuidgen
命令生成的uuidgen
。 将您复制的UUID粘贴为此变量的值。
接下来,将loose-group_replication_ip_whitelist
设置为所有MySQL服务器IP地址的列表,用逗号分隔。 loose-group_replication_group_seeds
设置应与白名单几乎相同,但应将我们将使用的组复制端口附加到每个成员的末尾。 对于本指南,我们将使用推荐的33061端口进行组复制:
. . .
# Shared replication group configuration
loose-group_replication_group_name = "959cf631-538c-415d-8164-ca00181be227"
loose-group_replication_ip_whitelist = "203.0.113.1,203.0.113.2,203.0.113.3"
loose-group_replication_group_seeds = ""203.0.113.1:33061,203.0.113.2:33061,203.0.113.3:33061"
. . .
每个MySQL服务器上的这个部分应该是一样的,因此请务必仔细复制。
选择单个主要或多个主要
接下来,您需要决定是配置单主还是多主组。 在官方MySQL文档的某些部分,这种区别也被称为“单主”与“多主”复制。 在单个主要配置中,MySQL指定一个主服务器(几乎总是第一个组成员)来处理写入操作。 多主组允许写入任何组成员。
如果要配置多主组,请取消注释loose-group_replication_single_primary_mode
和loose-group_replication_enforce_update_everywhere_checks
指令。 这将设置一个多主组。 对于单个主组,只需留下两行注释:
. . .
# Single or Multi-primary mode? Uncomment these two lines
# for multi-primary mode, where any host can accept writes
#loose-group_replication_single_primary_mode = OFF
#loose-group_replication_enforce_update_everywhere_checks = ON
. . .
这些设置必须在每个MySQL服务器上相同。
您可以稍后更改此设置,但不能重新启动MySQL组。 要转换到新配置,您将不得不停止组中的每个MySQL实例,使用新设置启动每个成员,然后重新引导组复制。 这不会影响任何数据,但需要一小段停机时间。
主机特定配置设置
第四部分包含在每个服务器上不同的设置,包括:
- 服务器ID
- 要绑定的地址
- 向其他成员报告的地址
- 本地复制地址和监听端口
server_id
指令必须设置为唯一的编号。 对于第一个成员,只需将其设置为“1”,并增加每个附加主机上的数字。 将bind-address
和report_host
设置为当前服务器的IP地址,以便MySQL实例监听外部连接并将其地址正确地报告给其他主机。 还应将loose-group_replication_local_address
设置为当前服务器的IP地址,并使用组复制端口(33061)附加到IP地址:
. . .
# Host specific replication configuration
server_id = 1
bind-address = "203.0.113.1"
report_host = "203.0.113.1"
loose-group_replication_local_address = "203.0.113.1:33061"
在每个MySQL服务器上完成此过程。
完成后,请仔细检查每个主机上的共享复制设置是否相同,并为每个主机定制主机特定的设置。 完成后,保存并关闭每台主机上的文件。
重新启动MySQL并启用远程访问
我们的MySQL配置文件现在包含引导MySQL组复制所需的指令。 要将新设置应用于MySQL实例,请使用以下命令在每个服务器上重新启动服务:
sudo systemctl restart mysql
在MySQL配置文件中,我们将服务配置为监听默认端口3306上的外部连接。我们还将33061定义为成员应用于复制协调的端口。
我们需要在我们的防火墙中打开这两个端口的访问,我们可以通过键入以下内容:
sudo ufw allow 33061
sudo ufw allow 3306
通过访问MySQL端口,我们可以创建复制用户并启用组复制插件。
配置复制用户和启用组复制插件
在每个MySQL服务器上 ,使用管理用户登录您的MySQL实例以启动交互式会话:
mysql -u root -p
系统将提示您输入MySQL管理密码。 之后,你将被丢入MySQL会话。 我们需要做的第一件事是创建复制用户。
每个服务器都需要复制用户来建立组复制。 因为每个服务器都有自己的复制用户,所以我们需要在创建过程中关闭二进制日志记录。 否则,一旦复制开始,组将尝试将复制用户从主服务器传播到其他服务器,从而与复制用户建立冲突。
我们将要求复制用户使用SSL,在服务器上授予它们复制权限,然后刷新权限以实现更改。 之后,我们将重新启用二进制日志记录以恢复正常操作。 确保在创建复制用户时使用安全密码:
SET SQL_LOG_BIN=0;
CREATE USER 'repl'@'%' IDENTIFIED BY 'password' REQUIRE SSL;
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
SET SQL_LOG_BIN=1;
接下来,我们需要将group_replication_recovery
通道设置为使用我们的新复制用户和关联的密码。 然后,每个服务器将使用这些凭据来验证组。
CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='password' FOR CHANNEL 'group_replication_recovery';
使用复制用户,我们可以启用组复制插件来准备初始化组。 由于我们使用最新版本的MySQL,因此我们可以通过键入以下内容来启用该插件:
INSTALL PLUGIN group_replication SONAME 'group_replication.so';
键入以下内容,验证插件是否处于活动
SHOW PLUGINS;
Output+----------------------------+----------+--------------------+----------------------+---------+
| Name | Status | Type | Library | License |
+----------------------------+----------+--------------------+----------------------+---------+
| | | | | |
| . . . | . . . | . . . | . . . | . . . |
| | | | | |
| group_replication | ACTIVE | GROUP REPLICATION | group_replication.so | GPL |
+----------------------------+----------+--------------------+----------------------+---------+
45 rows in set (0.00 sec)
group_replication
行确认该插件已加载并且当前处于活动状态。
启动组复制
现在,每个MySQL服务器配置了复制用户并启用了组复制插件,我们可以开始组织我们的组。
自举第一节点
要启动组,请在组中的单个成员上完成以下步骤。
组成员依赖现有成员在最初加入组时发送复制数据,最新成员资格列表和其他信息。 因此,我们需要使用稍微不同的过程来启动初始组成员,以便它知道不要在其种子列表中期望来自其他成员的信息。
如果设置, group_replication_bootstrap_group
变量告诉成员它不应该期望从对等体接收信息,而应该建立一个新的组并选择自己的主要成员。 由于唯一适合的情况是当没有现有的组成员时,我们将在引导组后立即关闭此功能:
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;
该组应该以此服务器作为唯一成员启动。 我们可以通过检查performance_schema
数据库中的replication_group_members
表中的条目进行验证:
SELECT * FROM performance_schema.replication_group_members;
您应该看到一行代表当前主机:
Output+---------------------------+--------------------------------------+--------------+-------------+--------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| group_replication_applier | 13324ab7-1b01-11e7-9dd1-22b78adaa992 | 203.0.113.1 | 3306 | ONLINE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
1 row in set (0.00 sec)
MEMBER_STATE
的ONLINE
值表示该节点在组内完全可操作。
接下来,创建一个测试数据库和表来测试我们的复制:
CREATE DATABASE playground;
CREATE TABLE playground.equipment ( id INT NOT NULL AUTO_INCREMENT, type VARCHAR(50), quant INT, color VARCHAR(25), PRIMARY KEY(id));
INSERT INTO playground.equipment (type, quant, color) VALUES ("slide", 2, "blue");
查看内容以确保输入正确:
SELECT * FROM playground.equipment;
Output+----+-------+-------+-------+
| id | type | quant | color |
+----+-------+-------+-------+
| 1 | slide | 2 | blue |
+----+-------+-------+-------+
1 row in set (0.00 sec)
我们现在已经验证了该服务器是该组的成员,并且具有写入功能。 现在其他服务器可以加入该组。
启动剩余节点
接下来,在第二个服务器上 ,启动组复制。 既然我们已经有一个活跃的成员,我们不需要引导组,可以加入它:
START GROUP_REPLICATION;
在第三台服务器上 ,以同样的方式启动组复制:
START GROUP_REPLICATION;
再次查看会员列表。 您现在应该看到三台服务器:
SELECT * FROM performance_schema.replication_group_members;
Output+---------------------------+--------------------------------------+--------------+-------------+--------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| group_replication_applier | 13324ab7-1b01-11e7-9dd1-22b78adaa992 | 203.0.113.1 | 3306 | ONLINE |
| group_replication_applier | 1ae4b211-1b01-11e7-9d89-ceb93e1d5494 | 203.0.113.2 | 3306 | ONLINE |
| group_replication_applier | 157b597a-1b01-11e7-9d83-566a6de6dfef | 203.0.113.3 | 3306 | ONLINE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
3 rows in set (0.01 sec)
所有成员的MEMBER_STATE
值应为ONLINE
。 对于一个新的组,如果任何节点被列为RECOVERING
超过一秒钟或两秒,通常会指示发生错误或某些配置错误。 检查/var/log/mysql/error.log
上的日志,以获取有关出错的更多信息。
检查测试数据库信息是否已复制到新成员上:
SELECT * FROM playground.equipment;
Output+----+-------+-------+-------+
| id | type | quant | color |
+----+-------+-------+-------+
| 1 | slide | 2 | blue |
+----+-------+-------+-------+
1 row in set (0.01 sec)
如果新成员上的数据可用,则表示组复制正常工作。
测试新集团成员的写作能力
接下来,我们可以尝试从我们的新成员写入数据库。 是否成功是您是否选择配置单个主要或多主要组的功能。
在单个主要环境中进行测试
在一个主要组中,您应该期望从非主服务器执行的任何写入操作由于一致性原因而被拒绝。 您可以随时通过以下查询来发现当前主要:
SHOW STATUS LIKE '%primary%';
Output+----------------------------------+--------------------------------------+
| Variable_name | Value |
+----------------------------------+--------------------------------------+
| group_replication_primary_member | 13324ab7-1b01-11e7-9dd1-22b78adaa992 |
+----------------------------------+--------------------------------------+
1 row in set (0.01 sec)
查询的值将是一个MEMBER_ID
,您可以通过像以前一样查询组成员列表来匹配主机:
SELECT * FROM performance_schema.replication_group_members;
Output+---------------------------+--------------------------------------+--------------+-------------+--------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| group_replication_applier | 13324ab7-1b01-11e7-9dd1-22b78adaa992 | 203.0.113.1 | 3306 | ONLINE |
| group_replication_applier | 1ae4b211-1b01-11e7-9d89-ceb93e1d5494 | 203.0.113.2 | 3306 | ONLINE |
| group_replication_applier | 157b597a-1b01-11e7-9d83-566a6de6dfef | 203.0.113.3 | 3306 | ONLINE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
3 rows in set (0.01 sec)
在这个例子中,我们可以看到, 203.0.113.1
的主机203.0.113.1
是主服务器。 如果我们尝试从另一个成员写入数据库,我们应该期望操作失败:
INSERT INTO playground.equipment (type, quant, color) VALUES ("swing", 10, "yellow");
OutputERROR 1290 (HY000): The MySQL server is running with the --super-read-only option so it cannot execute this statement
这是预期的,因为该组目前配置有单个可写入的主要。 如果主服务器存在问题并离开组,则组将自动选择新成员作为主要成员并接受写入。
多主要环境中的测试写入
对于已经以多主方向配置的组,任何成员都应该能够向数据库提交写入。
您可以通过再次检查group_replication_primary_member
变量的值来仔细检查您的组是否在多主模式下运行:
SHOW STATUS LIKE '%primary%';
Output+----------------------------------+-------+
| Variable_name | Value |
+----------------------------------+-------+
| group_replication_primary_member | |
+----------------------------------+-------+
1 row in set (0.02 sec)
如果变量为空,这意味着没有指定的主要主机,任何成员都应该能够接受写入。
通过键入以下内容在您的第二个服务器上测试
INSERT INTO playground.equipment (type, quant, color) VALUES ("swing", 10, "yellow");
OutputQuery OK, 1 row affected (0.00 sec)
第二个服务器承诺写操作没有任何错误。
在第三台服务器上查询以查看添加的新项目:
SELECT * FROM playground.equipment;
Output+----+-------+-------+--------+
| id | type | quant | color |
+----+-------+-------+--------+
| 1 | slide | 2 | blue |
| 2 | swing | 10 | yellow |
+----+-------+-------+--------+
2 rows in set (0.00 sec)
这确认了第二个服务器的写入被成功复制。
现在,通过键入以下内容在第三台服务器上测试写入功能:
INSERT INTO playground.equipment (type, quant, color) VALUES ("seesaw", 3, "green");
OutputQuery OK, 1 row affected (0.02 sec)
回到第一个服务器上 ,测试以确保来自两个新成员的写操作都被复制回来:
SELECT * FROM playground.equipment;
Output+----+--------+-------+--------+
| id | type | quant | color |
+----+--------+-------+--------+
| 1 | slide | 2 | blue |
| 2 | swing | 10 | yellow |
| 3 | seesaw | 3 | green |
+----+--------+-------+--------+
3 rows in set (0.01 sec)
这确认复制在每个方向上都有效,并且每个成员都能够执行写入操作。
带动小组备份
一旦组被引导,只要有足够的成员选择主服务器,个别成员可以加入并离开而不影响可用性。 但是,如果进行某些配置更改(例如单个和多个主要环境之间的切换),或该组的所有成员都离开,则可能需要重新引导组。 你这样做的方式与你最初一样。
在您的第一个服务器上 ,设置group_replciation_bootstrap_group
变量,然后开始初始化组:
SET GLOBAL GROUP_REPLICATION_BOOTSTRAP_GROUP=ON;
START GROUP_REPLICATION;
SET GLOBAL GROUP_REPLICATION_BOOTSTRAP_GROUP=OFF;
一旦第一个成员启动了该组,其他成员可以加入:
START GROUP_REPLICATION;
按照此过程为其他成员:
START GROUP_REPLICATION;
该小组现在应该在线,所有会员可用:
SELECT * FROM performance_schema.replication_group_members;
Output+---------------------------+--------------------------------------+--------------+-------------+--------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| group_replication_applier | 13324ab7-1b01-11e7-9dd1-22b78adaa992 | 203.0.113.1 | 3306 | ONLINE |
| group_replication_applier | 1ae4b211-1b01-11e7-9d89-ceb93e1d5494 | 203.0.113.2 | 3306 | ONLINE |
| group_replication_applier | 157b597a-1b01-11e7-9d83-566a6de6dfef | 203.0.113.3 | 3306 | ONLINE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
3 rows in set (0.01 sec)
该过程可用于在必要时再次启动组。
MySQL开始时自动加入组
使用当前设置,如果成员服务器重新启动,它将不会在启动时自动重新加入组。 如果要成员自动重新加入组,则可以稍微修改配置文件。
当您希望成员在启动时自动加入,我们将概述的设置很有帮助。 但是,有一些你应该注意的事情:
首先,此设置仅影响MySQL实例本身的启动。 如果由于超时问题而将组员从组中删除,但MySQL实例保持在线状态,则该成员将不会自动重新加入。
其次,在首次引导组时启用此设置可能是有害的。 当没有现有组加入时,MySQL进程需要很长时间才能启动,因为它将尝试联系其他不存在的成员进行初始化。 只有经过长时间的超时才能放弃并正常启动。 之后,您必须使用上述步骤来引导组。
考虑到上述注意事项,如果您希望在MySQL启动时自动配置节点加入组,请打开主MySQL配置文件:
sudo nano /etc/mysql/my.cnf
在里面,找到loose-group_replication_start_on_boot
变量,并将其设置为“ON”:
[mysqld]
. . .
loose-group_replication_start_on_boot = ON
. . .
完成后保存并关闭文件。 该成员应该在下次启动MySQL实例时自动尝试加入该组。
结论
在本教程中,我们介绍了如何在三个Ubuntu 16.04服务器之间配置MySQL组复制。 对于单个主要设置,成员将在必要时自动选择具有写入能力的主要。 对于多主组,任何成员都可以执行写入和更新。
组复制提供了灵活的复制拓扑,允许成员加入或离开,同时提供有关数据一致性和消息排序的保证。 MySQL组复制可能会更复杂一些,但它提供了传统复制中不可能的功能。