Поиск по сайту:

Настройка высокой доступности Apache ShardingSphere с помощью MySQL


Узнайте, как и почему ShardingSphere может обеспечить высокую доступность базы данных на примере MySQL.

Пользователи имеют множество возможностей для настройки и расширения решений высокой доступности (HA) ShardingSphere. Наша команда реализовала два плана обеспечения высокой доступности: решение высокой доступности MySQL на основе MGR и решение высокой доступности базы данных openGauss, предоставленное некоторыми участниками сообщества. Принципы обоих решений одинаковы.

Ниже показано, как и почему ShardingSphere может обеспечить высокую доступность базы данных на примере MySQL:

(Чжао Цзиньчао, CC BY-SA 4.0)

Предварительное условие

ShardingSphere проверяет, готова ли базовая кластерная среда MySQL, выполняя следующий оператор SQL. ShardingSphere не может быть запущена, если какой-либо из тестов не пройден.

Проверьте, установлен ли MGR:

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

Просмотрите номер участника группы MGR. Базовый кластер MGR должен состоять как минимум из трех узлов:

SELECT count(*) FROM performance_schema.replication_group_members

Проверьте, соответствует ли имя группы кластера MGR имени в конфигурации. Имя группы является маркером группы MGR, и каждая группа кластера MGR имеет только одно имя группы:

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

Проверьте, установлен ли текущий MGR в качестве единственного основного режима. В настоящее время ShardingSphere не поддерживает сценарии двойной или множественной записи. Он поддерживает только режим одиночной записи:

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

Опросите все хосты узлов, порты и состояния в кластере группы MGR, чтобы проверить правильность настроенного источника данных:

SELECT MEMBER_HOST, MEMBER_PORT, MEMBER_STATE FROM performance_schema.replication_group_members

Динамическое обнаружение первичной базы данных

ShardingSphere находит URL-адрес основной базы данных в соответствии с командой SQL запроса главной базы данных, предоставленной 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;
}

Сравните URL-адреса основной базы данных, указанные выше, один за другим с настроенными URL-адресами dataSources. Сопоставленный источник данных — это основная база данных. Он будет обновлен до текущей памяти ShardingSphere и сохранен в центре реестра, через который он будет распространен на другие вычислительные узлы в кластере.

(Чжао Цзиньчао, CC BY-SA 4.0)

Динамическое обнаружение базы данных-получателя

В ShardingSphere существует два типа состояний вторичной базы данных: включить и отключить. Состояние базы данных-получателя будет синхронизировано с памятью ShardingSphere, чтобы обеспечить правильную маршрутизацию трафика чтения.

Получите все узлы в группе MGR:

SELECT MEMBER_HOST, MEMBER_PORT, MEMBER_STATE FROM performance_schema.replication_group_members

Отключите базы данных-получатели:

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);
        }
    }
}

Отключение базы данных-получателя зависит от настроенного источника данных и всех узлов в группе MGR.

ShardingSphere может проверить одну за другой, может ли настроенный источник данных правильно получить Connection, а также проверить, содержит ли URL-адрес источника данных узлы группы MGR.

Если Соединение невозможно получить или проверка не удалась, ShardingSphere отключит источник данных с помощью триггера события и синхронизирует его с центром реестра.

Включите базы данных-получатели:

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);
            }
        }
    }
}

После того как поврежденная база данных-получатель будет восстановлена и добавлена в группу MGR, будет проверена конфигурация, чтобы определить, используется ли восстановленный источник данных. Если да, триггер события сообщит ShardingSphere, что источник данных необходимо включить.

Механизм сердцебиения

В модуль HA введен механизм Heartbeat, обеспечивающий синхронизацию первичного и вторичного состояний в режиме реального времени.

Благодаря интеграции подпроекта ShardingSphere ElasticJob вышеуказанные процессы выполняются платформой планировщика ElasticJob в форме задания при инициализации модуля высокой доступности, что обеспечивает разделение разработки функций и планирования заданий.

Даже если разработчикам необходимо расширить функцию высокой доступности, им не нужно заботиться о том, как создаются и управляются рабочие места:

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);
        }
    }
}

Заворачивать

На данный момент функция высокой доступности Apache ShardingSphere доказала свою применимость для решений высокой доступности MySQL и openGauss. В дальнейшем он будет интегрировать больше продуктов MySQL HA и поддерживать больше решений для обеспечения высокой доступности баз данных.

Как всегда, если вам интересно, вы можете присоединиться к нам и внести свой вклад в проект Apache ShardingSphere.

Ссылки на проект с открытым исходным кодом Apache ShardingSphere:

  • ШардингСфера GitHub
  • ШардингСфера Твиттер
  • ShardingSphere Slack
  • Рекомендации для авторов