InnoDB 集群入门指南(四)
九、InnoDB 集群管理
正如您已经发现的,设置 InnoDB Cluster 并不困难,除了学习如何使用 MySQL Shell 和 AdminAPI 中的一些类和方法之外,配置 InnoDB Cluster 的步骤也同样简单。然而,我们从经验中知道,设置和管理在复杂性上并不总是一致的。
事实上,在 InnoDB Cluster 之前,管理和维护 MySQL 复制需要学习一系列特定的任务和技术,以保持一切正常运行。幸运的是,对于 InnoDB 集群来说,情况并非如此。在 InnoDB 集群的发展过程中,管理任务是一个关键的目标领域,其目标是以更低的复杂性更容易使用。正如您将看到的,除了一些罕见的例外情况,AdminAPI 已经达到了这个目标。
在本章中,您将了解管理 InnoDB 集群的常见任务。当您在自己的环境中探索 InnoDB 集群时,可以将本章作为参考。正如您将看到的,我们已经通过第 5 、 7 和 8 章中的沙箱和实时服务器部署示例浏览了一些任务。为了完整起见,本章使用简短的示例总结了这些任务。任务我们还没有更详细地讨论功能示例。
您还将通过研究保护集群的管理任务,了解如何使您的 InnoDB 集群更加安全。
概观
管理 InnoDB 集群需要通过 MySQL Shell 使用 AdminAPI。尽管可以直接使用 InnoDB 集群和组复制,但不建议这样做。InnoDB 集群的管理应该始终通过 AdminAPI 进行。
警告
始终通过 MySQL Shell 使用 AdminAPI 来管理 InnoDB 集群。不建议直接使用组复制或 InnoDB 集群元数据。
你在第 5 章中看到了 AdminAPI 的概述。AdminAPI 中有两个类:dba类,用于建立集群并使用实例为集群做准备,以及cluster类,用于管理集群,从检查状态到解决实例问题。
让我们先简要回顾一下这些工具,然后看看常见的任务。然后我们将讨论故障排除任务,这些任务并不经常使用,只是在出现问题时才使用。
工具,命令
使用 InnoDB Cluster 的首选工具是 MySQL Shell,这是本书的重点。这是唯一推荐使用集群的工具。但是,应该注意的是,您可以用 Java 或 Python 编写自己的特殊脚本来处理集群。这两种方法都使用 AdminAPI 来处理集群。同样,您不应该使用任何不使用 AdminAPI 的工具或实用程序。如果您是使用组复制的专家,您可能会尝试通过操作组复制参数和选项来使用群集,但不建议这样做。您应该迁移到使用 MySQL Shell。
在本节中,我们将研究使用 InnoDB 集群的常见管理任务。我们将任务分为以下几类:
-
常规:状态和相关检查的基本管理任务
-
实例:使用实例的任务
因为本章旨在用作参考,所以示例有些简短,仅显示了可用于完成任务的基本命令。但是,对于那些更频繁使用的任务,会显示详细的示例和输出。此外,示例省略了 MySQL Shell 的连接步骤。有关使用 MySQL Shell 连接到服务器的更多信息,请参见第 4 章。
一般
此类别中的任务是那些更频繁执行的任务,包括获取集群、检查状态和描述集群配置。
获取集群
当使用 MySQL Shell 管理 InnoDB 集群时,我们必须首先请求一个cluster类的实例。回想一下,在本书中你已经多次看到这种情况。为了检索cluster实例,我们使用了dba.get_cluster()方法。下面显示了一个检索群集的示例。请记住,我们必须先连接到集群中的一个服务器,然后才能检索它。
\connect root@cluster-rpi1:3306
cluster = dba.get_cluster()
您也可以将集群的名称作为参数传递,但这是用于额外的错误检查(如果服务器不属于指定的集群,则会引发错误)。当您的基础架构中有多个集群时,这很有帮助。
检查集群状态
与上一个任务一样,您之前已经看到了如何检索集群状态报告。回想一下,我们使用的是cluster.status()方法。我们必须连接到集群中的一个服务器,检索它,然后使用状态方法:
\connect root@cluster-rpi1:3306
cluster = dba.get_cluster()
cluster.status()
您还可以将检索集群和显示状态的方法链接起来:
dba.get_cluster().status()
如果您想在批处理模式下使用 MySQL Shell 来快速检索状态,这可能会很方便。
描述集群
您还可以获得关于集群的信息,比如集群中机器的主机名以及每个 MySQL 实例使用的端口。我们使用清单 9-1 中展示的cluster.describe()方法。
MySQL cluster-rpi2:3306 ssl Py > cluster = dba.get_cluster('RPI_Cluster')
MySQL cluster-rpi2:3306 ssl Py > cluster.describe()
{
"clusterName": "RPI_Cluster",
"defaultReplicaSet": {
"name": "default",
"topology": [
{
"address": "cluster-rpi1:3306",
"label": "cluster-rpi1:3306",
"role": "HA"
},
{
"address": "cluster-rpi2:3306",
"label": "cluster-rpi2:3306",
"role": "HA"
},
{
"address": "cluster-rpi3:3306",
"label": "cluster-rpi3:3306",
"role": "HA"
},
{
"address": "cluster-rpi4:3306",
"label": "cluster-rpi4:3306",
"role": "HA"
},
]
}
}
Listing 9-1Describing the Cluster
现在,让我们看看使用实例的任务。
例子
此类别中的任务旨在用于单个实例,以将实例加入集群,将实例重新加入集群,检查实例是否适合与 InnoDB 集群一起使用,以及类似的任务。
检查实例是否适合与 InnoDB 集群一起使用
您已经看到了可以用来准备与 InnoDB Cluster 一起使用的实例的两种方法。第一个是dba.configure_local_instance(),用于准备在集群中使用的本地机器。第二个是dba.check_instance_configuration(),可以用来测试服务器的设置是否正确。与第一种方法不同,检查实例配置方法可以远程运行。以下是每个命令的示例:
\connect root@localhost:3306
dba.configure_local_instance()
dba.check_instance_configuration('root@cluster-rpi2:43306')
检查实例的集群状态
您还可以通过使用dba.check_instance_state()方法来检查实例的当前或最后已知状态。此方法将服务器连接信息作为参数,并返回四种状态之一:
-
OK new:实例没有执行过任何 GTID 事务,所以不能与集群执行的 GTID 冲突。 -
OK recoverable:该实例已经执行了与集群种子实例的已执行 gtid 不冲突的 gtid。 -
ERROR diverged:该实例已执行的 gtid 与集群种子实例已执行的 gtid 不同。 -
ERROR lost_transactions:实例的已执行 gtid 多于集群种子实例的已执行 gtid。
清单 9-2 展示了一个运行这个命令的例子。
MySQL Py > \connect root@localhost:3306
MySQL Py > dba.configure_local_instance('localhost:3306')
Please provide the password for 'root@localhost:3306': ****
Configuring local MySQL instance listening at port 3306 for use in an InnoDB cluster...
This instance reports its own address as cluster-rpi3
WARNING: User 'root' can only connect from localhost.
If you need to manage this instance while connected from other hosts, new account(s) with the proper source address specification must be created.
1) Create remotely usable account for 'root' with same grants and password
2) Create a new admin account for InnoDB cluster with minimal required grants
3) Ignore and continue
4) Cancel
Please select an option [1]: 1
Please provide a source address filter for the account (e.g: 192.168.% or % etc) or leave empty and press Enter to cancel.
Account Host: %
Some configuration options need to be fixed:
+--------------------------+---------------+----------+-------------------+
| Variable | Current Value | Required | Note | Value
+--------------------------+---------------+----------------+-------------+
| binlog_checksum | CRC32 | NONE | 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 |
+--------------------------+---------------+----------+-------------------+
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
Cluster admin user 'root'@'%' created.
Configuring instance...
The instance 'localhost:3306' was configured for cluster usage.
Restarting MySQL...
MySQL server at localhost:3306 was restarted.
Listing 9-2Configuring the Local Instance
将实例加入集群
在本书中,您已经多次看到了如何将实例加入集群。回想一下,我们使用cluster.add_instance()方法,传入连接信息以连接到一个实例,从而加入当前集群。此处显示了将实例加入集群的命令示例;我们必须连接到群集中的服务器才能使用此方法:
\connect root@cluster-rpi1:3306
cluster = dba.get_cluster()
cluster.add_instance('root@cluster-rpi3:3306')
将实例重新加入集群
如果服务器与集群断开连接,我们可以使用cluster.rejoin_instance()方法将其重新加入集群。这将连接信息作为参数,并尝试将实例重新连接到群集。你在第 8 章中已经看到了这种方法。下面显示了一个将实例重新加入集群的示例。在操作之后检查集群的状态总是一个好主意。
\connect root@cluster-rpi1:3306
\connect root@cluster-rpi1:3306
cluster = dba.get_cluster()
cluster.rejoin_instance('root@cluster-rpi4:3306')
cluster.status()
从集群中删除一个实例
如果您需要在物理机器或服务器上运行的 MySQL 实例上执行维护,您应该首先将它从集群中删除。我们可以用cluster.remove_instance(方法做到这一点:
\connect root@cluster-rpi1:3306
cluster = dba.get_cluster()
cluster.remove_instance('root@cluster-rpi2:3306')
cluster.status()
在操作之后检查集群的状态总是一个好主意。如果 InnoDB 集群元数据完好无损,您可以使用cluster.rejoin_instance()方法将服务器添加回集群。如果您必须删除数据或重建服务器,您可能需要使用cluster.add_instance()方法。
创建实例的白名单
假设我们想要指定一个允许与集群一起使用的服务器列表(一个称为白名单 1 的许可列表),该列表限制只有列表中的机器才能连接到集群。我们可以通过向create_cluster()、add_instance()或rejoin_instance()方法传递一个特殊的参数来显式地创建白名单。例如,下面显示了如何创建一个群集来限制与特定子网(192.168.42.X)上的服务器的连接:
\connect root@cluster-rpi1:3306
cluster = dba.create_cluster('RPI_Cluster', {ipWhitelist: '192.168.42.0/24"})
自定义集群元数据
您可能还记得本书和本章中的各种例子,除了命名集群之外,我们通常不会向集群元数据添加任何定制。这是因为为组名、本地地址和种子实例等选项设置了默认值。然而,我们可以通过在设置和配置方法中覆盖它们来指定这些属性的值,包括dba.create_cluster()和cluster.add_instance()。
设置组名
要设置复制组的名称,请使用dba.create_cluster()方法的groupName选项。但是,您必须使用有效的 UUID。下面是一个例子:
dba.create_cluster('RPI_Cluster', {'groupName':'0cc3ebad-6759-11e8-966f-c49ded13bebf'})
您可以通过发出以下 SQL 语句来查看系统变量group_replication_group_name中的值:
\sql;
SHOW VARIABLES LIKE 'group_replication_group_name';
设置本地地址
本地地址是一个实例为来自其他实例的连接提供的地址。当调用dba.create_cluster()和cluster.add_instance()方法时,可以通过使用localAddress选项来设置本地地址。这将在实例上设置系统变量group_replication_local_address。
您必须使用字符串格式host:port提供一个值。请注意,该地址必须是群集中所有实例都可以访问的地址(由 DNS 或 hosts 文件条目支持)。此外,Oracle 在《在线参考手册》中指出,该值只能用于内部的实例对实例的通信,而不能用于外部访问集群。以下是如何使用该选项的示例:
dba.create_cluster('RPI_Cluster', {'localAddress':'192.168.42.241:3306'})
您可以通过发出以下 SQL 语句来查看系统变量group_replication_local_address中的值:
\sql;
SHOW VARIABLES LIKE 'group_replication_local_address';
设置组种子
组种子是当一个实例加入集群时用来连接的那些实例的列表。这允许实例获取正确的元数据以用于集群。当调用dba.create_cluster()和cluster.add_instance()方法时,可以通过使用groupSeeds选项来设置组种子。
使用格式host1:port1,host2:port2, <etc>将地址指定为逗号分隔的列表(字符串)。以下是如何使用该选项的示例:
dba.create_cluster('RPI_Cluster', {'groupSeeds':'192.168.42.241:3306,192.168.42.242:3306'})
您可以通过发出以下 SQL 语句来查看系统变量group_replication_group_seeds中的值:
\sql;
SHOW VARIABLES LIKE ' group_replication_group_seeds ';
现在,我们来看看故障排除任务。
InnoDB 集群故障排除
如果您正在努力组建集群并添加实例,那么您的机器或这些机器上的 MySQL 可能会发生一些奇怪的事情。例如,假设您无法添加一个实例,并且已经通过dba.check_instance_configuration()方法检查了该实例是否有效,但是集群仍然不接受该实例。在这种情况下,群集可能已经消失(元数据错误、损坏或丢失)。
故障排除类别中的任务是在出现问题或失败时您可能会遇到或需要的任务。这些不是你通常会执行的任务——相反,我们希望永远不需要这些任务!事实上,有些可能被认为是危险的,所以要谨慎使用。
从仲裁丢失中恢复
在不寻常的情况下,一个实例可能会失去其法定人数(在失败的情况下,参与投票方案以确定主要成员的能力)。我们可以使用cluster.force_quorum_using_partition_of()方法来重新建立法定人数。当实例的状态被设置为NO_QUORUM时,您就会知道这已经发生了。下面显示了 AdminAPI 的命令和输出示例:
cluster.force_quorum_using_partition_of ("localhost:3310")
Restoring replicaset 'default' from loss of quorum, by using the partition composed of [localhost:3310]
Please provide the password for 'root@localhost:3310': ******
Restoring the InnoDB cluster ...
The InnoDB cluster was successfully restored using the partition from the instance 'root@localhost:3310'.
警告
使用这种方法要非常小心。可以遇到裂脑的情况。为了避免这种情况,请确保副本集的所有其他成员都被删除或重新加入到被还原的组中。
从完全丢失中重新启动
如果您需要在完全中断或丢失后重新启动集群,比如集群中的所有服务器都已关闭(或类似事件),您可以使用dba.reboot_cluster_from_complete_outage()方法。只需确保所有集群服务器都在线,并且 MySQL 在所有节点上都已启动和运行。该任务可用于有意或出于必要而关闭的试验性或开发性集群。清单 9-3 展示了一个从完全丢失状态重启集群的例子。
MySQL Py > \connect root@cluster-rpi1:3306
Creating a session to 'root@cluster-rpi1:3306'
Enter password: ****
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 15
Server version: 8.0.11 MySQL Community Server (GPL)
No default schema selected; type \use <schema> to set one.
MySQL cluster-rpi1:3306 ssl Py > cluster = dba.reboot_cluster_from_complete_outage('RPI_Cluster')
Reconfiguring the cluster 'RPI_Cluster' from complete outage...
The instance 'cluster-rpi2:3306' was part of the cluster configuration.
Would you like to rejoin it to the cluster? [y/N]: y
The instance 'cluster-rpi4:3306' was part of the cluster configuration.
Would you like to rejoin it to the cluster? [y/N]: y
The instance 'cluster-rpi3:3306' was part of the cluster configuration.
Would you like to rejoin it to the cluster? [y/N]: y
The cluster was successfully rebooted.
MySQL cluster-rpi1:3306 ssl Py > cluster.status()
{
"clusterName": "RPI_Cluster",
"defaultReplicaSet": {
"name": "default",
"primary": "cluster-rpi1:3306",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"cluster-rpi1:3306": {
"address": "cluster-rpi1:3306",
"mode": "R/W",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"cluster-rpi2:3306": {
"address": "cluster-rpi2:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"cluster-rpi3:3306": {
"address": "cluster-rpi3:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"cluster-rpi4:3306": {
"address": "cluster-rpi4:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
}
}
},
"groupInformationSourceMember": "mysql://root@cluster-rpi1:3306"
}
Listing 9-3Rebooting the Cluster from Complete Loss
注意
您必须指定群集名称才能使用此方法。否则,您将得到一个错误。
重新扫描集群元数据
如果您(或某人)在不使用 AdminAPI 的情况下对集群进行了更改(比如手动从组复制中添加或删除一台机器),您可以使用cluster.rescan()方法修复 InnoDB 集群元数据。
此方法将尝试修复元数据,并尝试将实例添加到群集。系统将提示您批准找到的每台服务器。如果不允许服务器加入集群或遇到错误,将会报告该服务器,以便您可以在以后尝试添加实例。以下是运行此命令的示例:
MySQL cluster-rpi1:3306 ssl Py > cluster.rescan()
Rescanning the cluster...
Result of the rescanning operation:
{
"defaultReplicaSet": {
"name": "default",
"newlyDiscoveredInstances": [],
"unavailableInstances": []
}
}
在这里,我们看不到任何新的或不可用的实例。如果有的话,您会看到它们如下:
MySQL cluster-rpi1:3306 ssl Py > cluster.rescan()
Rescanning the cluster...
Result of the rescanning operation:
{
"defaultReplicaSet": {
"name": "default",
"newlyDiscoveredInstances": [],
"unavailableInstances": [
{
"host": "cluster-rpi4:3306",
"label": "cluster-rpi4:3306",
"member_id": "1fec2731-53d2-11e8-914c-b827ebcb9200"
}
]
}
}
请注意,实例cluster-rpi4:3306不再是高可用性设置的一部分。它或者脱机,或者已经离开高可用性组。您可以尝试使用cluster.rejoin_instance('cluster-rpi4:3306')命令再次将它添加到集群中,或者从集群配置中删除它。
Would you like to remove it from the cluster metadata? [Y/n]: n
对于这个问题,我们通常不希望在正常情况下删除实例,例如,当您需要将实例脱机进行修复时。所以要回复N。另一方面,如果应该删除实例,可以通过回答问题Y来实现。
以下是使用cluster.rejoin_instance()方法将实例重新加入集群的示例:
MySQL cluster-rpi1:3306 ssl Py > cluster.rejoin_instance('root@cluster-rpi4:3306')
Rejoining the instance to the InnoDB cluster. Depending on the original
problem that made the instance unavailable, the rejoin operation might not be successful and further manual steps will be needed to fix the underlying problem.
Please monitor the output of the rejoin operation and take necessary action if the instance cannot rejoin.
Please provide the password for 'root@cluster-rpi4:3306': ****
Rejoining instance to the cluster ...
The instance 'cluster-rpi4:3306' was successfully rejoined on the cluster.
现在,如果我们重新扫描群集,我们应该会看到不再有任何实例不可用或对群集来说是新的,如下所示:
MySQL cluster-rpi1:3306 ssl Py > cluster.rescan()
Rescanning the cluster...
Result of the rescanning operation:
{
"defaultReplicaSet": {
"name": "default",
"newlyDiscoveredInstances": [],
"unavailableInstances": []
}
}
集群拆卸
在极少数情况下,您需要从集群中完全删除所有实例并删除集群,您可以使用cluster.dissolve()方法。如果您将force选项设置为True,该方法将删除所有实例并解散集群。与类似的方法一样,这是一种作为最后手段或用于实验或开发的方法。下面是使用方法的一个示例:
MySQL cluster-rpi1:3306 ssl Py > cluster.dissolve({'force':True})
The cluster was successfully dissolved.
Replication was disabled but user data was left intact.
You can now rebuild your cluster.
如果您执行此任务是为了尝试修复错误或重建群集,则可以这样做,因为所有元数据和设置都已从实例中删除。清单 9-4 显示了如何重建第 7 章中的示例集群。
MySQL cluster-rpi1:3306 ssl Py > dba.create_cluster('RPI_Cluster')
A new InnoDB cluster will be created on instance 'root@cluster-rpi1:3306'.
The MySQL instance at 'cluster-rpi1:3306' currently has the super_read_only
system variable set to protect it from inadvertent updates from applications. You must first unset it to be able to perform any changes to this instance.
For more information see: https://dev.mysql.com/doc/refman/en/server-system-variables.html#sysvar_super_read_only.
Note: there are open sessions to 'cluster-rpi1:3306'.
You may want to kill these sessions to prevent them from performing unexpected updates:
1 open session(s) of 'root@192.168.42.240'.
Do you want to disable super_read_only and continue? [y/N]: y
Validating instance at cluster-rpi1:3306...
This instance reports its own address as cluster-rpi1
Instance configuration is suitable.
Creating InnoDB cluster 'RPI_Cluster' on 'root@cluster-rpi1:3306'...
Adding Seed Instance...
Cluster successfully created. Use Cluster.add_instance() to add MySQL instances. At least 3 instances are needed for the cluster to be able to withstand up to one server failure.
<Cluster:RPI_Cluster>
MySQL cluster-rpi1:3306 ssl Py > cluster = dba.get_cluster()
MySQL cluster-rpi1:3306 ssl Py > cluster.add_instance('root@cluster-rpi2:3306')
A new instance will be added to the InnoDB cluster. Depending on the amount of data on the cluster this might take from a few seconds to several hours.
Please provide the password for 'root@cluster-rpi2:3306': ****
Adding instance to the cluster ...
Validating instance at cluster-rpi2:3306...
This instance reports its own address as cluster-rpi2
Instance configuration is suitable.
The instance 'root@cluster-rpi2:3306' was successfully added to the cluster.
MySQL cluster-rpi1:3306 ssl Py > cluster.add_instance('root@cluster-rpi3:3306')
A new instance will be added to the InnoDB cluster. Depending on the amount of data on the cluster this might take from a few seconds to several hours.
Please provide the password for 'root@cluster-rpi3:3306': ****
Adding instance to the cluster ...
Validating instance at cluster-rpi3:3306...
This instance reports its own address as cluster-rpi3
Instance configuration is suitable.
The instance 'root@cluster-rpi3:3306' was successfully added to the cluster.
MySQL cluster-rpi1:3306 ssl Py > cluster.add_instance('root@cluster-rpi4:3306')
A new instance will be added to the InnoDB cluster. Depending on the amount of data on the cluster this might take from a few seconds to several hours.
Please provide the password for 'root@cluster-rpi4:3306': ****
Adding instance to the cluster ...
Validating instance at cluster-rpi4:3306...
This instance reports its own address as cluster-rpi4
Instance configuration is suitable.
The instance 'root@cluster-rpi4:3306' was successfully added to the cluster.
MySQL cluster-rpi1:3306 ssl Py > cluster.status()
{
"clusterName": "RPI_Cluster",
"defaultReplicaSet": {
"name": "default",
"primary": "cluster-rpi1:3306",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"cluster-rpi1:3306": {
"address": "cluster-rpi1:3306",
"mode": "R/W",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"cluster-rpi2:3306": {
"address": "cluster-rpi2:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"cluster-rpi3:3306": {
"address": "cluster-rpi3:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"cluster-rpi4:3306": {
"address": "cluster-rpi4:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
}
}
},
"groupInformationSourceMember": "mysql://root@cluster-rpi1:3306"
}
MySQL cluster-rpi1:3306 ssl Py > cluster.describe()
{
"clusterName": "RPI_Cluster",
"defaultReplicaSet": {
"name": "default",
"topology": [
{
"address": "cluster-rpi1:3306",
"label": "cluster-rpi1:3306",
"role": "HA"
},
{
"address": "cluster-rpi2:3306",
"label": "cluster-rpi2:3306",
"role": "HA"
},
{
"address": "cluster-rpi3:3306",
"label": "cluster-rpi3:3306",
"role": "HA"
},
{
"address": "cluster-rpi4:3306",
"label": "cluster-rpi4:3306",
"role": "HA"
}
]
}
}
Listing 9-4Reestablishing the Cluster after Dissolving
从元数据错误中恢复
如果您的服务器关机,比如说,您在一天结束时将它们全部关闭,那么当第二天重新开始工作时,您可能会遇到以下错误(或类似的错误)。这是因为 InnoDB 集群(以及任何高可用性系统)不是为关闭和重启而设计的。您可以关闭一些服务器进行维护,但是您应该始终保持至少三台服务器运行。尽管 InnoDB 集群将继续运行,但在至少三台服务器添加到集群之前,日志中会出现警告和潜在错误。因此,如果您只有三个实例,并且必须让其中一个脱机进行维护,那么您可以。但是,您应该考虑在维护任务期间向集群添加另一个实例,以确保您拥有最少数量的实例。
MySQL localhost:33060+ ssl Py > cluster = dba.get_cluster('RPI_Cluster')
Traceback (most recent call last):
File "<string>", line 1, in <module>
SystemError: RuntimeError: Dba.get_cluster: This function is not available through a session to a standalone instance (metadata exists, but GR is not active)
下面演示了如何从完全中断中恢复集群。具体来说,所有服务器都已重新启动(关闭电源并重新启动),您需要从最后一个已知良好的位置重新启动集群。我们将使用dba.reboot_cluster_from_complete_outage()方法重启集群。首先,登录到其中一个服务器,运行如下命令:
MySQL localhost:33060+ ssl Py > cluster = dba.reboot_cluster_from_complete_outage('RPI_Cluster')
Reconfiguring the cluster 'RPI_Cluster' from complete outage...
The instance '192.168.42.244:3306' was part of the cluster configuration.
Would you like to rejoin it to the cluster? [y/N]: y
The instance '192.168.42.243:3306' was part of the cluster configuration.
Would you like to rejoin it to the cluster? [y/N]: y
The instance '192.168.42.242:3306' was part of the cluster configuration.
Would you like to rejoin it to the cluster? [y/N]: y
The cluster was successfully rebooted.
如果成功,您将看到消息,指示集群已重新启动。
最后手段
如果在尝试恢复集群后,您仍然有问题,并希望从头开始,您可以通过在每台服务器上发出以下命令来实现。这是一个相当激进的步骤,将删除所有元数据并重新启动组复制。但是,您应该将此作为最后的手段,因为它会中断停机事件期间发生的任何事务的正常流程(不太可能,但有可能)。
dba.drop_metadata_schema()
RESET SLAVE
RESET MASTER
第一个命令删除 InnoDB 集群元数据,有效地将服务器从集群中删除。接下来的两个 SQL 命令重置服务器上的复制。回想一下,我们可以通过连接到机器的 MySQL Shell 运行这些命令,但是我们必须对第一个命令使用 Python 模式(\py),对最后两个命令使用 SQL 模式(\sql)。
警告
这是一项非常危险的任务,建议仅作为最后手段使用。
在所有服务器上输入这些命令后,您可以通过创建集群并添加实例来重新开始。同样,这是最后一招,不推荐用于任何生产系统。
现在,让我们看看管理 MySQL InnoDB 集群的一些最佳实践。
最佳实践
现在,您已经看到了修复集群可能需要的常见维护任务以及一些故障排除任务的列表,让我们讨论几个管理 InnoDB 集群的关键最佳实践。本节中列出的实践既不完整,也不是您应该为 InnoDB 集群考虑的唯一实践。相反,这里列出的实践旨在提醒您应该采用的关键实践。此外,这些实践假设您也在为任何高可用性系统采用典型的最佳实践。
我们从一些对某些人来说似乎平凡但有价值的事情开始,包括对 InnoDB 集群或高可用性解决方案的新手。除了您在组织中开发的最佳实践之外,以下部分还包含您应该考虑的最佳实践。
没有关机和重启
高可用性系统不是为定期关闭和重启而设计的。事实上,AdminAPI 中没有“关闭”命令。此语句指的是整个集群,而不是单个实例;您可能需要让实例脱机进行维护或修复,但是集群不应该关闭(唯一的例外是实验或开发试验)。您应该规划您的 InnoDB 集群安装,这样您就不必经常关闭或重启它。
在进行更改之前,请备份您的数据
对于拥有值为 2 的数据的任何系统的任何管理员来说,一个主要的工具是在进行任何更改之前保存数据的能力。这里选择的工具是备份和恢复实用程序。幸运的是,MySQL 在社区版中附带了两个这样的工具:mysqlpump和mysqldump。这些是逻辑备份工具,允许您使用 SQL 语句创建文件,以便从一个或多个数据库中恢复数据。mysqlpump实用程序是mysqldump的替代品,工作得更好,特别是对于大型数据库,因为mysqlpump可以执行并行转储。但是,这两种方法都可以在本演示中使用。
如前所述,这些是逐行制作数据副本的逻辑备份实用程序,而不是制作数据二进制副本的二进制备份。因此,逻辑备份往往比二进制备份更慢,占用的空间也更多。二进制备份通常还允许服务器在短时间内锁定数据,允许在服务器在线并接受更改时进行备份(有时称为热备份)。逻辑备份通常需要在备份期间锁定数据库。对于本演示,我们没有大型数据集,也不担心在线备份操作,因此可以毫无问题地使用逻辑备份。
MySQL 的企业级备份
如果您计划在您的企业中使用 MySQL,尤其是如果您计划部署 InnoDB 集群,您应该考虑企业版。虽然它是 Oracle 的付费产品,但它包含了许多企业级工具,包括 MySQL 企业备份(MEB)。
MEB 是一个完整的二进制备份工具,允许提供 MySQL 服务器的备份和恢复所需的所有备份方式。MEB 可以执行完整备份、增量备份、加密备份、压缩备份等等。如果您有兴趣了解更多关于 MEB 的信息,请参阅在线 MySQL 企业备份参考手册 https://dev.mysql.com/doc/mysql-enterprise-backup/8.0/en/ 。
在本节中,您将了解如何在执行任何维护操作之前对我们的数据进行逻辑备份。这对于允许您从禁用数据或以某种方式危及数据(或数据本身)访问的更改中恢复是必不可少的。一个好的系统管理员应该总是在进行任何维护之前进行备份。在确认更改是正确的之后,如果空间有问题,您可以随时删除备份文件。 3
如果集群处于活动状态,让我们首先备份我们创建的数据,以确保不会丢失。因为我们只有少量的数据,所以我们将使用名为mysqlpump的简单的逻辑备份工具。让我们从应用服务器开始工作。
下面显示了一个命令,您可以使用该命令对示例数据库执行逻辑备份,并将其存储在应用服务器上。我们必须提供用户连接信息、主机和端口。我们还提供了一个选项来生成位于文件顶部的DROP DATABASE和TABLE命令,这有助于通过首先删除数据来恢复数据。我们还添加了一个选项,将GTID_PURGED变量设置为OFF,这样我们就不会捕获 GTID 信息。回想一下,InnoDB 集群使用组复制,但是我们使用 InnoDB 集群和 AdminAPI 来管理集群,所以我们不需要 GTID 信息。我们将把这个文件命名为backup_pre_changes.sql。它被命名为.sql文件,因为mysqlpump创建 SQL 语句来保存模式和数据。
$ mysqlpump -uroot -p -h localhost --port=3306 --add-drop-database --add-drop-table --set-gtid-purged=OFF shopping > backup_pre_changes.sql
当您执行这个命令时,看起来什么也不会发生,因为我们将所有输出重定向到了该文件。这是因为该实用程序将 SQL 命令打印到标准输出。如果您感到好奇,请打开该文件并查看 SQL 命令。清单 9-5 是文件应该包含的内容的示例摘录。
-- Dump created by MySQL pump utility, version: 8.0.11, Linux (armv7l)
-- Dump start time: Fri Jun 1 15:23:00 2018
-- Server version: 8.0.11
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE;
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
SET @@SESSION.SQL_LOG_BIN= 0;
SET @OLD_TIME_ZONE=@@TIME_ZONE;
SET TIME_ZONE='+00:00';
SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT;
SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS;
SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION;
SET NAMES utf8mb4;
SET @@GLOBAL.GTID_PURGED=/*!80000 '+'*/ '16cce4a6-53d1-11e8-badc-b827eb2bc4f3:1-2,
2d550a88-5453-11e8-9517-b827eb2bc4f3:1-27,
4127c169-520f-11e8-a834-b827ebcb9200:1-170,
4b2d0de2-5454-11e8-9517-b827eb2bc4f3:1-3,
de5cbffd-5455-11e8-85a7-b827eb2bc4f3:1-119:1000102,
f1cdcfd9-62a2-11e8-9e50-b827eb2bc4f3:1-37,
fc2bf2b7-53ca-11e8-a131-b827eb2bc4f3:1-2';
DROP DATABASE IF EXISTS `shopping`;
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `shopping` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */;
DROP TABLE IF EXISTS `shopping`.`list`;
CREATE TABLE `shopping`.`list` (
`rowid` int(11) NOT NULL AUTO_INCREMENT,
`description` char(64) DEFAULT NULL,
`note` char(64) DEFAULT NULL,
`purchased` int(11) DEFAULT '0',
PRIMARY KEY (`rowid`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
;
INSERT INTO `shopping`.`list` VALUES (1,"Milk","2%",1),(2,"Bread",”,0),(3,"Eggs","Free range",0),(4,"Cheese","Cheddar",0),(5,"Bagels",”,0);
Dump progress: 1/1 tables, 0/5 rows
mysqlpump: [WARNING] (3719) 'utf8' is currently an alias for the character set UTF8MB3, which will be replaced by UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous.
mysqlpump: [WARNING] (3719) 'utf8' is currently an alias for the character set UTF8MB3, which will be replaced by UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous.
SET TIME_ZONE=@OLD_TIME_ZONE;
SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT;
SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS;
SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
SET SQL_MODE=@OLD_SQL_MODE;
-- Dump end time: Fri Jun 1 15:23:11 2018
Dump completed in 10798
Listing 9-5Example Logical Backup (mysqlpump)
如果您需要恢复数据,您可以使用 MySQL Shell 通过在 SQL 模式下使用\source命令来读取文件,如清单 9-6 所示。但是,除非您不小心删除了数据,否则您不需要这样做。此外,为了恢复 InnoDB 集群中的数据,您应该在读/写服务器连接上运行该命令(6446)。
pi@cluster-rpi1:~ $ mysqlsh root@cluster-rpi1:3306 --sql
Creating a session to 'root@cluster-rpi1:3306'
Enter password: ****
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 93
Server version: 8.0.11 MySQL Community Server (GPL)
No default schema selected; type \use <schema> to set one.
MySQL Shell 8.0.11
Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type '\help' or '\?' for help; '\quit' to exit.
MySQL cluster-rpi1:3306 ssl SQL > \source shopping_logical_backup.sql
Query OK, 0 rows affected (0.0022 sec)
Query OK, 0 rows affected (0.0018 sec)
Query OK, 0 rows affected (0.0017 sec)
Query OK, 0 rows affected (0.0017 sec)
Query OK, 0 rows affected (0.0016 sec)
Query OK, 0 rows affected (0.0017 sec)
Query OK, 0 rows affected (0.0019 sec)
Query OK, 0 rows affected (0.0017 sec)
Query OK, 0 rows affected (0.0017 sec)
Query OK, 0 rows affected (0.0017 sec)
Query OK, 0 rows affected (0.0019 sec)
Query OK, 1 row affected (0.2923 sec)
Query OK, 1 row affected (0.0121 sec)
Query OK, 0 rows affected, 1 warning (0.0083 sec)
Note (code 1051): Unknown table 'shopping.list'
Query OK, 0 rows affected (0.1067 sec)
Query OK, 5 rows affected (0.0187 sec)
Records: 5 Duplicates: 0 Warnings: 0
Query OK, 0 rows affected (0.0013 sec)
Query OK, 0 rows affected (0.0017 sec)
Query OK, 0 rows affected (0.0018 sec)
Query OK, 0 rows affected (0.0017 sec)
Query OK, 0 rows affected (0.0018 sec)
Query OK, 0 rows affected (0.0017 sec)
Query OK, 0 rows affected (0.0015 sec)
Listing 9-6Restoring a Logical Backup (mysqlsh)
小费
在对集群进行重大更改之前,请务必备份您的数据。
思考群体,而不是独立的服务器
学习如何部署和管理 InnoDB 集群的最大障碍之一是将集群视为一组相似的服务器,而不是一组单独的服务器。对于那些来自传统 MySQL 主/从复制的人来说,这是特别重要的一课。过去,人们通常认为拓扑中的服务器是必须单独使用的个体。这是因为我们有通过中继日志处理来自单个主服务器的更改的机器,而这些服务器(从服务器)从不互相通信。
但是,InnoDB Cluster(由于其组复制根)将集群中的机器视为一个组,其中一台(或多台)充当读/写服务器。事实上,管理哪台机器扮演哪种角色是自动的,所以我们不需要知道哪台服务器扮演哪种角色。MySQL 路由的加入为我们处理了集群中发生的变化。
在使用 InnoDB 集群时,我们必须将集群视为基础设施的高可用性组件,而不是一组松散耦合的服务器。这将允许您将 InnoDB Cluster 本身视为一个数据存储,这正是它应该成为的。
虽然硬件维护仍然是基于一台机器进行的,但是可以从集群中的任何机器或者不在集群中的另一台机器上使用 InnoDB 集群。这使得事情比旧形式的 MySQL 复制容易得多,使开发者能够专注于应用,而不是这种高可用性数据存储的细节。
保护 InnoDB 集群
最后一个最佳实践应该是任何人首先想到的—安全性!鉴于来自组织内外的威胁不断增加,提高安全性必须成为任何项目的首要目标。InnoDB 集群也不例外。
开发者不仅应该考虑保护他们的应用,还应该考虑保护幕后的数据和服务。事实上,所有使用互联网的解决方案都必须开发更好的安全实践。高可用性解决方案的独特方面使得计划和实施严格的安全实践变得特别困难,因为存在多个漏洞点。更具体地说,每个组件可能有不同类型的漏洞,从对设备的物理访问到对服务的远程攻击。
最近一连串的大规模数据泄露证明了他们的安全性不够。我们已经看到了从直接盗窃到利用从知名企业(如 Target)和政府机构(如美国人事管理办公室)窃取的数据(超过 4000 万个信用卡号可能已被泄露)的一切。有趣的是,违规的源头可以追溯到第三方承包商和服务。显然,没有人是安全的。我们需要一个革命性的步骤,而不是改进行之有效的机制。
可悲的是,在保护我们的解决方案方面,我们能走的路是有限的。任何信息技术(IT)专业人士都会告诉您,应用最好的、严格的密码策略和严密的安全措施会迫使用户危及旨在保护他们及其数据的策略。例如,考虑要求密码为 16 个或更多字符的密码策略,其中至少有四个大写字母、六个数字、三个特殊字符,并且没有英语词典单词;每 60 天过期一次;并且与以前的密码相同的字符不超过七个。在这种情况下,一些用户将被迫写下他们的密码,因为他们记不住字母、大写字母、数字和特殊字符的随机组合。
然而,让密码更难猜测或破解只是一种策略。事实上,关于如何正确保护系统存在各种不同的理念。尽管对所有技术的深入讨论超出了本书的范围,但是考虑如何提高 InnoDB 集群的安全性是很重要的。
那么,我们该怎么办?我们是否实施了良好的实践来确保系统不被轻易破坏,或者我们是否为了易用性而冒降低安全性的风险?底线是,您必须选择最能满足保护数据和服务需求的安全解决方案,而不强迫用户忍受繁重的实践,也不使他们的生活变得困难。
InnoDB Cluster 旨在与 MySQL 中最新的安全改进一起工作,包括新的身份验证插件( https://dev.mysql.com/doc/refman/8.0/en/caching-sha2-pluggable-authentication.html ),使其比 MySQL 的任何早期版本都更安全。然而,我们可以做更多的事情来保护我们的数据。幸运的是,MySQL 通过启用 SSL 连接来加密集群和客户端之间的通信(是的,甚至通过路由),使这一点变得很容易。
保护 InnoDB 集群有两个主要方面或工具。首先,我们应该使用 SSL 连接。其次,我们可以添加一个服务器白名单,只允许某些服务器访问集群。
SSL 连接
默认情况下,在 MySQL 8 的 GA 版本中,MySQL 服务器启用了 SSL 连接。如果您使用的是 MySQL 8.0.11 或更高版本,那么集群中服务器之间的连接已经配置了 SSL。但是,如果您加载了不同的身份验证插件,或者正在使用较旧但兼容的 MySQL 5.7 服务器,您可能需要考虑启用 SSL 连接。
小费
MySQL 可以配置为只使用安全连接。在 MySQL 中设置 SSL 的完整教程可以在在线参考手册的“使用加密连接”一节中找到。
通过在任何服务器上发出清单 9-7 中所示的查询,您可以快速确定您的服务器是否正在使用 SSL。注意,HAVE_SSL变量被设置为YES,, SSL 选项也相应地被填充,只有最少的一组选项。
SHOW VARIABLES LIKE '%ssl%';
+---------------------------------------------------+-----------------+
| Variable_name | Value |
+---------------------------------------------------+-----------------+
| group_replication_recovery_ssl_ca | |
| group_replication_recovery_ssl_capath | |
| group_replication_recovery_ssl_cert | |
| group_replication_recovery_ssl_cipher | |
| group_replication_recovery_ssl_crl | |
| group_replication_recovery_ssl_crlpath | |
| group_replication_recovery_ssl_key | |
| group_replication_recovery_ssl_verify_server_cert | OFF |
| group_replication_recovery_use_ssl | ON |
| group_replication_ssl_mode | REQUIRED |
| have_openssl | YES |
| have_ssl | YES |
| mysqlx_ssl_ca | |
| mysqlx_ssl_capath | |
| mysqlx_ssl_cert | |
| mysqlx_ssl_cipher | |
| mysqlx_ssl_crl | |
| mysqlx_ssl_crlpath | |
| mysqlx_ssl_key | |
| ssl_ca | ca.pem |
| ssl_capath | |
| ssl_cert | server-cert.pem |
| ssl_cipher | |
| ssl_crl | |
| ssl_crlpath | |
| ssl_fips_mode | OFF |
| ssl_key | server-key.pem |
+---------------------------------------------------+-----------------+
27 rows in set (0.0415 sec)
Listing 9-7Displaying the SSL Variables
我们告诉 InnoDB Cluster 使用 SSL 的方式是在dba.create_cluster()方法期间向集群传递一个选项,就像我们设置其他元数据一样。在这种情况下,我们必须将memberSslMode键设置为以下值之一。如您所见,默认情况下在创建时使用 SSL,因此按照本书中的演示创建集群将确保集群配置为 SSL 连接。
-
AUTO:(默认)如果服务器实例支持 SSL 加密,则自动启用,如果服务器不支持,则禁用。 -
REQUIRED:集群中的种子实例启用了 SSL 加密。如果无法启用,则会引发错误。 -
DISABLED:确保对集群中的种子实例禁用 SSL 加密。
类似地,当我们使用cluster.add_instance()方法添加实例或使用cluster.rejoin_instance()方法允许实例重新加入集群时,实例上的 SSL 加密会根据种子实例(我们创建集群的第一个实例)的设置启用或禁用。但是,您可以对这些方法使用相同的memberSslMode选项来控制 SSL 连接,如下所示。正如您所看到的,默认情况下对实例使用 SSL,就像创建集群一样。
-
AUTO:(默认)SSL 加密根据种子实例(集群的其他成员)使用的设置和实例本身提供的可用 SSL 支持自动启用或禁用。 -
REQUIRED:强制为集群中的实例启用 SSL 加密。 -
DISABLED:确保对集群中的实例禁用 SSL 加密。
对于已经配置了组复制的 InnoDB 集群的部署,我们可以在创建集群时使用adoptFromGR键告诉 InnoDB 集群采用现有组的 SSL 设置。这样做时,所采用的集群上的 SSL 设置不会改变。
注意
memberSslMode键不能与adoptFromGR键一起使用。
服务器白名单
正如前面“创建实例白名单”一节中提到的,您可以创建一个服务器白名单,限制对某些服务器的访问,这样就没有其他服务器可以连接或加入集群。如果您有一个大型基础架构,并且希望将对集群的访问隔离到一组服务器,以实现划分或类似的组织目标,这是一个很好的选择。
回想一下,我们可以通过向create_cluster()、add_instance()或rejoin_instance()方法传递一个特殊的参数来显式地创建白名单。因此,在配置服务器之前,必须考虑使用白名单。但是,如果您想为一个现有的集群建立一个白名单,您可能需要删除该实例,然后将其添加回集群,传递元数据以将其添加到白名单中。
小费
如果计划使用白名单,应该在创建集群和添加实例时设置白名单选项。
示例:需要 SSL 连接
这些选项最安全的形式是将memberSslMode设置为REQUIRED。让我们看一个简短的例子来说明如何做到这一点。与此同时,我们还将创建一个服务器白名单,以进一步保护集群。回想一下,我们通过将ipWhitelist设置为子网 192.168.42 来实现这一点。否则,我们发出创建集群和添加实例的普通命令,但是我们将以下选项传递给这些方法。在本节中,您将看到使用该选项的演示。
{'memberSslMode': 'REQUIRED', 'ipWhitelist': '192.168.42.0/24'}
这些选项允许我们通过始终要求 SSL 连接和限制对指定子网上的那些服务器的访问来使我们的集群更加安全。如果集群正在运行(并且已经进行了备份),请继续解散集群,如下所示。执行此操作之前,请务必进行备份,以免意外丢失任何数据。
MySQL cluster-rpi1:3306 ssl Py > cluster.dissolve({'force':True})
The cluster was successfully dissolved.
Replication was disabled but user data was left intact.
集群解散后,您可以关闭所有服务器的 MySQL 实例。回想一下,我们可以通过 MySQL Shell 使用SHUTDOWN SQL 命令关闭 MySQL,如下所示。记得使用--sql选项!在集群中的所有计算机上运行以下命令:
$ mysqlsh root@cluster-rpi1:3306 --sql -e "SHUTDOWN"
接下来,如果没有在所有服务器上配置 SSL,我们应该现在就设置它。再说一遍,如果你用的是 MySQL 8.0.11 或者更高版本,那就已经这样了。如果不确定,可以运行以下命令来确定每台服务器上是否启用了 SSL:
SHOW VARIABLES LIKE 'have_ssl';
好了,现在我们准备好了。我们将这个示例集群命名为RPI_Cluster_SSL,以表明它需要 SSL 连接。我们发出以下命令来创建集群并添加所有实例:
cluster = dba.create_cluster('RPI_Cluster_SSL', {'memberSslMode':'REQUIRED', 'ipWhitelist':'192.168.42.0/24'})
cluster.add_instance('root@cluster-rpi2:3306', {'memberSslMode':'REQUIRED', 'ipWhitelist':'192.168.42.0/24'})
cluster.add_instance('root@cluster-rpi3:3306', {'memberSslMode':'REQUIRED', 'ipWhitelist':'192.168.42.0/24'})
cluster.add_instance('root@cluster-rpi4:3306', {'memberSslMode':'REQUIRED', 'ipWhitelist':'192.168.42.0/24'})
请注意,白名单值是 192.168.42.0/24,它允许从 192.168.42.1 到 192.168.42.255 的连接。如果想要更多的限制,可以用更宽的口罩。如果你不确定如何计算,你可以使用在线子网计算器,比如在 www.subnet-calculator.com 找到的那种。
让我们看一个演示。清单 9-8 展示了一个设置集群的例子,这个集群需要 SSL,并且已经指定了一个白名单。因为您已经看到了这些命令的运行,所以我们将跳过对这些命令的详细解释,而将注意力集中在清单末尾的状态报告上。
MySQL cluster-rpi1:3306 ssl Py > cluster = dba.create_cluster('RPI_Cluster_SSL', {'memberSslMode':'REQUIRED', 'ipWhitelist':'192.168.42.0/24'})
A new InnoDB cluster will be created on instance 'root@cluster-rpi1:3306'.
Validating instance at cluster-rpi1:3306...
This instance reports its own address as cluster-rpi1
Instance configuration is suitable.
Creating InnoDB cluster 'RPI_Cluster_SSL' on 'root@cluster-rpi1:3306'...
Adding Seed Instance...
Cluster successfully created. Use Cluster.add_instance() to add MySQL instances.
At least 3 instances are needed for the cluster to be able to withstand up to
one server failure.
MySQL cluster-rpi1:3306 ssl Py > cluster.add_instance('root@cluster-rpi2:3306', {'memberSslMode':'REQUIRED', 'ipWhitelist':'192.168.42.0/24'})
A new instance will be added to the InnoDB cluster. Depending on the amount of
data on the cluster this might take from a few seconds to several hours.
Please provide the password for 'root@cluster-rpi2:3306': ****
Adding instance to the cluster ...
Validating instance at cluster-rpi2:3306...
This instance reports its own address as cluster-rpi2
Instance configuration is suitable.
The instance 'root@cluster-rpi2:3306' was successfully added to the cluster.
MySQL cluster-rpi1:3306 ssl Py > cluster.add_instance('root@cluster-rpi3:3306', {'memberSslMode':'REQUIRED', 'ipWhitelist':'192.168.42.0/24'})
A new instance will be added to the InnoDB cluster. Depending on the amount of
data on the cluster this might take from a few seconds to several hours.
Please provide the password for 'root@cluster-rpi3:3306': ****
Adding instance to the cluster ...
Validating instance at cluster-rpi3:3306...
This instance reports its own address as cluster-rpi3
Instance configuration is suitable.
The instance 'root@cluster-rpi3:3306' was successfully added to the cluster.
MySQL cluster-rpi1:3306 ssl Py > cluster.add_instance('root@cluster-rpi4:3306', {'memberSslMode':'REQUIRED', 'ipWhitelist':'192.168.42.0/24'})
A new instance will be added to the InnoDB cluster. Depending on the amount of
data on the cluster this might take from a few seconds to several hours.
Please provide the password for 'root@cluster-rpi4:3306': ****
Adding instance to the cluster ...
Validating instance at cluster-rpi4:3306...
This instance reports its own address as cluster-rpi4
Instance configuration is suitable.
The instance 'root@cluster-rpi4:3306' was successfully added to the cluster.
MySQL cluster-rpi1:3306 ssl Py > cluster.status()
{
"clusterName": "RPI_Cluster_SSL",
"defaultReplicaSet": {
"name": "default",
"primary": "cluster-rpi1:3306",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"cluster-rpi1:3306": {
"address": "cluster-rpi1:3306",
"mode": "R/W",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"cluster-rpi2:3306": {
"address": "cluster-rpi2:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"cluster-rpi3:3306": {
"address": "cluster-rpi3:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"cluster-rpi4:3306": {
"address": "cluster-rpi4:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
}
}
},
"groupInformationSourceMember": "mysql://root@cluster-rpi1:3306"
}
Listing 9-8Example Cluster Setup with SSL = Required and Whitelist Defined
请注意,在状态报告中,我们看到 SSL 模式是必需的(这是默认的)。我们可以通过查询 SSL 选项来确认这一点:
MySQL cluster-rpi1:3306 ssl mysql_innodb_cluster_metadata SQL > SHOW VARIABLES LIKE '%ssl%';
+---------------------------------------------------+-----------------+
| Variable_name | Value |
+---------------------------------------------------+-----------------+
| group_replication_recovery_ssl_ca | |
| group_replication_recovery_ssl_capath | |
| group_replication_recovery_ssl_cert | |
| group_replication_recovery_ssl_cipher | |
| group_replication_recovery_ssl_crl | |
| group_replication_recovery_ssl_crlpath | |
| group_replication_recovery_ssl_key | |
| group_replication_recovery_ssl_verify_server_cert | OFF |
| group_replication_recovery_use_ssl | ON |
| group_replication_ssl_mode | REQUIRED |
| have_openssl | YES |
| have_ssl | YES |
| mysqlx_ssl_ca | |
| mysqlx_ssl_capath | |
| mysqlx_ssl_cert | |
| mysqlx_ssl_cipher | |
| mysqlx_ssl_crl | |
| mysqlx_ssl_crlpath | |
| mysqlx_ssl_key | |
| ssl_ca | ca.pem |
| ssl_capath | |
| ssl_cert | server-cert.pem |
| ssl_cipher | |
| ssl_crl | |
| ssl_crlpath | |
| ssl_fips_mode | OFF |
| ssl_key | server-key.pem |
+---------------------------------------------------+-----------------+
27 rows in set (0.0391 sec)
那么,白名单呢?我们可以通过检查组复制的变量来查看白名单,如清单 9-9 所示。我们可以看到白名单以及组复制的所有其他变量,所有这些都是 InnoDB Cluster 为我们设置的。
MySQL cluster-rpi1:3306 ssl mysql_innodb_cluster_metadata Py > \sql
Switching to SQL mode... Commands end with ;
MySQL cluster-rpi1:3306 ssl mysql_innodb_cluster_metadata SQL > SHOW VARIABLES LIKE '%ssl%';
+---------------------------------------------------+-----------------+
| Variable_name | Value |
+---------------------------------------------------+-----------------+
| group_replication_recovery_ssl_ca | |
| group_replication_recovery_ssl_capath | |
| group_replication_recovery_ssl_cert | |
| group_replication_recovery_ssl_cipher | |
| group_replication_recovery_ssl_crl | |
| group_replication_recovery_ssl_crlpath | |
| group_replication_recovery_ssl_key | |
| group_replication_recovery_ssl_verify_server_cert | OFF |
| group_replication_recovery_use_ssl | ON |
| group_replication_ssl_mode | REQUIRED |
| have_openssl | YES |
| have_ssl | YES |
| mysqlx_ssl_ca | |
| mysqlx_ssl_capath | |
| mysqlx_ssl_cert | |
| mysqlx_ssl_cipher | |
| mysqlx_ssl_crl | |
| mysqlx_ssl_crlpath | |
| mysqlx_ssl_key | |
| ssl_ca | ca.pem |
| ssl_capath | |
| ssl_cert | server-cert.pem |
| ssl_cipher | |
| ssl_crl | |
| ssl_crlpath | |
| ssl_fips_mode | OFF |
| ssl_key | server-key.pem |
+---------------------------------------------------+-----------------+
27 rows in set (0.0391 sec)
MySQL cluster-rpi1:3306 ssl mysql_innodb_cluster_metadata SQL > SHOW VARIABLES LIKE '%white%';
+--------------------------------+-----------------+
| Variable_name | Value |
+--------------------------------+-----------------+
| group_replication_ip_whitelist | 192.168.42.0/24 |
+--------------------------------+-----------------+
1 row in set (0.0383 sec)
MySQL cluster-rpi1:3306 ssl SQL > SHOW VARIABLES LIKE 'group_replication%';
+-----------------------------------------------------+-------------------+
| Variable_name | Value |
+-----------------------------------------------------+-------------------+
| group_replication_allow_local_lower_version_join | OFF |
| group_replication_auto_increment_increment | 7 |
| group_replication_bootstrap_group | OFF |
| group_replication_communication_debug_options | GCS_DEBUG_NONE |
| group_replication_components_stop_timeout | 31536000 |
| group_replication_compression_threshold | 1000000 |
| group_replication_enforce_update_everywhere_checks | OFF |
| group_replication_flow_control_applier_threshold | 25000 |
| group_replication_flow_control_certifier_threshold | 25000 |
| group_replication_flow_control_hold_percent | 10 |
| group_replication_flow_control_max_quota | 0 |
| group_replication_flow_control_member_quota_percent | 0 |
| group_replication_flow_control_min_quota | 0 |
| group_replication_flow_control_min_recovery_quota | 0 |
| group_replication_flow_control_mode | QUOTA |
| group_replication_flow_control_period | 1 |
| group_replication_flow_control_release_percent | 50 |
| group_replication_force_members | |
| group_replication_group_name | a587a3ad-7002-11e8-8612-b827eb2bc4f3 |
| group_replication_group_seeds | |
| group_replication_gtid_assignment_block_size | 1000000 |
| group_replication_ip_whitelist | 192.168.42.0/24 |
| group_replication_local_address | |
| group_replication_member_weight | 50 |
| group_replication_poll_spin_loops | 0 |
| group_replication_recovery_complete_at | TRANSACTIONS_APPLIED |
| group_replication_recovery_get_public_key | ON |
| group_replication_recovery_public_key_path | |
| group_replication_recovery_reconnect_interval | 60 |
| group_replication_recovery_retry_count | 10 |
| group_replication_recovery_ssl_ca | |
| group_replication_recovery_ssl_capath | |
| group_replication_recovery_ssl_cert | |
| group_replication_recovery_ssl_cipher | |
| group_replication_recovery_ssl_crl | |
| group_replication_recovery_ssl_crlpath | |
| group_replication_recovery_ssl_key | |
| group_replication_recovery_ssl_verify_server_cert | OFF |
| group_replication_recovery_use_ssl | OFF |
| group_replication_single_primary_mode | ON |
| group_replication_ssl_mode | DISABLED |
| group_replication_start_on_boot | OFF |
| group_replication_transaction_size_limit | 150000000 |
| group_replication_unreachable_majority_timeout | 0 |
+-----------------------------------------------------+-------------------+
44 rows in set (0.0340 sec)
Listing 9-9Displaying the Group Replication Variables
保护路由
既然我们已经为 SSL 和白名单配置了 MySQL 服务器,您可能想知道路由需要做些什么。我们如何知道我们有从应用到路由的 SSL 连接?
因为我们使用的是 MySQL 8.0.11 或更高版本,我们知道我们默认使用 SSL 连接,即使设置为需要 SSL 连接。因为我们更改了集群的名称,所以我们只需要做一个更改:我们必须编辑路由配置文件以连接到正确的集群。打开路由配置文件(例如,/usr/local/mysql/lib/mysql-router/mysqlrouter.conf)并更改清单 9-10 中突出显示的集群名称。
# File automatically generated during MySQL Router bootstrap
[DEFAULT]
user=pi
logging_folder=/usr/local/mysql/lib/mysql-router/log
runtime_folder=/usr/local/mysql/lib/mysql-router/run
data_folder=/usr/local/mysql/lib/mysql-router/data
plugin_folder=/usr/local/mysql/lib/mysqlrouter
keyring_path=/usr/local/mysql/lib/mysql-router/data/keyring
master_key_path=/usr/local/mysql/lib/mysql-router/mysqlrouter.key
connect_timeout=30
read_timeout=30
[logger]
level = INFO
[metadata_cache:RPI_Cluster_SSL]
router_id=4
bootstrap_server_addresses=mysql://cluster-rpi1:3306,mysql://cluster-rpi2:3306,mysql://cluster-rpi3:3306,mysql://c
luster-rpi4:3306
user=mysql_router4_gnl6peiy2z2v
metadata_cluster=RPI_Cluster_SSL
ttl=5
[routing:RPI_Cluster_SSL_default_rw]
bind_address=0.0.0.0
bind_port=6446
destinations=metadata-cache://RPI_Cluster_SSL/default?role=PRIMARY
routing_strategy=round-robin
protocol=classic
[routing:RPI_Cluster_SSL_default_ro]
bind_address=0.0.0.0
bind_port=6447
destinations=metadata-cache://RPI_Cluster_SSL/default?role=SECONDARY
routing_strategy=round-robin
protocol=classic
[routing:RPI_Cluster_SSL_default_x_rw]
bind_address=0.0.0.0
bind_port=64460
destinations=metadata-cache://RPI_Cluster_SSL/default?role=PRIMARY
routing_strategy=round-robin
protocol=x
[routing:RPI_Cluster_SSL_default_x_ro]
bind_address=0.0.0.0
bind_port=64470
destinations=metadata-cache://RPI_Cluster_SSL/default?role=SECONDARY
routing_strategy=round-robin
protocol=x
Listing 9-10Change the Router Configuration File
请注意,我们将所有出现的RPI_Cluster更改为RPI_Cluster_SSL。您可以执行搜索并替换,或者,如果您想要创建新的路由配置文件,您可以使用 Bootstrap 选项并指定新的配置文件,如下所示。注意使用了--directory选项来指定存储配置的新目录,使用了--name选项来指定配置的新名称。
$ sudo /usr/local/mysql/bin/mysqlrouter --bootstrap root@cluster-rpi4:3306 \
--directory=/usr/local/mysql/lib/mysql-router-ssl/mysqlrouter.conf --user=pi \
--name=router_rpi_ssl
在我们做出这些更改之后,我们可以通过使用配置文件(修改过的或者新的)重启路由,并尝试我们的测试代码,如清单 9-11 所示。您可以将该文件保存为router_connect_test.py。
import mysql.connector
# Simple function to display results from a cursor
def show_results(cur_obj):
for row in cur_obj:
print(row)
my_cfg = {
'user':'root',
'passwd':'secret',
'host':'127.0.0.1',
'port':6446
}
# Connecting to the server
conn = mysql.connector.connect(**my_cfg)
print("Listing the databases on the server.")
query = "SHOW DATABASES"
cur = conn.cursor()
cur.execute(query)
show_results(cur)
print("\nRetrieve the port for the server to which we're connecting.")
query = "SELECT @@port"
cur = conn.cursor()
cur.execute(query)
show_results(cur)
# Close the cursor and connection
cur.close()
conn.close()
Listing 9-11
Router Test Script
现在,我们运行 Python 脚本,应该会看到如下所示的输出。您可能认为代码会失败,但是在我们的集群中,客户端连接没有任何变化。因为我们使用的是 MySQL 8.0.11,SSL 连接在默认情况下是打开的。
$ python3 ./router_connect_test.py
('information_schema',)
('mysql',)
('mysql_innodb_cluster_metadata',)
('performance_schema',)
('shopping',)
('sys',)
就这样!我们刚刚通过启用 SSL 连接和使用白名单使我们的开发 InnoDB 集群更加安全。
摘要
对一个系统的真正考验是随着时间的推移它能被维护得多好。这种成功的一个重要因素是完成常规任务所需任务的复杂性。执行维护任务越容易,保持系统健康就越容易。同样,对于如何从导致需要修复或排除故障的错误或情况中恢复,有明确的任务也很重要。幸运的是,AdminAPI 拥有我们保持 InnoDB 集群良好运行所需的工具。
在本章中,您回顾了最常见的管理任务,查看了一组故障排除任务,并发现了一些与管理 InnoDB Cluster 相关的最佳实践。
下一章通过讨论部署 InnoDB Cluster 的注意事项以及为使用 MySQL 早期版本的用户准备迁移到 MySQL 8.0 来结束对 InnoDB Cluster 的介绍。
Footnotes [1](#Fn1_source)与黑名单相反,黑名单是你想要禁止的东西的列表。
所有的数据对所有者来说不都是有价值的吗?
我倾向于保存备份文件至少一个月,以防万一。
十、规划您的部署
这本书涵盖了很多内容,包括高可用性对您的意义以及 MySQL 如何满足这些需求的简要概述。我们重点介绍了 MySQL InnoDB 集群及其所有组件:InnoDB 存储引擎、组复制、AdminAPI、MySQL Shell 和 MySQL 路由。不仅如此,我们还通过使用沙箱和一组运行在不同机器上的 MySQL 服务器,完成了开发高可用性应用以及设置 InnoDB 集群的过程。
您还看到了一个独特的替代平台来试验 InnoDB 集群:Raspberry Pi。回想一下,您了解到尽管 Raspberry Pi 受限于相对较小的内存和较慢的处理器,但 MySQL 在该平台上运行得相当好——好到我们可以用 InnoDB Cluster 进行试验,而无需在一堆服务器硬件上花费大量资金。
然而,现在是时候了解如何在企业中采用 MySQL 8 和 MySQL InnoDB 集群了。本章接下来介绍规划集群部署的注意事项和技术。因为许多读者可能不熟悉 MySQL 8,所以我们也包括了如何计划在您的企业中采用 MySQL 8 的信息。
让我们首先简要讨论一些规划集群部署的策略。
规划您的集群
如果您想取得成功,系统管理和规划是不可分割的,在规划高可用性解决方案时更是如此。我们必须提前计划设置、配置和部署解决方案,否则将面临意外问题和延迟的危险。InnoDB Cluster 也不例外,您最好计划一下在您的环境中使用它的方式。
本节中的信息将帮助您深入了解特定于 InnoDB 集群的规划领域。您应该将本节视为一个额外的资源,与您已建立的策略、实践和工具一起用于规划系统。下面列出了 InnoDB 集群需要仔细规划的一些关键领域:
-
为 InnoDB 集群管理员创建一个单独的用户
-
配置主机名
-
考虑您想要的元数据选项
-
在 MySQL Shell 和 AdminAPI 中使用日志进行调试
-
考虑读/写服务器的数量
-
考虑只读服务器的数量
-
规划物理部署
-
从组复制部署规划升级
在本书的整个过程中,我们已经讨论了这些主题中的大部分,但是有些可能对您来说是新的,尤其是如果您没有大量部署 MySQL 无论是使用复制还是类似的配置。
以下部分通过提供建议(在某些情况下,提供示例)来阐述这些方面,以便您可以评估是否将它们纳入您的 InnoDB 集群计划中。不要认为这些是强制性的,而是强烈推荐的。
用户帐户
前一章介绍了使用 SSL 和服务器白名单来保护 InnoDB 集群。没有讨论的是用于管理 InnoDB 集群的用户帐户。到目前为止,我们使用的用户帐户是 root 帐户。尽管您可以继续有效地使用该帐户,但更安全的选择是为该任务指定一个特定的用户帐户。有些人可能不愿意这样做,因为这需要记住另一个用户帐户和密码,但这被认为比使用 root 帐户更安全,因为 root 帐户通常由多个人使用,执行各种与 MySQL 相关的操作。为 InnoDB Cluster 使用一个单独的帐户意味着潜在的更少的人会使用它来做其他事情。
用于管理的用户帐户除了拥有完整的 MySQL 管理员权限(与 root 相同:SUPER、GRANT OPTION、CREATE、DROP等等)之外,还必须拥有对 InnoDB Cluster 元数据数据库表的完全读写权限。).您可以使用CREATE USER和GRANT SQL命令手动创建用户,如下所示:
CREATE USER idc_admin@'%' IDENTIFIED BY 'secret';
GRANT ALL ON mysql_innodb_cluster_metadata.* TO idc_admin@'%' WITH GRANT OPTION;
然而,首选方法是将clusterAdmin和clusterAdminPassword选项与dba.configure_instance()、dba.configure_local_instance()和cluster.add_instance()方法一起使用:
dba.configure_instance('root@localhost:3306', {'clusterAdmin': "idc_admin@'%'", 'clusterAdminPassword': 'secret'})
如果需要监控集群性能和元数据,可以使用更严格的权限创建一个只读帐户。以下示例创建用户并授予只读权限:
CREATE USER idc_mon@'%' IDENTIFIED BY 'secret';
GRANT SELECT ON mysql_innodb_cluster_metadata.* TO idc_mon@'%';
GRANT SELECT ON performance_schema.global_status TO idc_mon@'%';
GRANT SELECT ON performance_schema.replication_applier_configuration TO idc_mon@'%';
GRANT SELECT ON performance_schema.replication_applier_status TO idc_mon@'%';
GRANT SELECT ON performance_schema.replication_applier_status_by_coordinator TO idc_mon@'%';
GRANT SELECT ON performance_schema.replication_applier_status_by_worker TO idc_mon@'%';
GRANT SELECT ON performance_schema.replication_connection_configuration TO idc_mon@'%';
GRANT SELECT ON performance_schema.replication_connection_status TO idc_mon@'%';
GRANT SELECT ON performance_schema.replication_group_member_stats TO idc_mon@'%';
GRANT SELECT ON performance_schema.replication_group_members TO idc_mon@'%';
GRANT SELECT ON performance_schema.threads TO idc_mon@'%' WITH GRANT OPTION;
主机名
虽然选择主机名看起来不像是您想要计划的事情,但是考虑一下第 7 章中的示例部署,它使用了诸如cluster-rpi1和cluster-rpi2之类的主机名。选择这些主机名是为了帮助识别群集中使用的服务器。在这种情况下,主机名被选为模拟集群的名称。但是,您可以使用带有前缀或后缀的集群名称来帮助进一步识别服务器。底线是,建议选择一个帮助您管理服务器的主机名。除此之外,主机名只是一个字符串,在系统或网络上定义主机名的范围内,您可以使用任何名称。
我们已经知道,集群中的每台机器都必须有自己的主机名。我们还看到了一种分配主机名和使用/etc/hosts文件来管理没有域名系统(DNS)的访问的技术。更具体地说,每台机器都必须能够解析集群中其他服务器的主机名。但是,如果您的网络上有 DNS,您可以通过使用该服务来分配主机名并相应地映射它们。
另外,记得为集群中的每个服务器配置report_host服务器选项。InnoDB 集群将使用它向集群报告主机名,并将其存储在元数据(和配置文件)中。该值应该与机器的主机名相匹配。在前面章节的演示中,您也看到了如何做到这一点。
[计]元数据
你在第 9 章中发现了如何改变元数据。当我们以 JSON 键/值对集的形式创建集群时,我们可以设置某些选项。以下是dba.create_cluster()方法中可用选项的列表。回想一下,我们还提供了集群的名称作为该方法的一个单独的参数。确保为集群选择一个描述性的名称,并且在您的基础架构中是唯一的。
-
multiMaster:如果True,用多个可写实例定义一个 InnoDB 集群。 -
force:如果True,确认必须应用multiMaster选项。 -
adoptFromGR:如果True,基于现有的复制组创建 InnoDB 集群。 -
memberSslMode:用于配置集群成员的 SSL 模式。 -
ipWhitelist:允许连接到实例进行组复制的主机列表。 -
clearReadOnly:如果True,确认super_read_only必须禁用。 -
groupName:要使用的组复制组名 UUID,而不是自动生成的名称。 -
localAddress:要使用的组复制本地地址,而不是自动生成的地址。 -
groupSeeds:要使用的组复制对等地址的逗号分隔列表,而不是自动生成的列表。
规划群集时,您应该查看此列表,看看是否需要设置这些选项中的任何一个。您已经看到了如何使用这些选项来设置 SSL 模式和建立白名单。如果您要从组复制部署进行迁移,您可能需要考虑其中的一些选项。我们将在后面的部分讨论从组复制迁移。我们还将在下一节讨论多主机选项。
记录
如果您做过任何形式的系统管理或问题诊断,包括调试代码,那么您应该熟悉日志和日志记录。MySQL Shell 提供了强大的调试级日志功能,可以帮助您管理集群。这在使用服务器实例时尤其有用。要在 MySQL Shell 中打开日志记录,请使用如下的–log-level选项:
$ mysqlsh root@cluster-rpi1:3306 --log-level=DEBUG3
DEBUG3值是最详细的选项,建议用于诊断。如果您想默认不进行额外的日志记录或使用不同的级别,您可以随时重新启动 shell。有关日志记录的更多详细信息,请参见 MySQL Shell 在线参考。
您还可以通过将dba.verbose成员变量设置为下列值之一来增加 AdminAPI 的详细程度(日志记录):
-
0或OFF:(默认)提供最小输出,是推荐的等级 -
1或ON:将每个调用的详细输出添加到 AdminAPI 中 -
2:显示附加调试输出以及详细输出(developer-level)
清单 10-1 展示了一个使用带有日志记录和 verbosity 成员变量的 shell 的例子,以及您可以看到的输出。在这种情况下,我们使用级别 2,这是最详细的选项。该示例显示我们需要向集群中重新添加一个实例:一台机器出现了问题,需要修复,包括重新安装 MySQL。这个清单相当长,因为我们已经打开了最大详细度,但是它让我们深入了解了 shell 和 AdminAPI 中发生的许多事情。为了简洁起见,已经排除了一些更普通的部分。请花些时间仔细阅读这个列表。它揭示了很多关于add_instance()方法的工作方式。
MySQL cluster-rpi1:3306 ssl Py > dba.verbose=2
MySQL cluster-rpi1:3306 ssl Py > cluster = dba.get_cluster()
MySQL cluster-rpi1:3306 ssl Py > cluster.add_instance('root@cluster-rpi2:3306')
A new instance will be added to the InnoDB cluster. Depending on the amount of
data on the cluster this might take from a few seconds to several hours.
Please provide the password for 'root@cluster-rpi2:3306': ****
Adding instance to the cluster ...
Validating instance at cluster-rpi2:3306...
This instance reports its own address as cluster-rpi2
DBA: mysqlprovision: Executing printf '[{"server":{"host":"cluster-rpi2","passwd":"****","password":"****","port":3306,"scheme":"mysql","user":"root"},"verbose":2}]\n.\n' | /usr/local/mysql/bin/mysqlsh --log-level=8 --py -f /usr/local/mysql/share/mysqlsh/mysqlprovision.zip check
=========================== MySQL Provision Output ===========================
DEBUG: MySQL query: SHOW VARIABLES LIKE 'READ_ONLY'
Running check command.
DEBUG: MySQL query: SELECT GROUP_NAME FROM performance_schema.replication_connection_status where CHANNEL_NAME = 'group_replication_applier'
Checking Group Replication prerequisites.
DEBUG: The server: 'cluster-rpi2:3306' has been set to check
DEBUG: Option checking started: {'log_slave_updates': {'ONE OF': ('ON', '1')}, 'binlog_format': {'ONE OF': ('ROW',)}, 'relay_log_info_repository': {'ONE OF': ('TABLE',)}, 'binlog_checksum': {'ONE OF': ('NONE',)}, 'report_port': {'ONE OF': ('3306',)}, 'enforce_gtid_consistency': {'ONE OF': ('ON', '1')}, 'master_info_repository': {'ONE OF': ('TABLE',)}, 'log_bin': {'ONE OF': ('1', 'ON')}, 'gtid_mode': {'ONE OF': ('ON',)}, 'transaction_write_set_extraction': {'ONE OF': ('XXHASH64', '2', 'MURMUR32', '1')}}
DEBUG: Checking option: 'log_slave_updates'
DEBUG: MySQL query: SELECT @@log_slave_updates
DEBUG: Option current value: '1'
DEBUG: OK: value 1 is one of ('ON', '1')
DEBUG: Checking option: 'binlog_format'
DEBUG: MySQL query: SELECT @@binlog_format
DEBUG: Option current value: 'ROW'
DEBUG: OK: value ROW is one of ('ROW',)
...
DEBUG: MySQL query: SHOW VARIABLES LIKE 'VERSION'
DEBUG: Server version: [8, 0, 11]
DEBUG: Server version check result: True
* Checking server version... PASS
Server is 8.0.11
...
DEBUG: MySQL query: show plugins
* Verifying Group Replication plugin for server 'cluster-rpi2:3306' ...
DEBUG: MySQL query: show plugins
WARNING: The group_replication plugin has not been installed/loaded in 'cluster-rpi2:3306'
Group Replication plugin: Not loaded
DEBUG: MySQL query: SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE 'group_replication%'
DEBUG: Plugin group_replication is not installed
===========================================================================
Instance configuration is suitable.
DBA: mysqlprovision: Executing printf '[{"group_seeds":"cluster-rpi3:33061,cluster-rpi4:33061,cluster-rpi1:33061","rep_user_passwd":"****","replication_user":"mysql_innodb_cluster_r0000124900","ssl_mode":"REQUIRED","verbose":2},{"host":"cluster-rpi2","passwd":"****","password":"****","port":3306,"user":"root"},{"host":"cluster-rpi1","passwd":"****","port":3306,"user":"root"}]\n.\n' | /usr/local/mysql/bin/mysqlsh --log-level=8 --py -f /usr/local/mysql/share/mysqlsh/mysqlprovision.zip join-replicaset
=========================== MySQL Provision Output ===========================
DEBUG: MySQL query: SHOW VARIABLES LIKE 'READ_ONLY'
Running join command on 'cluster-rpi2:3306'.
Checking Group Replication prerequisites.
DEBUG: MySQL query: SHOW VARIABLES LIKE 'READ_ONLY'
DEBUG: MySQL query: SELECT GROUP_NAME FROM performance_schema.replication_connection_status where CHANNEL_NAME = 'group_replication_applier'
DEBUG: MySQL query: SELECT MEMBER_STATE FROM performance_schema.replication_group_members as m JOIN performance_schema.replication_group_member_stats as s on m.MEMBER_ID = s.MEMBER_ID AND m.MEMBER_ID = @@server_uuid
DEBUG: MySQL query: SELECT @@have_ssl
DEBUG: MySQL query: SELECT @@group_replication_recovery_use_ssl
DEBUG: MySQL query: SELECT @@group_replication_ssl_mode
DEBUG: ->parse_server_address
host: %
address_type: host like
DEBUG: Using replication_user: mysql_innodb_cluster_r0000124900@'%'
DEBUG: ->rpl_user_dict {'replication_user': "mysql_innodb_cluster_r0000124900@'%'", 'rep_user_passwd': '******', 'host': '%', 'recovery_user': 'mysql_innodb_cluster_r0000124900', 'ssl_mode': u'REQUIRED'}
DEBUG: MySQL query: select MEMBER_HOST, MEMBER_PORT from performance_schema.replication_group_members
DEBUG: MySQL query: SHOW VARIABLES LIKE 'READ_ONLY'
DEBUG: MySQL query: SHOW VARIABLES LIKE 'READ_ONLY'
DEBUG: MySQL query: SHOW VARIABLES LIKE 'READ_ONLY'
DEBUG: The server: 'cluster-rpi2:3306' has been set to check
DEBUG: Option checking started: {'log_slave_updates': {'ONE OF': ('ON', '1')}, 'binlog_format': {'ONE OF': ('ROW',)}, 'relay_log_info_repository': {'ONE OF': ('TABLE',)}, 'binlog_checksum': {'ONE OF': ('NONE',)}, 'report_port': {'ONE OF': ('3306',)}, 'enforce_gtid_consistency': {'ONE OF': ('ON', '1')}, 'master_info_repository': {'ONE OF': ('TABLE',)}, 'log_bin': {'ONE OF': ('1', 'ON')}, 'gtid_mode': {'ONE OF': ('ON',)}, 'transaction_write_set_extraction': {'ONE OF': ('XXHASH64', '2', 'MURMUR32', '1')}}
DEBUG: Checking option: 'log_slave_updates'
DEBUG: MySQL query: SELECT @@log_slave_updates
DEBUG: Option current value: '1'
DEBUG: OK: value 1 is one of ('ON', '1')
DEBUG: Checking option: 'binlog_format'
DEBUG: MySQL query: SELECT @@binlog_format
DEBUG: Option current value: 'ROW'
DEBUG: OK: value ROW is one of ('ROW',)
DEBUG: Checking option: 'relay_log_info_repository'
DEBUG: MySQL query: SELECT @@relay_log_info_repository
DEBUG: Option current value: 'TABLE'
DEBUG: OK: value TABLE is one of ('TABLE',)
DEBUG: Checking option: 'binlog_checksum'
DEBUG: MySQL query: SELECT @@binlog_checksum
DEBUG: Option current value: 'NONE'
DEBUG: OK: value NONE is one of ('NONE',)
DEBUG: Checking option: 'report_port'
DEBUG: MySQL query: SELECT @@report_port
DEBUG: Option current value: '3306'
DEBUG: OK: value 3306 is one of ('3306',)
DEBUG: Checking option: 'enforce_gtid_consistency'
DEBUG: MySQL query: SELECT @@enforce_gtid_consistency
DEBUG: Option current value: 'ON'
DEBUG: OK: value ON is one of ('ON', '1')
DEBUG: Checking option: 'master_info_repository'
DEBUG: MySQL query: SELECT @@master_info_repository
DEBUG: Option current value: 'TABLE'
DEBUG: OK: value TABLE is one of ('TABLE',)
DEBUG: Checking option: 'log_bin'
DEBUG: MySQL query: SELECT @@log_bin
DEBUG: Option current value: '1'
DEBUG: OK: value 1 is one of ('1', 'ON')
DEBUG: Checking option: 'gtid_mode'
DEBUG: MySQL query: SELECT @@gtid_mode
DEBUG: Option current value: 'ON'
DEBUG: OK: value ON is one of ('ON',)
DEBUG: Checking option: 'transaction_write_set_extraction'
DEBUG: MySQL query: SELECT @@transaction_write_set_extraction
DEBUG: Option current value: 'XXHASH64'
DEBUG: OK: value XXHASH64 is one of ('XXHASH64', '2', 'MURMUR32', '1')
DEBUG: Options check result: True
* Comparing options compatibility with Group Replication... PASS
Server configuration is compliant with the requirements.
DEBUG: Checking option: 'transaction_write_set_extraction'
DEBUG: MySQL query: SELECT @@global.transaction_write_set_extraction
DEBUG: MySQL query: SELECT @@global.transaction_write_set_extraction
DEBUG: expected value: XXHASH64 found
* Comparing options compatibility with the group of the given peer-instance... PASS
Server configuration is compliant with current group configuration.
Option name Required Value Current Value Result
------------------------------- --------------- --------------- -----
transaction_write_set_extraction XXHASH64 XXHASH64 PASS
DEBUG: Server version checking: 5.7.17
DEBUG: MySQL query: SHOW VARIABLES LIKE 'VERSION'
DEBUG: Server version: [8, 0, 11]
DEBUG: Server version check result: True
* Checking server version... PASS
Server is 8.0.11
DEBUG: checking server id uniqueness
DEBUG: MySQL query: SELECT @@server_id
DEBUG: server id = 102
DEBUG: MySQL query: SELECT variable_source FROM performance_schema.variables_info WHERE variable_name="server_id"
DEBUG: MySQL query: SELECT @@server_id
DEBUG: Verifying the peer 'cluster-rpi3:3306' ...
DEBUG: The peer 'cluster-rpi3:3306' have a different server_id 103
DEBUG: MySQL query: SELECT @@server_id
DEBUG: Verifying the peer 'cluster-rpi4:3306' ...
DEBUG: The peer 'cluster-rpi4:3306' have a different server_id 104
DEBUG: MySQL query: SELECT @@server_id
DEBUG: Verifying the peer 'cluster-rpi1:3306' ...
DEBUG: The peer 'cluster-rpi1:3306' have a different server_id 101
* Checking that server_id is unique... PASS
The server_id is valid.
DEBUG: MySQL query: SELECT @@slave_parallel_workers
* Checking compatibility of Multi-Threaded Slave settings... PASS
Multi-Threaded Slave settings are compatible with Group Replication.
DEBUG: MySQL query: show plugins
* Verifying Group Replication plugin for server 'cluster-rpi2:3306' ...
DEBUG: MySQL query: SELECT @@global.super_read_only
Initializing group_replication plugin on 'cluster-rpi2:3306'
DEBUG: MySQL query: show plugins
DEBUG: MySQL query: SELECT @@version_compile_os
DEBUG: MySQL query: INSTALL PLUGIN group_replication SONAME 'group_replication.so'
DEBUG: The group_replication plugin has been successfully install in server: 'cluster-rpi2:3306'
DEBUG: MySQL query: SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE 'group_replication%'
DEBUG: Plugin group_replication has state: ACTIVE and not the expected: DISABLED
WARNING: Not running locally on the server and can not access its error log.
DEBUG: MySQL query: SELECT MEMBER_STATE FROM performance_schema.replication_group_members as m JOIN performance_schema.replication_group_member_stats as s on m.MEMBER_ID = s.MEMBER_ID AND m.MEMBER_ID = @@server_uuid
DEBUG: local_address to use: cluster-rpi2:33061
DEBUG: MySQL query: show plugins
DEBUG: MySQL query: SELECT @@global.group_replication_local_address
DEBUG: MySQL query: SELECT @@group_replication_single_primary_mode
DEBUG: MySQL query: SELECT @@server_id
DEBUG: Trying to retrieve group replication name from peer server.
DEBUG: MySQL query: show plugins
DEBUG: MySQL query: SELECT GROUP_NAME FROM performance_schema.replication_connection_status WHERE CHANNEL_NAME="group_replication_applier"
DEBUG: Retrieved group replication name from peer server: dc7cb30b-701a-11e8-bc94-b827eb2bc4f3.
Joining Group Replication group: dc7cb30b-701a-11e8-bc94-b827eb2bc4f3
DEBUG: Setting Group Replication variables
DEBUG: group_replication_group_seeds = cluster-rpi3:33061,cluster-rpi4:33061,cluster-rpi1:33061
DEBUG: MySQL query: SET SQL_LOG_BIN=0
DEBUG: MySQL query: SET PERSIST group_replication_group_seeds = ?, params (u'cluster-rpi3:33061,cluster-rpi4:33061,cluster-rpi1:33061',)
DEBUG: MySQL query: SET SQL_LOG_BIN=1
DEBUG: group_replication_single_primary_mode = 'ON'
DEBUG: MySQL query: SET SQL_LOG_BIN=0
DEBUG: MySQL query: SET PERSIST group_replication_single_primary_mode = 'ON'
DEBUG: MySQL query: SET SQL_LOG_BIN=1
DEBUG: group_replication_group_name = dc7cb30b-701a-11e8-bc94-b827eb2bc4f3
DEBUG: MySQL query: SET SQL_LOG_BIN=0
DEBUG: MySQL query: SET PERSIST group_replication_group_name = ?, params ('dc7cb30b-701a-11e8-bc94-b827eb2bc4f3',)
DEBUG: MySQL query: SET SQL_LOG_BIN=1
DEBUG: group_replication_recovery_use_ssl = 'ON'
DEBUG: MySQL query: SET SQL_LOG_BIN=0
DEBUG: MySQL query: SET PERSIST group_replication_recovery_use_ssl = 'ON'
DEBUG: MySQL query: SET SQL_LOG_BIN=1
DEBUG: auto_increment_offset = 2
DEBUG: MySQL query: SET SQL_LOG_BIN=0
DEBUG: MySQL query: SET PERSIST auto_increment_offset = ?, params (2,)
DEBUG: MySQL query: SET SQL_LOG_BIN=1
DEBUG: group_replication_ssl_mode = 'REQUIRED'
DEBUG: MySQL query: SET SQL_LOG_BIN=0
DEBUG: MySQL query: SET PERSIST group_replication_ssl_mode = 'REQUIRED'
DEBUG: MySQL query: SET SQL_LOG_BIN=1
DEBUG: group_replication_start_on_boot = ON
DEBUG: MySQL query: SET SQL_LOG_BIN=0
DEBUG: MySQL query: SET PERSIST group_replication_start_on_boot = ?, params ('ON',)
DEBUG: MySQL query: SET SQL_LOG_BIN=1
DEBUG: group_replication_local_address = 'cluster-rpi2:33061'
DEBUG: MySQL query: SET SQL_LOG_BIN=0
DEBUG: MySQL query: SET PERSIST group_replication_local_address = 'cluster-rpi2:33061'
DEBUG: MySQL query: SET SQL_LOG_BIN=1
DEBUG: auto_increment_increment = 1
DEBUG: MySQL query: SET SQL_LOG_BIN=0
DEBUG: MySQL query: SET PERSIST auto_increment_increment = ?, params (1,)
DEBUG: MySQL query: SET SQL_LOG_BIN=1
* Running change master command
DEBUG: MySQL query: SET SQL_LOG_BIN=0
DEBUG: MySQL query: CHANGE MASTER TO MASTER_USER = /*(*/ 'mysql_innodb_cluster_r0000124900' /*)*/, MASTER_PASSWORD = /*(*/ '******' /*)*/ FOR CHANNEL 'group_replication_recovery';
DEBUG: MySQL query: SET SQL_LOG_BIN=1
Attempting to join to Group Replication group...
DEBUG:
DEBUG: * Starting Group Replication plugin...
DEBUG: MySQL query: START group_replication
Server 'cluster-rpi2:3306' joined Group Replication group dc7cb30b-701a-11e8-bc94-b827eb2bc4f3.
===========================================================================
The instance 'root@cluster-rpi2:3306' was successfully added to the cluster.
Listing 10-1Using the Verbosity Setting in MySQL Shell
请注意来自 MySQL 供应脚本的许多语句。这是一个隐藏在 shell 中的特殊脚本,用于为您与服务器进行交互。如你所见,很多事情需要去做。如果您更深入地查看该列表,您还会看到关于配置组复制的陈述。现在,如果这还不能让您相信 InnoDB 集群更简单,那就更好了!
服务器部署
我们还没有谈到的一个领域是选择集群中应该有多少台服务器,以及应该如何在您的基础架构中部署它们。本节介绍了在确定您应该开始使用的主(读/写)和辅助(只读)服务器数量时的注意事项,以及物理和网络选择的注意事项。
主服务器
InnoDB 集群的大多数部署都基于默认的单主模式,在任何给定的时间,集群中都有一个主(读/写)服务器。然而,另一种称为多主服务器的模式为组中的多个读/写服务器提供支持。我们使用多主模式进行写入扩展,即将写入划分到两个或更多主模式,以提高写入性能。
在 InnoDB 集群中设置多主节点并没有很好的文档记录,但它是受支持的。联机参考手册的“组复制”一章中包含了大多数多主服务器文档。然而,InnoDB 集群关于多主服务器的文档指出,创建集群时所有服务器都作为读/写服务器参与,这可能不是您想要的。
如果您希望让服务器的子集作为读/写服务器参与,您应该考虑首先使用该配置设置组复制,然后将该配置应用到新集群中。请参阅下一节,了解如何采用“从组复制升级”到 InnoDB 集群。
要在 InnoDB 集群中启用多主模式,我们必须在创建集群时使用multiMaster和force选项,如下所示。同样,这将使所有服务器都能作为读/写服务器参与进来。
dba.create_cluster('RPI_Cluster_MP', {'multiMaster':True, 'force': True})
让我们来看看实际情况。清单 10-2 展示了在多主模式下创建 InnoDB 集群的摘录。请注意,所有服务器都列为读/写服务器。
MySQL cluster-rpi1:3306 ssl Py > cluster = dba.create_cluster('RPI_Cluster_MP', {'multiMaster':True, 'force': True})
...
MySQL cluster-rpi1:3306 ssl Py > cluster.add_instance('root@cluster-rpi4:3306')
...
MySQL cluster-rpi1:3306 ssl Py > cluster.add_instance('root@cluster-rpi3:3306')
...
MySQL cluster-rpi1:3306 ssl Py > cluster.add_instance('root@cluster-rpi2:3306')
...
MySQL cluster-rpi1:3306 ssl Py > cluster.status()
{
"clusterName": "RPI_Cluster_MP",
"defaultReplicaSet": {
"name": "default",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"cluster-rpi1:3306": {
"address": "cluster-rpi1:3306",
"mode": "R/W",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"cluster-rpi2:3306": {
"address": "cluster-rpi2:3306",
"mode": "R/W",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"cluster-rpi3:3306": {
"address": "cluster-rpi3:3306",
"mode": "R/W",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"cluster-rpi4:3306": {
"address": "cluster-rpi4:3306",
"mode": "R/W",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
}
}
},
"groupInformationSourceMember": "mysql://root@cluster-rpi1:3306"
}
Listing 10-2Creating a Multi-Primary Mode Cluster
当您为多主模式集群引导路由时,您会得到一些有趣的结果。以下示例使用多主模式集群引导路由:
$ sudo /usr/local/mysql/bin/mysqlrouter --bootstrap root:root@cluster-rpi4:3306 --user=pi --directory=/usr/local/mysql/lib/test_mp --name=test_mp
Bootstrapping MySQL Router instance at '/usr/local/mysql/lib/test_mp'...
Module “ not registered with logger - logging the following message as 'main' instead
MySQL Router 'test_mp' has now been configured for the InnoDB cluster 'RPI_Cluster_MP' (multi-master).
The following connection information can be used to connect to the cluster.
Classic MySQL protocol connections to cluster 'RPI_Cluster_MP':
- Read/Write Connections: localhost:6446
X protocol connections to cluster 'RPI_Cluster_MP':
- Read/Write Connections: localhost:64460
与单主模式不同,我们只有一个用于读/写连接的端口,没有用于读连接的端口。同样,使用多基色是一个特殊的用例。InnoDB 集群部署的默认和推荐模式是单主模式。
小费
有关在 InnoDB 集群中使用多主模式的更多信息,请参见在线参考手册的“多主模式”一节,该节从组复制的角度描述了多主模式的工作原理。
辅助服务器
集群中需要的辅助(只读)服务器的初始数量被定义为您希望集群能够容忍的不可恢复故障数量的函数。服务器数量(S)等于您想要容忍的故障数量(f)的 2 倍,加上 1:
S = 2f + 1 因此,如果我们希望我们的集群能够容忍最多三个故障,我们应该在集群中至少有七个服务器。
小费
请参阅“如何计算一个组可以处理的故障数量?”第 3 章的侧边栏中有关于该公式的更多解释。
对于读取扩展,该功能是一个良好的开端,但是您可能需要添加更多的只读服务器来满足需求。需要多少将取决于各种因素,这些因素可以用下面的简化公式来概括: 1
AverageLoad = (∑ReadLoad + ∑WriteLoad) / ∑Capacity
服务器上的平均负载是读取负载加上写入负载的总和除以最大容量。这是因为在执行读请求时,每个只读服务器不仅必须满足读请求,还必须满足来自读/写(主)服务器的所有写请求。
让我们考虑一个例子。假设您有一个辅助服务器,通过试验或推荐(目标),它可以每秒处理 20,000 个事务,而没有明显的性能损失。假设集群有一个稳定的平均写入负载,每秒 5,000 个事务,一个只读服务器有一个平均读取负载,每秒 10,000 个读取(读写比为 2:1)。使用前面的公式,我们发现服务器只有 75%的容量,对于大多数应用来说,这是一个安全的操作余量:
(10,000 + 5,000) / 20,000 = 0.75 = 75%
但是,考虑到当有更多读取或写入请求时,平均负载可能会有峰值时间。发生这种情况时,可能会达到容量极限,您可能会遇到服务器之间的延迟。为了防止这种情况,您应该规划只读服务器的数量,使负载保持在 50–75%之间。您可以根据您的应用经历需求高峰期的频率(或是否)来调整该阈值。
那么,这对 InnoDB 集群有什么帮助呢?回想一下,默认情况下,路由循环访问只读服务器,在它们之间分配读取请求。如果我们考虑分配读取的循环算法,我们可以将公式修改如下:
AverageLoad = ((∑ReadLoad/NumSecondaries) + ∑WriteLoad) / ∑Capacity
在这里,我们可以在一组辅助节点上分配读取负载,从而更准确地估计集群的读取容量。回到我们的例子,如果我们有四个辅助服务器,那么只读服务器的平均负载要小得多:
((10,000 / 4) + 5,000) / 20,000 = 0.375 = 37.5%
因此,集群可以安全地处理只读服务器上平均 10,000 次读取。如果您监控平均向群集发出的读取请求的数量,您可以使用该公式来确定平均负载何时大于您选择的阈值,当出现这种情况时,您应该添加更多的只读服务器。
小费
有关读取横向扩展规划的精彩解释,请参见查尔斯·贝尔等人(O'Reilly,2014)在 MySQL 高可用性第二版中的“MySQL 复制横向扩展”。
物理部署
很少考虑参与 InnoDB 集群的服务器计算机的物理位置。最简单的方法是将服务器放在指定的气候控制实验室中,让管理这些实验室的服务员来决定和管理。尽管这对大多数人来说都很好,但你可能需要考虑某些方面。
首先要考虑硬件配置。任何形式的高可用性系统的最佳实践是为所有参与的服务器选择相似的硬件。虽然单个读/写(主)实例确实可能需要更多内存,且可以从更快的处理器中获益,但是这种说法并不适用于 InnoDB 集群,因为集群可以随时决定选择新的主实例。您应该计划使群集中的所有服务器具有相同的硬件配置。
此外,将机器放置在彼此靠近的位置,无论是在同一个机架中,作为同一个刀片服务器阵列的一部分,还是在同一个机架上,都有助于使机器的硬件维修更加容易。如果一台机器坏了,你可以用为该组购买的现有备件来修理它;您可能只需要一套备件。此外,如果你采用备有备用机器的做法(机器没有通电,但准备好交换),你可以这样做,而不必在实验室里拖着硬件到处跑。
网络注意事项
实验室中需要考虑的另一个方面是群集中服务器的网络配置。建议服务器位于自己的子网上,并切换以限制到群集的流量。与集群通信的路由应该能够通过高速连接访问该子网(或者它们也可以在该子网中)。如果这是您想要探索的一个选项,请咨询网络专家,以确保您拥有所需的设备,或者您获得了正确的设备来正确工作。
另一方面,如果您实验室的网络足够快,并且没有网络延迟,您可能要考虑以后再实施。不过,首先规划子网可能有助于避免将子网改造到集群部署中所需的停机时间。
从组复制升级
可能需要仔细规划的最后一个领域是通过使用现有的组复制部署来部署 InnoDB 集群。因为 InnoDB Cluster 使用组复制,所以这应该是一件容易的事情,对吗?答案是这很简单,但是只有当您创建集群时,将名为adoptFromGR的特殊选项设置为True,并使用dba.create_cluster()方法指定。但是,您应该了解三个注意事项。
首先,如果您的组复制拓扑包括 MyISAM(或其他存储引擎)表,您必须将它们转换为 InnoDB。这可以通过ALTER TABLE SQL 命令来完成。
其次,新的 InnoDB 集群将使用组复制模式进行配置。如果组复制是单主复制,InnoDB 集群也应该是单主复制。唯一的问题可能是如果你想改变模式。在这种情况下,从现有的组复制拓扑创建集群时,不可能指定multiMaster选项。
第三,如果任何实例的super_read_only设置为ON,那么当实例被添加到集群时,AdminAPI 会将其设置为OFF。
让我们来看一个组复制拓扑,它作为单独的实例在一台机器上运行(不是在沙箱中)。清单 10-3 显示了在性能模式数据库上使用两个查询的现有组复制拓扑的示例。第一个选项选择组成员,第二个选项标识组中的主要成员。
> SELECT * FROM performance_schema.replication_group_members \G
*************************** 1\. row ***************************
CHANNEL_NAME: group_replication_applier
MEMBER_ID: ab44d8c1-70c0-11e8-9776-d4258b76e981
MEMBER_HOST: oracle-pc
MEMBER_PORT: 24801
MEMBER_STATE: ONLINE
MEMBER_ROLE: PRIMARY
MEMBER_VERSION: 8.0.13
*************************** 2\. row ***************************
CHANNEL_NAME: group_replication_applier
MEMBER_ID: b1bf7839-70c0-11e8-b7ee-d4258b76e981
MEMBER_HOST: oracle-pc
MEMBER_PORT: 24802
MEMBER_STATE: ONLINE
MEMBER_ROLE: SECONDARY
MEMBER_VERSION: 8.0.13
*************************** 3\. row ***************************
CHANNEL_NAME: group_replication_applier
MEMBER_ID: b83fcc48-70c0-11e8-9d84-d4258b76e981
MEMBER_HOST: oracle-pc
MEMBER_PORT: 24803
MEMBER_STATE: ONLINE
MEMBER_ROLE: SECONDARY
MEMBER_VERSION: 8.0.13
*************************** 4\. row ***************************
CHANNEL_NAME: group_replication_applier
MEMBER_ID: be81348f-70c0-11e8-8ce4-d4258b76e981
MEMBER_HOST: oracle-pc
MEMBER_PORT: 24804
MEMBER_STATE: ONLINE
MEMBER_ROLE: SECONDARY
MEMBER_VERSION: 8.0.13
> SELECT member_id, member_host, member_port FROM performance_schema.global_status JOIN performance_schema.replication_group_members ON VARIABLE_VALUE=member_id WHERE VARIABLE_NAME="group_replication_primary_member";
+--------------------------------------+-------------+-------------+
| member_id | member_host | member_port |
+--------------------------------------+-------------+-------------+
| ab44d8c1-70c0-11e8-9776-d4258b76e981 | oracle-pc | 24801 |
+--------------------------------------+-------------+-------------+
Listing 10-3Existing Group Replication Topology
这里的主服务器是当前运行在端口 28401 上的服务器。现在,让我们看看如何将这个组复制拓扑转换为 InnoDB 集群。因为已经设置了组复制,所以我们只需要创建集群。清单 10-4 显示了将名为GR_Cluster的集群的组复制转换为 InnoDB 集群的脚本。
MySQL 192.168.1.80:24801 ssl Py > cluster = dba.create_cluster('GR_Cluster', {'adoptFromGR':True})
A new InnoDB cluster will be created based on the existing replication group on instance 'root@192.168.1.80:24801'.
Creating InnoDB cluster 'GR_Cluster' on 'root@192.168.1.80:24801'...
Adding Seed Instance...
Adding Instance 'oracle-pc:24802'...
Adding Instance 'oracle-pc:24803'...
Adding Instance 'oracle-pc:24804'...
Cluster successfully created based on existing replication group.
MySQL 192.168.1.80:24801 ssl Py > cluster.status()
{
"clusterName": "GR_Cluster",
"defaultReplicaSet": {
"name": "default",
"primary": "oracle-pc:24801",
"ssl": "DISABLED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"oracle-pc:24801": {
"address": "oracle-pc:24801",
"mode": "R/W",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"oracle-pc:24802": {
"address": "oracle-pc:24802",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"oracle-pc:24803": {
"address": "oracle-pc:24803",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"oracle-pc:24804": {
"address": "oracle-pc:24804",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
}
}
},
"groupInformationSourceMember": "mysql://root@192.168.1.80:24801"
}
Listing 10-4Converting an Exiting Group Replication Topology to InnoDB Cluster
create_cluster()方法自动找到实例并为集群配置它们。主服务器仍然是运行在端口 24801 上的服务器。还有一点需要注意:SSL 选项的状态。它目前已被禁用。这是意料之中的,因为组中的服务器运行时没有 SSL。因此,这个示例还演示了您不需要为了在 InnoDB 集群中使用服务器而将它们转换为使用 SSL(但是为了更好的安全性,建议这样做)。
我们还可以在状态输出中看到,我们确实有一个正在工作的集群。酷!因此,您不需要放弃当前的组复制拓扑来采用 InnoDB 集群。事实上,迁移到 InnoDB 集群更容易。
小费
有关使用组复制的更多信息,请参见在线参考手册中的“组复制”一章( https://dev.mysql.com/doc/refman/8.0/en/group-replication.html )。
限制
幸运的是,使用 InnoDB 集群只有一些限制。对于规划 InnoDB 集群部署来说,可能很重要的限制包括以下几点。请注意,该列表很短,适用于非关键领域:
-
包含多字节字符的结果有时可能不会与列输出对齐。
-
非标准字符集可能无法在结果中正确显示。
-
AdminAPI 不支持 UNIX 套接字连接。
-
将非沙箱服务器实例添加到沙箱中的集群可能会阻止 MySQL Shell 将配置更改保存在实例的配置文件中。
-
InnoDB 集群服务器实例不支持使用
--defaults-extra-file选项来指定选项文件。
小费
参见 https://dev.mysql.com/doc/refman/8.0/en/mysql-innodb-cluster-limitations.html 了解最新的限制列表和可能的解决方法。您还应该看到“组复制限制”一节,了解可能影响 InnoDB 集群的限制。
MySQL 8 的规划
虽然这本书不是升级到 MySQL 8 的教程,但在采用 MySQL InnoDB Cluster 之前,您应该考虑一些事情,这将很可能导致升级您现有的 MySQL 服务器。
有几种方法可以学习如何进行升级。最明显和推荐的途径是阅读在线参考手册,其中包含一个关于升级 MySQL 的章节(提供您必须知道的关键信息)。但是,一些更高级的或一般的实践适用于任何形式的升级或迁移。本节介绍的升级实践将帮助您避免升级像 MySQL 这样的主要系统时遇到的一些麻烦。
本节将介绍 MySQL 的升级类型,以及规划和执行升级的一般实践。我们以关于执行升级的原因的简短讨论来结束本节。我们将在最后讨论进行升级的原因,以便您能够更好地理解升级所涉及的内容,包括隐含的风险。
让我们先来看看您可能会遇到的升级类型。
升级类型
在线参考手册和类似出版物描述了两种基本的升级方法,即如何进行升级的策略和步骤。下面是这些方法的总结:
-
就地 : MySQL 服务器实例通过使用现有的数据字典用二进制文件升级。这种方法使用各种实用程序和工具来确保平稳过渡到新版本。
-
逻辑:在旧安装上安装新版本之前,数据被备份,升级之后,数据被恢复。
这两个升级 MySQL 的通用策略并没有涵盖所有可能的选项。事实上,您将在后面的章节中看到另一种方法。毕竟,您的安装可能会略有不同——特别是如果您已经使用 MySQL 很长时间了,或者有许多配置了高可用性的 MySQL 服务器,或者在您自己的应用中使用第三方应用和组件。这些因素会使遵循一个给定的、通用的程序成为问题。
与其尝试扩展升级方法,不如让我们从系统管理员的角度来看一下。具体来说,如果我们有 x.y.z 版本,想升级到 a.b.c,该怎么做?以下部分描述了基于版本的升级。
警告
Oracle 建议仅升级 GA 版本。不建议升级其他版本,这可能需要额外的时间进行迁移,并接受潜在的不兼容性。升级非 GA 版本的风险由您自行承担。
MySQL 版本号术语
MySQL 使用三位数的版本号,以主要版本号、次要版本号和修订版本号的形式表示(奇怪的是,它在文档中也被称为版本)。这通常用点符号表示。例如,版本 5.7.20 将主版本定义为 5,次版本定义为 7,修订版定义为 20。通常,版本号后面是文本(在文档中称为后缀),表示额外的版本历史、稳定性或一致性——例如通用可用性(GA)、发布候选(RC)等等。关于 MySQL 中版本号的完整解释,见 https://dev.mysql.com/doc/refman/8.0/en/which-version.html 。
版本升级
在最简单的升级形式中,只更改修订版号。这通常被称为 x.y.z 版本号中的 z,或者简称为主版本号的版本。例如,版本 5.7.20 是修订版 20,或 5.7 的版本 20。
在此版本级别升级通常是安全的,尽管不能保证工作完美,但风险很低。但是,您仍然应该在执行升级之前阅读发行说明。如果您正在使用非公开发行(GA)版本,这一点尤其正确。如果该版本不是 GA 版本,您必须注意参考手册中的发行说明和升级部分。尽管这种情况很少发生,但有时还是存在一些特殊的考虑因素,您必须为实现升级做好准备并克服这些因素。幸运的是,Oracle 在传达任何必要的步骤和过程方面做得非常好;你只需要阅读文档!例如,请参见 https://dev.mysql.com/doc/relnotes/mysql/8.0/en/ 的发行说明,以了解更多关于从一个版本到另一个版本的变更。
小升级
在下一次升级中,次要编号会发生变化。这通常被称为 x.y.z 版本号中的 y。例如,您可能从 5.6 升级到 5.7。
对于次要版本的个位数增量,升级通常是可接受的,并有记录。例如,支持从 5.6 升级到 5.7,但不直接支持从 5.0 升级到 5.7。这是因为版本之间存在太多差异,使得升级不可行(但并非不可能)。
然而,如果您有相应的计划,您可以升级较小的版本更改,风险可控。您将在后面的章节中看到更多关于管理风险的内容。
重大升级
在下一次升级中,主版本号会发生变化。除了不兼容的版本之外,这个类别是风险最大的类别,也是最有可能需要更多工作的类别。
主版本的版本升级很少发生,只有当 Oracle 发布了对服务器的一组新的、主要的更改(因此得名)时才会发生。MySQL Server 版本 8 包含了对 MySQL 5 的许多改进。大多数都带来了性能、高级特性和稳定性的巨大提高。但是,一些变化使得旧版本中的一些功能不兼容。
例如,MySQL 8.0 作为 GA 发布后,支持从 MySQL 5.7 升级到 MySQL 8.0,但您可能需要迁移某些功能才能完成升级。
幸运的是,Oracle 已经详细记录了所有问题领域,为迁移到新特性提供了建议。
不兼容的升级
正如您可能已经猜到的那样,有些升级是不推荐的,要么是因为缺少支持升级的功能,要么是因为主要的不兼容性。比如你不要考虑从 MySQL 5.0 升级到 MySQL 8.0。这仅仅是因为 8.0 不支持一些较老的 5.0 特性。这些类型的升级并不常见,因此我们在下面的列表中总结了一些不兼容的升级。不兼容的原因不是您要升级到的新版本;您要升级的是旧版本。
-
跳过主要版本:升级主要版本可能会引入不兼容的变更。
-
跳过次要版本:次要版本的某些升级可能会引入不兼容的变更。
-
升级不兼容的硬件:升级一种字节序的硬件可能与另一种不兼容。例如,big-endian 到 little-endian 可能不兼容。
-
更改 InnoDB 格式的版本:一些升级会导致 InnoDB 存储引擎内部发生变化。大多数计划用于兼容的次要修订版升级(例如,从 5.7.3 到 5.7.12),但有些需要一些额外的步骤来准备数据。
-
新特性:新特性的引入很少会导致不兼容。例如,添加了数据字典,使得
.frm元数据过时。 -
平台变更:包括变更平台在内的一些升级可能需要额外的工作或引入潜在的不兼容性。例如,从文件系统中不支持区分大小写的平台转移到支持区分大小写的平台。
-
升级非正式发布版本:不建议从非正式发布版本升级到正式发布版本,从正式发布版本升级到非正式发布版本,以及在非正式发布版本之间升级。
显然,不兼容性取决于某些功能、硬件或内部存储机制。在大多数情况下,在线文档概述了您可以做些什么来确保成功。有时这需要遵循特定的升级路径,例如在升级到目标版本之前先升级到一个版本。
如果我必须升级不兼容的版本怎么办?
如果您的升级策略属于这些不兼容升级类别之一,不要绝望。您可能仍然能够执行升级,但它可能更昂贵,需要更多的工作。例如,您可以通过使用带有mysqldump或mysqlpump的 SQL 语句来备份数据,安装新版本,然后使用 SQL 文件来调整它们以消除任何不兼容性,从而执行逻辑升级。尽管这确实带来了相当大的风险,即您仍然可以干净地导入所有数据,但这仍然是可能的。如果您发现自己处于这种情况,一定要花更多的时间使用并行安装和延长测试时间等策略来解决风险。
既然您对可能的升级类型有了很好的了解,那么让我们来看看执行升级的一些最佳实践。
升级实践
在升级任何系统时,我们都应该遵循一般惯例,或者至少将它们作为指南。本节描述了升级 MySQL 服务器时应该考虑的一些基本实践。虽然其中一些可能是熟悉的,但其他的可能不是您在升级 MySQL 时会考虑的。此外,在线参考手册中没有列出其中的一些内容。
正如您将看到的,这些实践不一定是下一个实践的顺序,甚至不一定是下一个实践的先决条件。例如,计划还应该包括测试时间。这里讨论的实践按照重要性的一般顺序排列,但是不应该按照这个顺序来考虑或实现。
检查先决条件
升级 MySQL 时,您应该做的第一件事是查看文档以了解任何先决条件。有时前提条件只是安全地备份数据,但也可能包括一些因素,如迁移某些功能(或数据)所需的实用程序和工具。在开始升级之前,请确保满足所有先决条件。
升级文档还将包括不兼容问题。这种情况通常发生在升级主要版本时,但有时次要版本也会出现不兼容的情况。幸运的是,在线参考手册中概述了这些内容。检查先决条件还可以帮助您提供可用于规划升级的详细信息。
警告
出现问题时,在线参考手册中关于升级的部分应该是您的第一站,而不是最后一站。阅读升级部分和发行说明可以帮助您在升级过程中避免不必要的返工和问题。
一旦通读了文档,作为先决条件,您需要做的一件事就是使用mysqlcheck实用程序来检查 MySQL 安装的兼容性。例如,升级到 MySQL 8 的一个先决条件是,根据在线参考手册,“不能有使用过时数据类型的表、过时函数、孤立的.frm文件、使用非本机分区的 InnoDB 表、缺少或空定义器的触发器或无效的创建上下文。”我们可以使用mysqlcheck实用程序来识别这些情况,如清单 10-5 所示。
$ mysqlcheck -u root -p --all-databases --check-upgrade
Enter password:
library_v1.authors OK
library_v1.books OK
library_v1.books_authors OK
library_v1.notes OK
library_v1.publishers OK
library_v2.books OK
library_v2.notes OK
library_v2.publishers OK
library_v3.books OK
...
mysql.user OK
sys.sys_config OK
Listing 10-5Using mysqlcheck to Identify Upgrade Issues
为了获得最佳结果,您应该使用您想要升级到的版本的mysqlcheck实用程序。这将确保该实用程序是最新的,并应识别更多的升级问题。
规划升级
当您规划好所有的先决条件并确定了需要特殊处理来解决不兼容问题的任何功能后,就该计划升级服务器了。如果您有数千台服务器,这可能是一件显而易见的事情,但对于只有几台(甚至一台)服务器需要升级的人来说,这就不那么明显了。
您应该抵制简单地运行升级而不计划要做什么的诱惑。我们希望通过降低(或消除)风险来确保升级顺利进行。这对于生产环境来说更加重要,但是任何潜在的可用性、性能或数据损失都会导致生产效率的损失。
您可以从文档中获得大部分需要规划的内容,但是文档并不特定于您的安装、服务器、平台等等。您必须填写这些空白,并根据您自己的安装修改文档中建议的步骤。通过阅读“MySQL 8.0 中的新特性”( https://dev.mysql.com/doc/refman/8.0/en/mysql-nutshell.html )一节,并关注《服务器在线参考手册》中标有“升级的后果”的小节,您可以学到很多东西。在那里,你会找到一些技巧,可以帮助你避免复杂的决定,或者更好地,避免复杂的维修。
这一步还包括确保您手头有合适的人员来进行升级,或者做好准备在出现问题时参与进来。 2 例如,不要忘记你的开发者、网站管理员和其他关键角色。
计划的形式由你决定;然而,建议你把你打算做的事情写下来,并与他人分享。这样,升级所有权链中的每个人都知道要做什么。你会惊讶地发现,小小的交流可以大大降低事情出错的风险。
警告
如果您正在使用或计划使用支持自动更新的平台,并且这些设施包括监控 MySQL 的存储库,那么您可能要考虑将 MySQL 排除在自动更新之外。对于生产环境来说尤其如此。您不应该在生产环境中为任何关键任务数据自动更新 MySQL。
记录您的结果
一些最优秀的管理者、建筑师和规划者拥有的品质之一是他们把一切都写下来。他们记下他们所尝试的和结果。这既包括正确的事情,也包括错误的事情。你应该总是考虑为你所有的项目保留一个工程师的笔记本。
有些人保留一个笔记本,按时间顺序记录笔记。其他人可能会保留几个笔记本,每个笔记本都专注于一个特定的项目或实验。笔记本不必是豪华的、用皮革包裹的、有三重安全锁的大部头,但它应该是典型的笔记本,因为它的书页不可移动,而且足够小,可以放在你的工作站旁边。
强烈建议您使用墨水来记录您的想法、实验、设置、配置和观察,这样您就不会试图删除它们。如果你绘图,你应该考虑使用铅笔,因为绘图经常改变或可能需要修改的准确性。你应该用日期和可选的时间标记你所有的条目。
此外,一定要把你的笔记本放在附近,这样你就不会想“以后再写下来”有时,诊断或实验时的激情会占据上风。只有常规和习惯才能确保你的笔记完整。
最后,要注意笔记本中信息的安全性。例如,如果您正在处理的项目包含不应被其他人看到的公司私人信息或其他信息,您应该将笔记本存放在公司政策为此类信息指定的地方。如果您无法做到这一点,或者您的公司政策中没有这样的机制,您应该避免写下任何此类信息。虽然这可能会使笔记不完整,但如果有人偶然发现你的笔记本上没有这些数据,这比偶然分享这些信息要好得多。 3
考虑并行部署
在升级需要大量工作的系统时,最有帮助的一种做法是与现有版本并行安装新版本。这是软件工程中的一种做法,旨在确保在安装和配置新系统时,现有的数据和应用保持不变。新版本(安装)将被视为一个开发平台,并且通常在完成充分的迁移测试后进行生产部署。
虽然这本身并不是一次升级(这是一次新的安装),但是并行运行 MySQL 的新版本在如何解决现有数据和应用的迁移方面提供了相当大的自由度。毕竟,如果出现问题,您的数据在旧系统上仍然是可操作的。
这种做法还提供了另一个好处:您可以更改平台或其他主要硬件,而不必拿现有数据冒险。如果您现有的服务器有要同时更新的硬件,您可以使用并行安装在新硬件上安装 MySQL,从而隔离新硬件的风险。
最后,采用并行安装可以确保现有系统完全能够运行,从而帮助您安排和规划迁移。更好的是,如果在迁移过程中出现问题,您可以随时回到旧系统。
并行部署通常包括让两个系统都运行一段时间。时间的长短可能取决于您愿意承担的风险大小,或者完全切换所有应用所需的时间。
不幸的是,有些人可能没有资源来考虑并行部署。考虑到同时运行两个 MySQL 安装可能会给开发者、管理员和支持人员带来更大的负担。考虑到并行开发的好处,在短时间内增加额外的资源或接受一些人员的低生产率可能是值得的。
然而,如果你没有进行足够的测试,即使这个安全网也是脆弱的。
测试测试测试!
测试和计划一起,经常被忽视或者被给予远远低于它应有的重要性。有时这是由于外部因素造成的,例如没有合适的人员可用,或者计划失败导致没有时间进行广泛的测试。不管借口是什么,未能充分测试您的升级会增加超出大多数人愿意忍受的风险。
测试应包括确保所有数据都已迁移,所有应用都能正常工作,以及所有访问权限(用户帐户、权限等)。)是功能性的。然而,不要就此止步。您还应该确保您的所有操作实践都已针对新版本进行了修改。更具体地说,您的维护脚本、过程和工具都可以在新版本中正常工作。
此外,您的测试应该导致接受升级的通过/不通过决定。如果事情不工作或有太多的问题,您可能需要决定保留或拒绝升级。并行安装实践可以以这种方式提供帮助,因为在确定一切正常之前,您不会破坏现有的数据或安装。将这些标准写入你的计划将确保成功。
小费
确保测试所有现有的操作程序,作为验收标准的一部分。
生产部署策略
如果您有一个生产和开发(或测试)环境,您还应该考虑如何将开发或测试部署转移到生产环境中。如果您使用并行安装,您可能会切换应用路由和类似的设备和应用。如果您使用就地安装,这个过程可能会更复杂。例如,您可能需要计划一段停机时间来完成迁移。
对于并行安装,计划停机时间可能更精确,并且涉及的时间更短,因为您有更多的时间来测试。但是,对于就地升级,您可能需要留出一段时间来完成迁移。自然,您会希望通过尽可能多地进行迁移来最大限度地减少停机时间。但在 MySQL 的基地里,这可能无非就是形成一个计划,聚集资源。底线是,不要放弃在你的计划中包括生产部署。
既然我们已经讨论了升级实践,那么让我们花点时间来讨论执行升级的原因,这显然是一个具有一定风险的复杂过程。
升级的原因
如果您像大多数平台或系统的狂热用户一样,每当有新版本发布时,您都会希望升级到最新、最好的版本。精明的管理员和规划者知道,在生产数据库环境中,这种行为几乎没有存在的空间。升级的理由将需要一些真正物有所值的东西。升级一定是值得的。升级 MySQL 的主要原因包括以下几点:
-
特性:发布了一项新特性,可以改进您的应用或数据。示例包括文档存储、组复制和 InnoDB 集群。
-
性能:新版本提高了性能,使您的应用更好。例如,最新的 5.7 版本比以前的版本快很多倍,MySQL 8 有望在此基础上有所改进。
-
维护:新功能可以帮助你更好的维护系统。示例包括新的数据字典、组复制和 MySQL 企业备份等辅助工具。
-
错误修复:旧版本中的缺陷可能需要解决方法或限制。较新的版本可能包含对关键错误的修复,因此您可以删除由缺陷引起的变通办法和限制。
-
合规性:您的平台、标准操作程序或外部实体需要升级以实现合规性。例如,根据合同协议,您可能需要运行特定版本的 MySQL。
底线是你必须回答这个问题,“我为什么要升级?”这个答案必须给你、你的数据、客户、员工和公司的未来带来好处。将资源花费在几乎没有或根本没有好处的升级上是没有意义的,这也是公司经常跳过版本升级的另一个原因。唉,跳过太多升级会让以后的升级更成问题。然而,鉴于 MySQL 8.0 与 MySQL 5.7 和更早版本相比的所有改进,许多人会想升级到 MySQL 8。
小费
有关迁移到 MySQL 8 的更多详细信息,包括特定平台的步骤,请参见 http://dev.mysql.com/doc/refman/8.0/en/upgrading-from-previous-series.html 。
那么,我到底该不该升级到 MySQL 8?
本节的讨论可能会对您是否应该升级到 MySQL 8 产生一些疑问。那不是我的意图。这本书应该说服你尽快升级到 MySQL 8,只要你能以一种安全、无风险的方式升级。本节建议您需要仔细规划和执行升级,以确保成功。如果你现在开始迁移到 MySQL 8,等到 MySQL 8 有了正式发布的修订版或小升级版,你就可以升级了。
升级到 MySQL 8 的注意事项
MySQL 8.0 的在线参考手册中指出了几个兼容性问题。以下是您在规划 MySQL 8.0 升级时应该注意的一些事项:
-
数据字典:新的元数据、事务存储机制是架构中的一个重大变化。如果您有处理
.frm文件和其他元数据的 DevOps,您可能需要进行更改以迁移到使用数据字典。 -
认证插件:默认认证插件已经改变。这可能会给使用旧身份验证机制的用户带来连接问题。
-
错误代码:部分错误代码已更改。如果您的应用使用了错误代码,请研究这些更改以避免升级后出现应用错误。
-
分区:默认的分区存储引擎支持已经被移除。如果您使用的是定制存储引擎(或旧引擎),请确保存在可用于 MySQL 8 的升级版本。
-
INFORMATION_SCHEMA :对视图做了微小的修改。如果您的应用或开发团队使用这些视图,请确保检查您正在使用的任何视图是否已被删除或更改。
-
SQL 命令:这个版本有一些新的和过时的 SQL 命令。请务必检查您的 SQL 语句,看看您是否在使用一些旧的、已删除的命令。
-
默认字符集:默认字符集已更改为
utf8mb4。如果您的应用支持字符集,您可能需要使用新的默认值进行测试,以确保兼容性。
同样,请务必阅读在线参考手册的“验证 MySQL 5.7 安装的升级先决条件”一节,以及“影响 MySQL 8.0 升级的更改”一节,了解升级到 MySQL 8.0 所需的这些和其他先决条件和迁移任务的最新信息。
其他优秀的资源还有 https://mysqlserverteam.com /的工程博客。这些博客通常在新功能正式发布之前就对其进行讨论,是关于这些功能如何工作以及工程团队已经发现或正在努力解决的任何升级问题的知识源泉。关注博客将会给变化一个很好的预警。
摘要
规划 InnoDB 集群安装的任务并不太复杂,但确实需要一些深谋远虑和周密的计划。与任何技术一样,我们应该从一个简单的实验装置开始,尽可能多地测试该技术的各个方面。这不仅包括设置集群的机制,还包括配置路由和应用以用于集群的细节。更重要的是,我们必须记录我们所有的结果——包括错误和不起作用的事情——以便我们在为生产部署 InnoDB Cluster 时能够避免它们。
在本章中,您了解了规划 InnoDB 集群的一些重要注意事项,包括规划 MySQL 8 的采用。这就完成了您对 InnoDB 集群的介绍。有了这些关于 InnoDB 集群工作方式的新知识,您就完全可以在自己的环境中采用 MySQL InnoDB 集群了。
对于 MySQL 用户来说,这是一个激动人心的时刻。Oracle 继续信守承诺,不仅继续开发 MySQL,还投入资源改进和扩展特性集。请密切关注更多优秀的特性以及进一步的改进和更新。MySQL 8 已经推出,现在是时候加入了。在 MySQL 8 上寻找更多的标题!
Footnotes [1](#Fn1_source)这个公式是简化的,因为它是一个近似的工具,而不是规范的工具。相应地使用它。
对于数据库或 web 管理员来说,接到电话(通常是在半夜)来解决升级中出现的问题总是令人震惊的——尤其是当他们不知道这样的升级是有计划的时候!是的,它确实发生了,太频繁了。
对于工程师为什么不使用笔记本电脑,我看到的最常见的回答是,他们不想冒暴露数据的风险。但是,再次强调,注意并建立或遵循数据保护政策有助于克服这种恐惧。