Pesquisa de site

Personalize a alta disponibilidade do Apache ShardingSphere com MySQL


Saiba como e por que o ShardingSphere pode alcançar alta disponibilidade de banco de dados usando MySQL como exemplo.

Os usuários têm muitas opções para personalizar e estender as soluções de alta disponibilidade (HA) do ShardingSphere. Nossa equipe concluiu dois planos de alta disponibilidade: uma solução de alta disponibilidade MySQL baseada em MGR e uma solução de alta disponibilidade de banco de dados openGauss contribuída por alguns committers da comunidade. Os princípios das duas soluções são os mesmos.

Abaixo está como e por que o ShardingSphere pode alcançar alta disponibilidade de banco de dados usando MySQL como exemplo:

(Zhao Jinchao, CC BY-SA 4.0)

Pré-requisito

O ShardingSphere verifica se o ambiente de cluster MySQL subjacente está pronto executando a seguinte instrução SQL. O ShardingSphere não poderá ser iniciado se algum dos testes falhar.

Verifique se o MGR está instalado:

SELECT * FROM information_schema.PLUGINS WHERE PLUGIN_NAME='group_replication'

Veja o número do membro do grupo MGR. O cluster MGR subjacente deve consistir em pelo menos três nós:

SELECT count(*) FROM performance_schema.replication_group_members

Verifique se o nome do grupo do cluster MGR é consistente com o da configuração. O nome do grupo é o marcador de um grupo MGR, e cada grupo de um cluster MGR possui apenas um nome de grupo:

SELECT * FROM performance_schema.global_variables WHERE VARIABLE_NAME='group_replication_group_name' 

Verifique se o MGR atual está definido como modo primário único. Atualmente, o ShardingSphere não oferece suporte a cenários de gravação dupla ou gravação múltipla. Ele suporta apenas o modo de gravação única:

SELECT * FROM performance_schema.global_variables WHERE VARIABLE_NAME='group_replication_single_primary_mode'

Consulte todos os hosts de nós, portas e estados no cluster do grupo MGR para verificar se a fonte de dados configurada está correta:

SELECT MEMBER_HOST, MEMBER_PORT, MEMBER_STATE FROM performance_schema.replication_group_members

Descoberta dinâmica de banco de dados primário

O ShardingSphere encontra a URL do banco de dados primário de acordo com o comando SQL do banco de dados mestre de consulta fornecido pelo MySQL:

private String findPrimaryDataSourceURL(final Map<String, DataSource> dataSourceMap) {
    String result = "";
    String sql = "SELECT MEMBER_HOST, MEMBER_PORT FROM performance_schema.replication_group_members WHERE MEMBER_ID = "
            + "(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'group_replication_primary_member')";
    for (DataSource each : dataSourceMap.values()) {
        try (Connection connection = each.getConnection();
             Statement statement = connection.createStatement();
             ResultSet resultSet = statement.executeQuery(sql)) {
            if (resultSet.next()) {
                return String.format("%s:%s", resultSet.getString("MEMBER_HOST"), resultSet.getString("MEMBER_PORT"));
            }
        } catch (final SQLException ex) {
            log.error("An exception occurred while find primary data source url", ex);
        }
    }
    return result;
}

Compare os URLs do banco de dados primário encontrados acima, um por um, com os URLs dataSources configurados. A fonte de dados correspondente é o banco de dados primário. Ele será atualizado para a memória atual do ShardingSphere e perpetuado no centro de registro, por meio do qual será distribuído para outros nós de computação no cluster.

(Zhao Jinchao, CC BY-SA 4.0)

Descoberta dinâmica de banco de dados secundário

Existem dois tipos de estados de banco de dados secundários no ShardingSphere: ativar e desativar. O estado do banco de dados secundário será sincronizado com a memória do ShardingSphere para garantir que o tráfego de leitura possa ser roteado corretamente.

Obtenha todos os nós do grupo MGR:

SELECT MEMBER_HOST, MEMBER_PORT, MEMBER_STATE FROM performance_schema.replication_group_members

Desative bancos de dados secundários:

private void determineDisabledDataSource(final String schemaName, final Map<String, DataSource> activeDataSourceMap,
                                         final List<String> memberDataSourceURLs, final Map<String, String> dataSourceURLs) {
    for (Entry<String, DataSource> entry : activeDataSourceMap.entrySet()) {
        boolean disable = true;
        String url = null;
        try (Connection connection = entry.getValue().getConnection()) {
            url = connection.getMetaData().getURL();
            for (String each : memberDataSourceURLs) {
                if (null != url && url.contains(each)) {
                    disable = false;
                    break;
                }
            }
        } catch (final SQLException ex) {
            log.error("An exception occurred while find data source urls", ex);
        }
        if (disable) {
            ShardingSphereEventBus.getInstance().post(new DataSourceDisabledEvent(schemaName, entry.getKey(), true));
        } else if (!url.isEmpty()) {
            dataSourceURLs.put(entry.getKey(), url);
        }
    }
}

A desativação do banco de dados secundário depende da fonte de dados configurada e de todos os nós do grupo MGR.

O ShardingSphere pode verificar um por um se a fonte de dados configurada pode obter Connection corretamente e verificar se a URL da fonte de dados contém nós do grupo MGR.

Se Connection não puder ser obtido ou a verificação falhar, o ShardingSphere desativará a fonte de dados por um gatilho de evento e a sincronizará com o centro de registro.

Habilite bancos de dados secundários:

private void determineEnabledDataSource(final Map<String, DataSource> dataSourceMap, final String schemaName,
                                        final List<String> memberDataSourceURLs, final Map<String, String> dataSourceURLs) {
    for (String each : memberDataSourceURLs) {
        boolean enable = true;
        for (Entry<String, String> entry : dataSourceURLs.entrySet()) {
            if (entry.getValue().contains(each)) {
                enable = false;
                break;
            }
        }
        if (!enable) {
            continue;
        }
        for (Entry<String, DataSource> entry : dataSourceMap.entrySet()) {
            String url;
            try (Connection connection = entry.getValue().getConnection()) {
                url = connection.getMetaData().getURL();
                if (null != url && url.contains(each)) {
                    ShardingSphereEventBus.getInstance().post(new DataSourceDisabledEvent(schemaName, entry.getKey(), false));
                    break;
                }
            } catch (final SQLException ex) {
                log.error("An exception occurred while find enable data source urls", ex);
            }
        }
    }
}

Depois que o banco de dados secundário travado for recuperado e adicionado ao grupo MGR, a configuração será verificada para ver se a fonte de dados recuperada é usada. Se sim, o gatilho do evento informará ao ShardingSphere que a fonte de dados precisa ser habilitada.

Mecanismo de batimento cardíaco

O mecanismo de pulsação é introduzido no módulo HA para garantir que os estados primário-secundário sejam sincronizados em tempo real.

Ao integrar o subprojeto ElasticJob do ShardingSphere, os processos acima são executados pela estrutura do agendador ElasticJob na forma de Job quando o módulo HA é inicializado, conseguindo assim a separação entre o desenvolvimento de funções e o agendamento de jobs.

Mesmo que os desenvolvedores precisem estender a função de HA, eles não precisam se preocupar com a forma como os trabalhos são desenvolvidos e operados:

private void initHeartBeatJobs(final String schemaName, final Map<String, DataSource> dataSourceMap) {
    Optional<ModeScheduleContext> modeScheduleContext = ModeScheduleContextFactory.getInstance().get();
    if (modeScheduleContext.isPresent()) {
        for (Entry<String, DatabaseDiscoveryDataSourceRule> entry : dataSourceRules.entrySet()) {
            Map<String, DataSource> dataSources = dataSourceMap.entrySet().stream().filter(dataSource -> !entry.getValue().getDisabledDataSourceNames().contains(dataSource.getKey()))
                    .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
            CronJob job = new CronJob(entry.getValue().getDatabaseDiscoveryType().getType() + "-" + entry.getValue().getGroupName(),
                each -> new HeartbeatJob(schemaName, dataSources, entry.getValue().getGroupName(), entry.getValue().getDatabaseDiscoveryType(), entry.getValue().getDisabledDataSourceNames())
                            .execute(null), entry.getValue().getHeartbeatProps().getProperty("keep-alive-cron"));
            modeScheduleContext.get().startCronJob(job);
        }
    }
}

Embrulhar

Até agora, o recurso HA do Apache ShardingSphere provou ser aplicável para soluções MySQL e openGauss HA. No futuro, ele integrará mais produtos MySQL HA e oferecerá suporte a mais soluções de HA de banco de dados.

Como sempre, se você estiver interessado, seja bem-vindo para se juntar a nós e contribuir com o projeto Apache ShardingSphere.

Links do projeto de código aberto Apache ShardingSphere:

  • ShardingSphere GitHub
  • Twitter do ShardingSphere
  • ShardingSphere Slack
  • Diretrizes para contribuidores