MySQL副本集介绍
MySQL InnoDB ReplicaSet(也叫MySQL副本集,有些地方也翻译成MySQL复制集)是在 MySQL 8.0.19 版本(2020-01-13 Released)之后开始支持的,MySQL副本集中拥有一个primary节点,一个或多个secondary节点,它不像MySQL InnoDB Cluster一样提供故障自愈和多主模式,但是它提供手工的方法添加、移除和配置相关节点.说得直白一点,MySQL副本集其实就是将MySQL主从复制、MySQL Shell、MySQL Router技术融合起来的一种技术,它的优点是可以帮助用户快速、简单的部署和管理主从复制。但是它的不足与限制也非常多。这里仅仅学习、研究MySQL副本集,毕竟技术都是快速向前迭代发展的。说不定哪天就蜕变得非常惊艳了。
MySQL副本集的限制
下面是官方文档[1]中介绍的MySQL InnoDB ReplicaSet(MySQL副本集)的一些限制:
-
没有自动故障转移功能。在主节点不可用的情况下,需要使用 AdminAPI 手动触发故障转移,然后才能再次进行任何更改。但是,辅助实例仍可供读取数据。
No automatic failover. In events where the primary becomes unavailable, a failover needs to be triggered manually
using AdminAPI before any changes are possible again. However, secondary instances remain available for reads. -
无法防止由于意外停止或不可用而导致部分数据丢失:在意外停止时未完成的事务可能会丢失。
No protection from partial data loss due to an unexpected halt or unavailability: Transactions that are not complete
at the time of the unexpected halt could be lost. -
在意外退出或不可用后无法防止数据不一致。如果手动故障转移提升了一个辅助实例,而前一个主实例仍然可用(例如,由于网络分区),
则裂脑情况可能会导致数据不一致。No protection against inconsistencies after an unexpected exit or unavailability. If a manual failover promotes
a secondary instance while the former primary is still available, for example, due to a network partition,
the split-brain situation could introduce data inconsistencies. -
InnoDB ReplicaSet 不支持多主模式。允许写入所有成员的经典复制拓扑无法保证数据一致性。
InnoDB ReplicaSet does not support a multi-primary mode. Data consistency cannot be guaranteed with
classic replication topologies that allow writes to all members. -
读取横向扩展是有限的。InnoDB ReplicaSet 基于异步复制,因此无法像 Group Replication 那样调整流量控制。
Read scale-out is limited. InnoDB ReplicaSet is based on asynchronous replication, and therefore there
is no possible tuning of flow control as there is with Group Replication. -
所有从节点(次要成员)都从单一来源复制。对于某些特定的用例,这可能会影响单一来源,例如,大量的小更新。
All secondary members replicate from a single source. For some particular use-cases,
this could impact the single source, for example, numerous small updates. -
仅支持运行 MySQL 8.0 及更高版本的实例。
Only instances running MySQL version 8.0 and later are supported.
-
仅支持基于 GTID 的复制,二进制日志文件位置复制与 InnoDB ReplicaSet 不兼容。
Only GTID-based replication is supported, Binary log file position replication is incompatible with InnoDB ReplicaSet.
-
仅支持基于行的复制 (RBR),不支持基于语句的复制 (SBR)。
Only Row-Based Replication (RBR) is supported, Statement-Based Replication (SBR) is unsupported.
-
不支持复制过滤
Replication filters are not supported. -
任何实例上都不允许使用非托管复制通道
Unmanaged replication channels are not allowed on any instance. -
一个 ReplicaSet 最多由一个主实例组成。支持一个或多个辅助实例。尽管可以添加到 ReplicaSet 的辅助节点数量没有限制,但连接到 ReplicaSet 的每个 MySQL Router 都必须监视每个实例。因此,添加到 ReplicaSet 的实例越多,监控就越多
A ReplicaSet consists of a maximum of one primary instance. One or multiple secondaries are supported. Although there is no limit to the number of secondaries you can add to a ReplicaSet, each MySQL Router connected to a ReplicaSet has to monitor each instance. Therefore, the more instances added to a ReplicaSet, the more monitoring there is. -
ReplicaSet 必须由 MySQL Shell 管理。例如,复制帐户是由 MySQL Shell 创建和管理的。不支持在 MySQL Shell 之外对实例进行配置更改,例如直接使用 SQL 语句更改主实例。始终使用 MySQL Shell 来处理 InnoDB ReplicaSet。
The ReplicaSet must be managed by MySQL Shell. For example, the replication account is created and managed by MySQL Shell. Making configuration changes to the instance outside MySQL Shell, for example, using SQL statements directly to change the primary instance, is not supported. Always use MySQL Shell to work with InnoDB ReplicaSet.
MySQL副本集的部署
这里计划在下面两台服务器上安装MySQL InnoDB ReplicaSet(MySQL副本集),MySQL数据库版本为8.0.33,操作系统版本为RHEL 8.8
192.168.9.154 dbtest04
192.168.9.159 dbtest05
1:配置/etc/hosts
192.168.9.154 dbtest04
192.168.9.159 dbtest05
如上所示,在配置文件/etc/hosts中加入服务器的ip/hostname信息。注意需要在MySQL副本集所有primary节点和secondary节点上配置/etc/hosts,具体根据你MySQL副本集的实际情况配置。
注意事项:必须配置/etc/hosts,否则副本集在调用addInstance等函数时可能会遇到问题.因为互相默认使用hostname信息来连接的.
2:安装MySQL Shell
这里使用root用户安装MySQL Shell,最好每个节点都安装MySQL Shell,另外,MySQL Shell有许多安装方式,有些安装方式需要设置环境变量,而yum安装方式不用额外去设置环境变量。
# yum localinstall mysql-shell-8.0.35-1.el8.x86_64.rpm
3:安装MySQL单实例
这里略过MySQl单实例安装,因为实在是太简单了(个人一般使用mysql_auto_install.sh脚本安装),不过需要注意参数文件my.cnf中一些参数设置,有一些基本参数需要你设置一下。下面是一些常见得参数(部分参数)
server_id=xxx #使用实际的数字替换,建议使用IP地址的最后一段数字
gtid_mode=on
enforce_gtid_consistency=on
binlog_transaction_dependency_tracking=writeset
否则,你后续操作可能遇到下面类似这样的错误:
MySQL 192.168.9.154:3306 ssl JS > dba.configureReplicaSetInstance('rsadmin@192.168.9.154:3306', {clusterAdmin: "'repl'@'192.168.9.%'"});
Configuring local MySQL instance listening at port 3306 for use in an InnoDB ReplicaSet...
This instance reports its own address as dbtest04:3306
Clients and other cluster members will communicate with it through this address by default. If this is not correct, the report_host MySQL system variable should be changed.
Password for new account: ***********
Confirm password: ***********
applierWorkerThreads will be set to the default value of 4.
NOTE: Some configuration options need to be fixed:
+----------------------------------------+---------------+----------------+--------------------------------------------------+
| Variable | Current Value | Required Value | Note |
+----------------------------------------+---------------+----------------+--------------------------------------------------+
| binlog_transaction_dependency_tracking | COMMIT_ORDER | WRITESET | Update the server variable |
| enforce_gtid_consistency | OFF | ON | Update read-only variable and restart the server |
| gtid_mode | OFF | ON | Update read-only variable and restart the server |
+----------------------------------------+---------------+----------------+--------------------------------------------------+
Some variables need to be changed, but cannot be done dynamically on the server.
Do you want to perform the required configuration changes? [y/n]: n
Dba.configureReplicaSetInstance: Cancelled (RuntimeError)
4:创建rsadmin管理账号
在各个节点上创建账号rsadmin(MySQL Shell使用),因为一般而言,root账号的hostname限定为localhost(127.0.0.1),所以我们单独为MySQL Shell创建一个管理账号。
create user rsadmin@'192.168.9.%' identified by 'gYj#jfdy874=d2';
grant all on *.* to 'rsadmin'@'192.168.9.%' with grant option;
flush privileges;
上面为了方便,授予rsadmin相当大的权限,你也可以限制一下它的权限
GRANT SELECT, RELOAD, SHUTDOWN, PROCESS, FILE, EXECUTE, REPLICATION SLAVE,
REPLICATION CLIENT, CREATE USER ON *.* TO `rsadmin`@`192.168.9.%` WITH GRANT OPTION;
GRANT CLONE_ADMIN,CONNECTION_ADMIN,GROUP_REPLICATION_ADMIN
,PERSIST_RO_VARIABLES_ADMIN,REPLICATION_APPLIER,REPLICATION_SLAVE_ADMIN
,ROLE_ADMIN,SYSTEM_VARIABLES_ADMIN
ON *.* TO `rsadmin`@`192.168.9.%` WITH GRANT OPTION;
GRANT INSERT, UPDATE, DELETE ON `mysql`.* TO `rsadmin`@`192.168.9.%` WITH GRANT OPTION;
5:检查实例是否符合副本集的条件
在MySQL shell中检查各个实例是否符合创建 ReplicaSet 的条件,如果发现需要修改的地方,就会提示确认修改。
connect rsadmin@192.168.9.154:3306
dba.configureReplicaSetInstance('rsadmin@192.168.9.154:3306', {clusterAdmin: "'rsadmin'@'192.168.9.%'"});
dba.configureReplicaSetInstance('rsadmin@192.168.9.159:3306', {clusterAdmin: "'rsadmin'@'192.168.9.%'"});
如下所示:192.168.9.159的参数文件my.cnf没有修改参数,会有错误提示:
MySQL 192.168.9.154:3306 ssl JS > dba.configureReplicaSetInstance('rsadmin@192.168.9.154:3306', {clusterAdmin: "'rsadmin'@'192.168.9.%'"});
Configuring local MySQL instance listening at port 3306 for use in an InnoDB ReplicaSet...
This instance reports its own address as dbtest04:3306
Clients and other cluster members will communicate with it through this address by default. If this is not correct, the report_host MySQL system variable should be changed.
User 'rsadmin'@'192.168.9.%' already exists and will not be created.
applierWorkerThreads will be set to the default value of 4.
The instance 'dbtest04:3306' is valid to be used in an InnoDB ReplicaSet.
The instance 'dbtest04:3306' is already ready to be used in an InnoDB ReplicaSet.
Successfully enabled parallel appliers.
MySQL 192.168.9.154:3306 ssl JS > dba.configureReplicaSetInstance('rsadmin@192.168.9.159:3306', {clusterAdmin: "'rsadmin'@'192.168.9.%'"});
Please provide the password for 'rsadmin@192.168.9.159:3306': **************
Save password for 'rsadmin@192.168.9.159:3306'? [Y]es/[N]o/Ne[v]er (default No): y
Configuring MySQL instance at dbtest05:3306 for use in an InnoDB ReplicaSet...
This instance reports its own address as dbtest05:3306
Clients and other cluster members will communicate with it through this address by default. If this is not correct, the report_host MySQL system variable should be changed.
User 'rsadmin'@'192.168.9.%' already exists and will not be created.
applierWorkerThreads will be set to the default value of 4.
NOTE: Some configuration options need to be fixed:
+----------------------------------------+---------------+----------------+--------------------------------------------------+
| Variable | Current Value | Required Value | Note |
+----------------------------------------+---------------+----------------+--------------------------------------------------+
| binlog_transaction_dependency_tracking | COMMIT_ORDER | WRITESET | Update the server variable |
| enforce_gtid_consistency | OFF | ON | Update read-only variable and restart the server |
| gtid_mode | OFF | ON | Update read-only variable and restart the server |
+----------------------------------------+---------------+----------------+--------------------------------------------------+
Some variables need to be changed, but cannot be done dynamically on the server.
Do you want to perform the required configuration changes? [y/n]: y
Do you want to restart the instance after configuring it? [y/n]: y
Configuring instance...
The instance 'dbtest05:3306' was configured to be used in an InnoDB ReplicaSet.
Restarting MySQL...
NOTE: MySQL server at dbtest05:3306 was restarted.
MySQL 192.168.9.154:3306 ssl JS >
建议根据提示修改my.cnf文件后,重启MySQL服务,然后重新验证:
dba.configureReplicaSetInstance('rsadmin@192.168.9.159:3306', {clusterAdmin: "'rsadmin'@'192.168.9.%'"});
6:创建副本集kerry_repl
#创建名为kerry_repl的副本集,名字可以根据实际情况设置
var rs = dba.createReplicaSet("kerry_repl")
#添加replication信任(MySQL Shell 8.0.28 及之后的版本创建 InnoDB ReplicaSet 时,如果你有安全需要,可以通过下面函数设置)
rs.setOption('replicationAllowedHost', '192.168.9.0/24')
#检查副本集的状态
rs.status()
具体执行过程如下所示:
MySQL 192.168.9.154:3306 ssl JS > var rs = dba.createReplicaSet("kerry_repl")
A new replicaset with instance 'dbtest04:3306' will be created.
* Checking MySQL instance at dbtest04:3306
This instance reports its own address as dbtest04:3306
dbtest04:3306: Instance configuration is suitable.
* Checking connectivity and SSL configuration...
* Updating metadata...
ReplicaSet object successfully created for dbtest04:3306.
Use rs.addInstance() to add more asynchronously replicated instances to this replicaset and rs.status() to check its status.
MySQL 192.168.9.154:3306 ssl JS > rs.setOption('replicationAllowedHost', '192.168.9.0/24')
Internally managed replication users updated for ReplicaSet 'kerry_repl'
MySQL 192.168.9.154:3306 ssl JS > rs.status()
{
"replicaSet": {
"name": "kerry_repl",
"primary": "dbtest04:3306",
"status": "AVAILABLE",
"statusText": "All instances available.",
"topology": {
"dbtest04:3306": {
"address": "dbtest04:3306",
"instanceRole": "PRIMARY",
"mode": "R/W",
"status": "ONLINE"
}
},
"type": "ASYNC"
}
}
MySQL 192.168.9.154:3306 ssl JS >
7:添加实例
rs.addInstance('192.168.9.159:3306')
rs.status()
具体操作如下所示
MySQL 192.168.9.154:3306 ssl JS > rs.addInstance('192.168.9.159:3306')
Adding instance to the replicaset...
* Performing validation checks
This instance reports its own address as dbtest05:3306
dbtest05:3306: Instance configuration is suitable.
* Checking async replication topology...
* Checking connectivity and SSL configuration...
* Checking transaction state of the instance...
NOTE: The target instance 'dbtest05:3306' has not been pre-provisioned (GTID set is empty). The Shell is unable to decide whether replication can completely recover its state.
The safest and most convenient way to provision a new instance is through automatic clone provisioning, which will completely overwrite the state of 'dbtest05:3306' with a physical snapshot from an existing replicaset member. To use this method by default, set the 'recoveryMethod' option to 'clone'.
WARNING: It should be safe to rely on replication to incrementally recover the state of the new instance if you are sure all updates ever executed in the replicaset were done with GTIDs enabled, there are no purged transactions and the new instance contains the same GTID set as the replicaset or a subset of it. To use this method by default, set the 'recoveryMethod' option to 'incremental'.
Please select a recovery method [C]lone/[I]ncremental recovery/[A]bort (default Clone): c
* Updating topology
Waiting for clone process of the new member to complete. Press ^C to abort the operation.
* Waiting for clone to finish...
NOTE: dbtest05:3306 is being cloned from dbtest04:3306
** Stage DROP DATA: Completed
** Clone Transfer FILE COPY ============================================================ 0% Not Started PAGE COPY ============================================================ 0% Not Started REDO COPY ============================================================ 0% Not Started** Clone Transfer FILE COPY ============================================================ 0% In Progress PAGE COPY ============================================================ 0% Not Started REDO COPY ============================================================ 0% Not Started** Clone Transfer FILE COPY ############################################################ 100% Completed PAGE COPY ############################################################ 100% Completed REDO COPY ############################################################ 100% Completed
NOTE: dbtest05:3306 is shutting down...
* Waiting for server restart... ready
* dbtest05:3306 has restarted, waiting for clone to finish...
** Stage RESTART: Completed
* Clone process has finished: 73.65 MB transferred in about 1 second (~73.65 MB/s)
** Changing replication source of dbtest05:3306 to dbtest04:3306
** Waiting for new instance to synchronize with PRIMARY...
** Transactions replicated ============================================================ 0% ** Transactions replicated ###########################################################= 98% ** Transactions replicated ############################################################ 100%
The instance 'dbtest05:3306' was added to the replicaset and is replicating from dbtest04:3306.
* Waiting for instance 'dbtest05:3306' to synchronize the Metadata updates with the PRIMARY...
** Transactions replicated ============================================================ 0% ** Transactions replicated ##########################################################== 97% ** Transactions replicated ############################################################ 100%
MySQL 192.168.9.154:3306 ssl JS > rs.status()
{
"replicaSet": {
"name": "kerry_repl",
"primary": "dbtest04:3306",
"status": "AVAILABLE",
"statusText": "All instances available.",
"topology": {
"dbtest04:3306": {
"address": "dbtest04:3306",
"instanceRole": "PRIMARY",
"mode": "R/W",
"status": "ONLINE"
},
"dbtest05:3306": {
"address": "dbtest05:3306",
"instanceRole": "SECONDARY",
"mode": "R/O",
"replication": {
"applierStatus": "APPLIED_ALL",
"applierThreadState": "Waiting for an event from Coordinator",
"applierWorkerThreads": 4,
"receiverStatus": "ON",
"receiverThreadState": "Waiting for source to send event",
"replicationLag": null,
"replicationSsl": "TLS_AES_256_GCM_SHA384 TLSv1.3"
},
"status": "ONLINE"
}
},
"type": "ASYNC"
}
}
MySQL 192.168.9.154:3306 ssl JS >
MySQL Router安装
bootstrap模式支持failover,在--directory指定的路径下自动生成安装目录,配置文件里的默认端口为6446和6447,这里将MySQL Router安装在192.168.9.159上,如果资源充足的话,建议将MySQL Router安装在单独的一台服务器上。
$ cd /data/soft
$ tar xvf mysql-router-8.0.35-linux-glibc2.28-x86_64.tar.xz -C /opt/mysql
$ cd /opt/mysql/
$ ln -s mysql-router-8.0.35-linux-glibc2.28-x86_64/ router
配置环境变量
在mysql用户下编辑~/.bash_profile,加入下面配置信息
export PATH=$PATH:/opt/mysql/router/bin
执行下面命令使其生效
$ source ~/.bash_profile
#查看帮助信息
mysqlrouter --help
#创建mysqlrouter的数据目录
mkdir -p /data/mysqlrouter
$ mysqlrouter --bootstrap rsadmin@dbtest04:3306
--directory /data/mysqlrouter
--account rs_router
--conf-bind-address="192.168.9.159" --account-host="192.168.9.%"
--name='myrouter' --user mysql --force-password-validation
$ mysqlrouter --bootstrap mysqladmin@dbtest04:3306
> --directory /data/mysqlrouter
> --account rs_router
> --name='myrouter' --user mysql --force-password-validation
Please enter MySQL password for mysqladmin:
# Bootstrapping MySQL Router 8.0.35 (MySQL Community - GPL) instance at '/data/mysqlrouter'...
Please enter MySQL password for rs_router:
- Creating account(s) (only those that are needed, if any)
- Verifying account (using it to run SQL queries that would be run by Router)
- Storing account in keyring
- Adjusting permissions of generated files
- Creating configuration /data/mysqlrouter/mysqlrouter.conf
# MySQL Router 'myrouter' configured for the InnoDB ReplicaSet 'kerry_repl'
After this MySQL Router has been started with the generated configuration
$ mysqlrouter -c /data/mysqlrouter/mysqlrouter.conf
InnoDB ReplicaSet 'kerry_repl' can be reached by connecting to:
## MySQL Classic protocol
- Read/Write Connections: localhost:6446
- Read/Only Connections: localhost:6447
## MySQL X protocol
- Read/Write Connections: localhost:6448
- Read/Only Connections: localhost:6449
我们配置一下mysqlrouter的systemctl服务。使用root账号创建文件/usr/lib/systemd/system/mysqlrouter.service,然后配置如下所示:
[Unit]
Description=MySQL Router
After=syslog.target
After=network.target
[Service]
Type=simple
User=mysql
Group=mysql
PIDFile=/data/mysqlrouter/mysqlrouter.pid
ExecStart=/opt/mysql/router/bin/mysqlrouter -c /data/mysqlrouter/mysqlrouter.conf
Restart=on-failure
PrivateTmp=true
[Install]
WantedBy=multi-user.target
然后执行下面命令
# systemctl daemon-reload
# systemctl enable mysqlrouter.service
# systemctl status mysqlrouter.service
# systemctl start mysqlrouter.service
到这边MySQL副本集就已经搭建成功,然后就是简单的测试。这里就不作展开介绍了。
总结
MySQL副本集的安装是非常简单的,但是从官方文档,我们可以看到MySQL副本集的限制与不足也是非常多。它跟MySQL主从复制的差别不是特别大。其最大的特点就是
使用MySQL Shell搭建非常简单方便,MySQL Router可以检测到MySQL副本集的节点变化。目前来说,生产环境中部署使用的价值和意义不是非常大。
参考资料
[1]
1: https://dev.mysql.com/doc/mysql-shell/8.0/en/mysql-innodb-replicaset.html