对于MySQL高可用方案,当前比较火的一个技术产品是orchestrator,本文不打算介绍它的整体功能及架构,相关内容可以在https://github.com/openark/orchestrator上查看手册。本文会详细介绍orchestrator discovery功能的大体流程,帮助大家理解orchestrator是怎么对一个实例、集群进行信息收集和维护的。
database_instance表
CREATE TABLE `database_instance` (`hostname` varchar(128) NOT NULL,`port` smallint(5) unsigned NOT NULL,`last_checked` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,`last_attempted_check` timestamp NOT NULL DEFAULT '1971-01-01 00:00:00',`last_check_partial_success` tinyint(3) unsigned NOT NULL,`last_seen` timestamp NULL DEFAULT NULL,`uptime` int(10) unsigned NOT NULL,`server_id` int(10) unsigned NOT NULL,`server_uuid` varchar(64) NOT NULL,`version` varchar(128) NOT NULL,`binlog_server` tinyint(3) unsigned NOT NULL,`read_only` tinyint(3) unsigned NOT NULL,`binlog_format` varchar(16) NOT NULL,`log_bin` tinyint(3) unsigned NOT NULL,`log_slave_updates` tinyint(3) unsigned NOT NULL,`binary_log_file` varchar(128) NOT NULL,`binary_log_pos` bigint(20) unsigned NOT NULL,`master_host` varchar(128) NOT NULL,`master_port` smallint(5) unsigned NOT NULL,`slave_sql_running` tinyint(3) unsigned NOT NULL,`slave_io_running` tinyint(3) unsigned NOT NULL,`replication_sql_thread_state` tinyint(4) NOT NULL DEFAULT '0',`replication_io_thread_state` tinyint(4) NOT NULL DEFAULT '0',`has_replication_filters` tinyint(3) unsigned NOT NULL,`oracle_gtid` tinyint(3) unsigned NOT NULL,`master_uuid` varchar(64) NOT NULL,`ancestry_uuid` text NOT NULL,`executed_gtid_set` text NOT NULL,`gtid_purged` text NOT NULL,`gtid_errant` text NOT NULL,`supports_oracle_gtid` tinyint(3) unsigned NOT NULL,`mariadb_gtid` tinyint(3) unsigned NOT NULL,`pseudo_gtid` tinyint(3) unsigned NOT NULL,`master_log_file` varchar(128) NOT NULL,`read_master_log_pos` bigint(20) unsigned NOT NULL,`relay_master_log_file` varchar(128) NOT NULL,`exec_master_log_pos` bigint(20) unsigned NOT NULL,`relay_log_file` varchar(128) NOT NULL,`relay_log_pos` bigint(20) unsigned NOT NULL,`last_sql_error` text NOT NULL,`last_io_error` text NOT NULL,`seconds_behind_master` bigint(20) unsigned DEFAULT NULL,`slave_lag_seconds` bigint(20) unsigned DEFAULT NULL,`sql_delay` int(10) unsigned NOT NULL,`allow_tls` tinyint(3) unsigned NOT NULL,`num_slave_hosts` int(10) unsigned NOT NULL,`slave_hosts` text NOT NULL,`cluster_name` varchar(128) NOT NULL,`suggested_cluster_alias` varchar(128) NOT NULL,`data_center` varchar(32) NOT NULL,`physical_environment` varchar(32) NOT NULL,`instance_alias` varchar(128) NOT NULL,`semi_sync_enforced` tinyint(3) unsigned NOT NULL,`replication_depth` tinyint(3) unsigned NOT NULL,`is_co_master` tinyint(3) unsigned NOT NULL,`replication_credentials_available` tinyint(3) unsigned NOT NULL,`has_replication_credentials` tinyint(3) unsigned NOT NULL,`version_comment` varchar(128) NOT NULL DEFAULT '',`major_version` varchar(16) NOT NULL,`binlog_row_image` varchar(16) NOT NULL,`last_discovery_latency` bigint(20) NOT NULL,`semi_sync_master_enabled` tinyint(3) unsigned NOT NULL,`semi_sync_replica_enabled` tinyint(3) unsigned NOT NULL,`gtid_mode` varchar(32) NOT NULL,PRIMARY KEY (`hostname`,`port`),KEY `cluster_name_idx_database_instance` (`cluster_name`),KEY `last_checked_idx_database_instance` (`last_checked`),KEY `last_seen_idx_database_instance` (`last_seen`),KEY `master_host_port_idx_database_instance` (`master_host`,`master_port`),KEY `suggested_cluster_alias_idx_database_instance` (`suggested_cluster_alias`)) ENGINE=InnoDB DEFAULT CHARSET=ascii
这个表是orchestrator的一个非常重要的表,用来保存探测实例的信息,为后续判断实例是否存活提供基数据。
探测机制
func ContinuousDiscovery() {//省略若干行代码go handleDiscoveryRequests()//省略若干行代码}
ContinuousDiscovery()函数是主要功能入口函数,它实现了异步发现、实例信息收集、探测实例失败、执行拓扑恢复等功能。handleDiscoveryRequests()实现并发执行discover的功能函数。
// handleDiscoveryRequests iterates the discoveryQueue channel and calls upon// instance discovery per entry.func handleDiscoveryRequests() {discoveryQueue = discovery.CreateOrReturnQueue("DEFAULT")// create a pool of discovery workersfor i := uint(0); i < config.Config.DiscoveryMaxConcurrency; i++ {go func() {for {instanceKey := discoveryQueue.Consume()// Possibly this used to be the elected node, but has// been demoted, while still the queue is full.if !IsLeaderOrActive() {log.Debugf("Node apparently demoted. Skipping discovery of %+v. "+"Remaining queue size: %+v", instanceKey, discoveryQueue.QueueLen())discoveryQueue.Release(instanceKey)continue}DiscoverInstance(instanceKey)discoveryQueue.Release(instanceKey)}}()}}
会创建一个discoveryQueue进行discover,开启DiscoveryMaxConcurrency(默认300)个并发执行。
instanceKey := discoveryQueue.Consume()从队列中取一个instanceKey,
type InstanceKey struct {Hostname stringPort int}
instanceKey是InstanceKey类型的结构体对象,包括Hostname、Port成员。取得instanceKey后会传递给DiscoverInstance(instanceKey)进行处理。
// DiscoverInstance will attempt to discover (poll) an instance (unless// it is already up to date) and will also ensure that its master and// replicas (if any) are also checked.func DiscoverInstance(instanceKey inst.InstanceKey) {//省略若干行代码// First we've ever heard of this instance. Continue investigation:instance, err = inst.ReadTopologyInstanceBufferable(&instanceKey, config.Config.BufferInstanceWrites, latency)// Investigate replicas:for _, replicaKey := range instance.SlaveHosts.GetInstanceKeys() {replicaKey := replicaKey // not needed? no concurrency here?if replicaKey.IsValid() {discoveryQueue.Push(replicaKey)}}// Investigate master:if instance.MasterKey.IsValid() {discoveryQueue.Push(instance.MasterKey)}}
分析DiscoverInstance中重要的代码,ReeadTopologyInstanceBufferable会返回Instance类型数据,它是一个实例的具体描述。详细成员字段内容请参考https://github.com/openark/orchestrator/blob/master/go/inst/instance.go Instance结构体。
type Instance struct {Key InstanceKeyInstanceAlias stringUptime uintServerID uintServerUUID stringVersion string//省略若干行}
获得一个instance对象后,会把这个instance下的slaves和master依次Push到队列中,以方便对集群相关成员进行discover,这也是为什么我们把一个集群的单个实例加入orchestrator后,它很快就能发现其他实例信息的原因。
ReadTopologyInstanceBufferable这个函数的内容比较多,我们分块介绍些。详细内容请参考:
https://github.com/openark/orchestrator/blob/master/go/inst/instance_dao.go
func ReadTopologyInstanceBufferable(instanceKey *InstanceKey, bufferWrites bool, latency *stopwatch.NamedStopwatch) (*Instance, error) {//省略若干行//初始化instanceinstance := NewInstance()//把实例的Uptime保存到instance.Uptime字段var dummy string// show global status works just as well with 5.6 & 5.7 (5.7 moves variables to performance_schema)err = db.QueryRow("show global status like 'Uptime'").Scan(&dummy, &instance.Uptime)//把实例的hostname、server_id、version等信息存入instance对象var mysqlHostname, mysqlReportHost stringerr = db.QueryRow("select @@global.hostname, ifnull(@@global.report_host, ''), @@global.server_id, @@global.version, @@global.version_comment, @@global.read_only, @@global.binlog_format, @@global.log_bin, @@global.log_slave_updates").Scan(&mysqlHostname, &mysqlReportHost, &instance.ServerID, &instance.Version, &instance.VersionComment, &instance.ReadOnly, &instance.Binlog_format, &instance.LogBinEnabled, &instance.LogSlaveUpdatesEnabled)if err != nil {goto Cleanup}//如果开启了LogBinEnabled 则把logfile、logpos、Executed_Gtid_Set信息存入instance实例go func() {defer waitGroup.Done()err = sqlutils.QueryRowsMap(db, "show master status", func(m sqlutils.RowMap) error {var err errorinstance.SelfBinlogCoordinates.LogFile = m.GetString("File")instance.SelfBinlogCoordinates.LogPos = m.GetInt64("Position")instance.ExecutedGtidSet = m.GetStringD("Executed_Gtid_Set", "")return err})}()//如果实例开启了半同步,则把半同步信息保存到instancewaitGroup.Add(1)go func() {defer waitGroup.Done()err = sqlutils.QueryRowsMap(db, "show global status like 'rpl_semi_sync_%_status'", funsqlutils.RowMap) error {if m.GetString("Variable_name") == "Rpl_semi_sync_master_status" {instance.SemiSyncMasterEnabled = (m.GetString("Value") == "ON")} else if m.GetString("Variable_name") == "Rpl_semi_sync_slave_status" {instance.SemiSyncReplicaEnabled = (m.GetString("Value") == "ON")}return nil})}()//读取实例中gtid_mode、gtid_purged等信息存入instance对象。go func() {defer waitGroup.Done()var masterInfoRepositoryOnTable bool// Stuff only supported on Oracle MySQL >= 5.6// ...// @@gtid_mode only available in Orcale MySQL >= 5.6// Previous version just issued this query brute-force, but I don't like errors being issued where they shouldn't._ = db.QueryRow("select @@global.gtid_mode, @@global.server_uuid, @@global.gtid_purged, @@global.master_info_repository = 'TABLE', @@global.binlog_row_image").Scan(&instance.GTIDMode, &instance.ServerUUID, &instance.GtidPurged, &masterInfoRepositoryOnTable, &instance.BinlogRowImage)if instance.GTIDMode != "" && instance.GTIDMode != "OFF" {instance.SupportsOracleGTID = true}if config.Config.ReplicationCredentialsQuery != "" {instance.ReplicationCredentialsAvailable = true} else if masterInfoRepositoryOnTable {_ = db.QueryRow("select count(*) > 0 and MAX(User_name) != '' from mysql.slave_master_info").Scan(&instance.ReplicationCredentialsAvailable)}}()//执行show slave status信息,把相关内容保存到instance实例err = sqlutils.QueryRowsMap(db, "show slave status", func(m sqlutils.RowMap) error {instance.HasReplicationCredentials = (m.GetString("Master_User") != "")instance.Slave_IO_Running = (m.GetString("Slave_IO_Running") == "Yes")if isMaxScale110 {// Covering buggy MaxScale 1.1.0instance.Slave_IO_Running = instance.Slave_IO_Running && (m.GetString("Slave_IO_State") == "Binlog Dump")}instance.Slave_SQL_Running = (m.GetString("Slave_SQL_Running") == "Yes")instance.ReadBinlogCoordinates.LogFile = m.GetString("Master_Log_File")instance.ReadBinlogCoordinates.LogPos = m.GetInt64("Read_Master_Log_Pos")instance.ExecBinlogCoordinates.LogFile = m.GetString("Relay_Master_Log_File")instance.ExecBinlogCoordinates.LogPos = m.GetInt64("Exec_Master_Log_Pos")instance.IsDetached, _ = instance.ExecBinlogCoordinates.ExtractDetachedCoordinates()instance.RelaylogCoordinates.LogFile = m.GetString("Relay_Log_File")instance.RelaylogCoordinates.LogPos = m.GetInt64("Relay_Log_Pos")instance.RelaylogCoordinates.Type = RelayLoginstance.LastSQLError = emptyQuotesRegexp.ReplaceAllString(strconv.QuoteToASCII(m.GetString("Last_SQL_Error")), "")instance.LastIOError = emptyQuotesRegexp.ReplaceAllString(strconv.QuoteToASCII(m.GetString("Last_IO_Error")), "")instance.SQLDelay = m.GetUintD("SQL_Delay", 0)instance.UsingOracleGTID = (m.GetIntD("Auto_Position", 0) == 1)instance.UsingMariaDBGTID = (m.GetStringD("Using_Gtid", "No") != "No")instance.MasterUUID = m.GetStringD("Master_UUID", "No")instance.HasReplicationFilters = ((m.GetStringD("Replicate_Do_DB", "") != "") || (m.GetStringD("Replicate_Ignore_DB", "") != "") || (m.GetStringD("Replicate_Do_Table", "") != "") || (m.GetStringD("Replicate_Ignore_Table", "") != "") || (m.GetStringD("Replicate_Wild_Do_Table", "") != "") || (m.GetStringD("Replicate_Wild_Ignore_Table", "") != ""))masterHostname := m.GetString("Master_Host")if isMaxScale110 {// Buggy buggy maxscale 1.1.0. Reported Master_Host can be corrupted.// Therefore we (currently) take @@hostname (which is masquarading as master host anyhow)masterHostname = maxScaleMasterHostname}masterKey, err := NewResolveInstanceKey(masterHostname, m.GetInt("Master_Port"))if err != nil {logReadTopologyInstanceError(instanceKey, "NewResolveInstanceKey", err)}masterKey.Hostname, resolveErr = ResolveHostname(masterKey.Hostname)if resolveErr != nil {logReadTopologyInstanceError(instanceKey, fmt.Sprintf("ResolveHostname(%q)", masterKey.Hostname), resolveErr)}instance.MasterKey = *masterKeyinstance.IsDetachedMaster = instance.MasterKey.IsDetached()instance.SecondsBehindMaster = m.GetNullInt64("Seconds_Behind_Master")if instance.SecondsBehindMaster.Valid && instance.SecondsBehindMaster.Int64 < 0 {log.Warningf("Host: %+v, instance.SecondsBehindMaster < 0 [%+v], correcting to 0", instanceKey, instance.SecondsBehindMaster.Int64)instance.SecondsBehindMaster.Int64 = 0}// And until told otherwise:instance.SlaveLagSeconds = instance.SecondsBehindMasterinstance.AllowTLS = (m.GetString("Master_SSL_Allowed") == "Yes")// Not breaking the flow even on errorslaveStatusFound = truereturn nil})Cleanup:waitGroup.Wait()//省略若干行if instanceFound {instance.LastDiscoveryLatency = time.Since(readingStartTime)instance.IsLastCheckValid = trueinstance.IsRecentlyChecked = trueinstance.IsUpToDate = truelatency.Start("backend")if bufferWrites {enqueueInstanceWrite(instance, instanceFound, err)} else {WriteInstance(instance, instanceFound, err)}WriteLongRunningProcesses(&instance.Key, longRunningProcesses)lastAttemptedCheckTimer.Stop()latency.Stop("backend")return instance, nil}// Something is wrong, could be network-wise. Record that we// tried to check the instance. last_attempted_check is also// updated on success by writeInstance.latency.Start("backend")_ = UpdateInstanceLastChecked(&instance.Key, partialSuccess)latency.Stop("backend")return nil, err//完整内容请参考https://github.com/openark/orchestrator/blob/master/go/inst/instance_dao.go}
将instance对象信息保存到后端database_instance表。
*************************** 1. row ***************************hostname: 192.168.1.1port: 3306last_checked: 2021-01-24 12:11:41last_attempted_check: 2021-01-24 12:11:41last_check_partial_success: 1last_seen: 2021-01-24 12:11:41uptime: 42599071server_id: 48301server_uuid: 16db875e-e197-11e8-9c8f-b4969111dff0version: 5.6.32-78.0-logbinlog_server: 0read_only: 0binlog_format: ROWlog_bin: 1log_slave_updates: 1binary_log_file: mysql-bin.000075binary_log_pos: 583135957master_host:master_port: 0slave_sql_running: 0slave_io_running: 0replication_sql_thread_state: -1replication_io_thread_state: -1has_replication_filters: 0oracle_gtid: 0master_uuid:ancestry_uuid: 16db875e-e197-11e8-9c8f-b4969111dff0executed_gtid_set: 16db875e-e197-11e8-9c8f-b4969111dff0:1-86855534gtid_purged: 16db875e-e197-11e8-9c8f-b4969111dff0:1-83262861gtid_errant:supports_oracle_gtid: 1mariadb_gtid: 0pseudo_gtid: 0master_log_file:read_master_log_pos: 0relay_master_log_file:exec_master_log_pos: 0relay_log_file:relay_log_pos: 0last_sql_error:last_io_error:seconds_behind_master: NULLslave_lag_seconds: NULLsql_delay: 0allow_tls: 0num_slave_hosts: 2slave_hosts: [{"Hostname":"192.168.1.2","Port":3306},{"Hostname":"192.168.1.3","Port":3306}]cluster_name: 192.168.1.1:3306suggested_cluster_alias: mysql_testdata_center: RWphysical_environment:instance_alias: bjlf-mysql-001.bjlf.comsemi_sync_enforced: 0replication_depth: 0is_co_master: 0replication_credentials_available: 0has_replication_credentials: 0version_comment: Percona Server (GPL), Release 78.0, Revision 8a8e016major_version: 5.6binlog_row_image: FULLlast_discovery_latency: 10706003semi_sync_master_enabled: 0semi_sync_replica_enabled: 0gtid_mode: ON
last_checked、last_attempted_check、last_check_partial_success、last_seen是判断实例健康状态的几个重要字段。
last_check_partial_success:从上文中可以看到ReadTopologyInstanceBufferable函数会连接实例获得很多的实例信息,如果函数连接到实例之前异常,那么这个字段为0,如果能连接到实例能获得部分或全部信息这个字段值为1。
last_attempted_check:ReadTopologyInstanceBufferable函数开始执行的时候会对instanceKey合法性进行判断,不合法情况下会更新last_attempted_check时间戳为当前时间,返回err信息为:ReadTopologyInstance will not act on invalid instance key。
另一个地方更新此字段的地方是ReadTopologyInstanceBufferable做了以下定义:
lastAttemptedCheckTimer := time.AfterFunc(time.Second, func() {go UpdateInstanceLastAttemptedCheck(instanceKey)})
lastAttemptedCheckTimer会延迟执行,不过如果ReadTopologyInstanceBufferable快于1秒内执行并且能正常discover到实例信息的话,lastAttemptedCheckTimer.Stop()会暂停更新last_attempted_check字段。
ReadTopologyInstanceBufferable函数中label Cleanup 代码块定义了相关逻辑,如果能对一个实例正常discover的情况下会执行WriteInstance函数把获得的instance实例信息保存到后端database_instance表里面。保存的过程中会一次性把last_checked、last_attempted_check、last_seen更新成当前时间,把last_check_partial_success更新成1。
总结
orchestrator会间隔InstancePollSeconds(默认5s)连接实例上拉取实例状态,并将这些状态信息存入orchestrator的元数据库的orchestrator.database_instance表中。获得的状态信息有(NOT MaxScale):
show global status like 'Uptime'select @@global.hostname, ifnull(@@global.report_host, ''), @@global.server_id, @@global.version, @@global.version_comment, @@global.read_only, @@global.binlog_format, @@global.log_bin, @@global.log_slave_updatesshow master statusshow global status like 'rpl_semi_sync_%_status'select @@global.gtid_mode, @@global.server_uuid, @@global.gtid_executed, @@global.gtid_purged, @@global.master_info_repository = 'TABLE', @@global.binlog_row_imageshow slave statusselect count(*) > 0 and MAX(User_name) != '' from mysql.slave_master_infoshow slave hostsselect substring_index(host, ':', 1) as slave_hostname from information_schema.processlist where command IN ('Binlog Dump', 'Binlog Dump GTID')SELECT SUBSTRING_INDEX(@@hostname, '.', 1)
获得到实例信息后,通过下面语句将状态值存入到orchestrator的元数据库中:
INSERT INTO database_instance(hostname, port, last_checked, last_attempted_check, last_check_partial_success, uptime, server_id, server_uuid, version, major_version, version_comment, binlog_server, read_only, binlog_format, binlog_row_image, log_bin, log_slave_updates, binary_log_file, binary_log_pos, master_host, master_port, slave_sql_running, slave_io_running, replication_sql_thread_state, replication_io_thread_state, has_replication_filters, supports_oracle_gtid, oracle_gtid, master_uuid, ancestry_uuid, executed_gtid_set, gtid_mode, gtid_purged, gtid_errant, mariadb_gtid, pseudo_gtid, master_log_file, read_master_log_pos, relay_master_log_file, exec_master_log_pos, relay_log_file, relay_log_pos, last_sql_error, last_io_error, seconds_behind_master, slave_lag_seconds, sql_delay, num_slave_hosts, slave_hosts, cluster_name, suggested_cluster_alias, data_center, region, physical_environment, replication_depth, is_co_master, replication_credentials_available, has_replication_credentials, allow_tls, semi_sync_enforced, semi_sync_master_enabled, semi_sync_replica_enabled, instance_alias, last_discovery_latency, last_seen)VALUES('192.168.1.1', 3306, NOW(), NOW(), 1, 42599071, 48301, '16db875e-e197-11e8-9c8f-b4969111dff0', '5.6.32-78.0-log', '5.6', 'Percona Server (GPL), Release 78.0, Revision 8a8e016', 0, 1, 'ROW', 'FULL', 1, 1, 'mysql-bin.000075', 583135957, '', 0, 1, 1, 1, 1, 0, 1, 1, '16db875e-e197-11e8-9c8f-b4969111dff0', '16db875e-e197-11e8-9c8f-b4969111dff0:1-86855534', '16db875e-e197-11e8-9c8f-b4969111dff0:1-83262861', 'ON', '', '', 0, 0, '', 0, '', , '', 137056344, '', '', 0, 0, 0, 1, '[{\"Hostname\":\"192.168.1.2\",\"Port\":3306}]', '192.168.1.1:3306', 'bjlf-mysql-001.bjlf.com', '', '', '', 1, 1, 1, 1, 0, 0, 0, 0, '', 10706003, NOW())ON DUPLICATE KEY UPDATEhostname=VALUES(hostname), port=VALUES(port), last_checked=VALUES(last_checked), last_attempted_check=VALUES(last_attempted_check), last_check_partial_success=VALUES(last_check_partial_success), uptime=VALUES(uptime), server_id=VALUES(server_id), server_uuid=VALUES(server_uuid), version=VALUES(version), major_version=VALUES(major_version), version_comment=VALUES(version_comment), binlog_server=VALUES(binlog_server), read_only=VALUES(read_only), binlog_format=VALUES(binlog_format), binlog_row_image=VALUES(binlog_row_image), log_bin=VALUES(log_bin), log_slave_updates=VALUES(log_slave_updates), binary_log_file=VALUES(binary_log_file), binary_log_pos=VALUES(binary_log_pos), master_host=VALUES(master_host), master_port=VALUES(master_port), slave_sql_running=VALUES(slave_sql_running), slave_io_running=VALUES(slave_io_running), replication_sql_thread_state=VALUES(replication_sql_thread_state), replication_io_thread_state=VALUES(replication_io_thread_state), has_replication_filters=VALUES(has_replication_filters), supports_oracle_gtid=VALUES(supports_oracle_gtid), oracle_gtid=VALUES(oracle_gtid), master_uuid=VALUES(master_uuid), ancestry_uuid=VALUES(ancestry_uuid), executed_gtid_set=VALUES(executed_gtid_set), gtid_mode=VALUES(gtid_mode), gtid_purged=VALUES(gtid_purged), gtid_errant=VALUES(gtid_errant), mariadb_gtid=VALUES(mariadb_gtid), pseudo_gtid=VALUES(pseudo_gtid), master_log_file=VALUES(master_log_file), read_master_log_pos=VALUES(read_master_log_pos), relay_master_log_file=VALUES(relay_master_log_file), exec_master_log_pos=VALUES(exec_master_log_pos), relay_log_file=VALUES(relay_log_file), relay_log_pos=VALUES(relay_log_pos), last_sql_error=VALUES(last_sql_error), last_io_error=VALUES(last_io_error), seconds_behind_master=VALUES(seconds_behind_master), slave_lag_seconds=VALUES(slave_lag_seconds), sql_delay=VALUES(sql_delay), num_slave_hosts=VALUES(num_slave_hosts), slave_hosts=VALUES(slave_hosts), cluster_name=VALUES(cluster_name), suggested_cluster_alias=VALUES(suggested_cluster_alias), data_center=VALUES(data_center), region=VALUES(region), physical_environment=VALUES(physical_environment), replication_depth=VALUES(replication_depth), is_co_master=VALUES(is_co_master), replication_credentials_available=VALUES(replication_credentials_available), has_replication_credentials=VALUES(has_replication_credentials), allow_tls=VALUES(allow_tls), semi_sync_enforced=VALUES(semi_sync_enforced), semi_sync_master_enabled=VALUES(semi_sync_master_enabled), semi_sync_replica_enabled=VALUES(semi_sync_replica_enabled), instance_alias=VALUES(instance_alias), last_discovery_latency=VALUES(last_discovery_latency), last_seen=VALUES(last_seen)
后期会针对orchestrator探测实例失败的流程进行分析,敬请期待。




