Фон
Apache ShardingSphere постепенно вводил различные функции, основанные на практических требованиях пользователей, такие как сегментирование данных и разделение чтения/записи.
Функция сегментирования данных содержит множество практических стратегий сегментирования, таких как стандартная стратегия сегментирования и сложная стратегия сегментирования, и пользователи могут легко настроить соответствующие алгоритмы сегментирования.
Когда дело доходит до разделения чтения/записи, Apache ShardingSphere предоставляет пользователям два типа: статический и динамический, а также многочисленные алгоритмы балансировки нагрузки.
Функции шардинга и разделения чтения/записи ShardingSphere уже очень полезны, но сценарии постоянно меняются.
В качестве примера возьмем случай с несколькими арендаторами: пользователь ожидает сегментирования данных в соответствии с арендатором, которому принадлежит учетная запись для входа, но информация об арендаторе существует не в каждом бизнес-SQL. В этом случае алгоритм извлечения полей шардинга из SQL невозможен.
Кроме того, в большинстве сценариев разделения чтения/записи пользователи хотят направлять запросы к базе данных-получателю для выполнения, но в некоторых сценариях, требующих выполнения операций в реальном времени, пользователи хотят направлять SQL для выполнения в базу данных-источник. В настоящее время разделение чтения/записи не может соответствовать бизнес-требованиям.
Учитывая вышеупомянутые болевые точки, Apache ShardingSphere создала функцию Hint
, позволяющую пользователям использовать другую логику, а не SQL, для реализации принудительной маршрутизации или сегментирования.
В настоящее время ShardingSphere предоставляет пользователям два метода Hint
. Один из них — это метод ручного программирования с Java API, в котором используется HintManager
для принудительной маршрутизации и сегментирования. Этот метод очень удобен для приложений, написанных с помощью JDBC, потому что разработчикам не нужно писать слишком много кода, и они могут легко реализовать независимые от SQL функции сегментирования или принудительной маршрутизации.
На основе распределенного SQL (DistSQL) компания ShardingSphere разработала SQL HINT
и DistSQL HINT
, чтобы предоставить пользователям функции сегментирования и принудительной маршрутизации, которые можно реализовать без программирования. Этот метод более удобен для администраторов баз данных (DBA).
Далее, давайте внимательно рассмотрим два метода.
Ручное программирование на основе HintManager
ShardingSphere может реализовать функции принудительного маршрута и шардинга через HintManager
объектов. С HintManager
пользователи могут выполнять сегментирование данных без SQL. Это также позволяет пользователям более гибко разделять данные или принудительно выполнять маршрутизацию, что значительно расширяет пользовательские сценарии.
На данный момент с помощью HintManager
пользователи могут использовать встроенные или пользовательские алгоритмы Hint
ShardingSphere для реализации функции сегментирования и могут установить указанный источник данных или заставить первичную базу данных выполнять разделение чтения/записи для реализации функции принудительной маршрутизации.
Сначала я хотел бы объяснить основной принцип его реализации, чтобы помочь вам лучше понять HintManager
.
- Реализация HintManager
Приведенный ниже фрагмент кода поможет вам быстро понять принцип HintManager
.
@NoArgsConstructor(access = AccessLevel.PRIVATE) public final class HintManager implements AutoCloseable { private static final ThreadLocal<HintManager> HINT_MANAGER_HOLDER = new ThreadLocal<>(); }
Как показано выше, ShardingSphere реализует функцию HintManager
с ThreadLocal
: пока они находятся в одном потоке, пользовательские настройки сегментирования сохраняются. Таким образом, пользователю нужно только вызвать соответствующие HintManager
функции перед выполнением операторов SQL, после чего ShardingSphere может получить условия сегментирования или обязательной маршрутизации, установленные пользователем в текущем потоке, чтобы выполнять операции сегментирования или маршрутизации.
Далее давайте научимся им пользоваться.
- Как использовать HitManager
- Используйте
HINT
для разделения
Чтобы использовать Hint Sharding Algorithm
, пользователи должны реализовать интерфейс org.apache.shardingsphere.sharding.api.sharding.hint.HintShardingAlgorithm
. Когда Apache ShardingSphere выполняет маршрутизацию, он получает значения сегментов из HintManager
для операций маршрутизации.
Конфигурация следующая:
rules: - !SHARDING tables: t_order: actualDataNodes: demo_ds_${0..1}.t_order_${0..1} databaseStrategy: hint: algorithmClassName: xxx.xxx.xxx.HintXXXAlgorithm tableStrategy: hint: algorithmClassName: xxx.xxx.xxx.HintXXXAlgorithm defaultTableStrategy: none: defaultKeyGenerateStrategy: type: SNOWFLAKE column: order_id props: sql-show: true
Получите экземпляр
HintManager
:
HintManager hintManager = HintManager.getInstance();
Добавить ключ сегмента:
- Используйте
hintManager.addDatabaseShardingValue
, чтобы добавить ключ сегмента источника данных. hintManager.addTableShardingValue
используется для добавления ключа осколка таблицы
Примечание. В случае сегментирования базы данных без сегментирования таблиц при использовании HINT
для принудительной маршрутизации в сегмент базы данных можно использовать hintManager.setDatabaseShardingValue
для добавления Shard
.
Удалить ключ сегмента:
Shard Key
хранится в ThreadLocal
, поэтому вам нужно вызвать hintManager.close()
в конце операции, чтобы очистить содержимое в ThreadLocal
Пример полного фрагмента кода выглядит следующим образом:
String sql = "SELECT * FROM t_order"; try (HintManager hintManager = HintManager.getInstance(); Connection conn = dataSource.getConnection(); PreparedStatement preparedStatement = conn.prepareStatement(sql)) { hintManager.addDatabaseShardingValue("t_order", 1); hintManager.addTableShardingValue("t_order", 2); try (ResultSet rs = preparedStatement.executeQuery()) { while (rs.next()) { // ... } } } String sql = "SELECT * FROM t_order"; try (HintManager hintManager = HintManager.getInstance(); Connection conn = dataSource.getConnection(); PreparedStatement preparedStatement = conn.prepareStatement(sql)) { hintManager.setDatabaseShardingValue(3); try (ResultSet rs = preparedStatement.executeQuery()) { while (rs.next()) { // ... } } }
2. Используйте HINT
до Force Primary Database Route
Получить HintManager
Это то же самое, что и разделение данных на основе HINT
, описанное выше.
Установить основной маршрут базы данных
Используйте hintManager.setWriteRouteOnly
для завершения настройки.
Очистить значение ключа сегмента
Это то же самое, что и разделение данных на основе HINT
, описанное выше.
Пример полного фрагмента кода выглядит следующим образом:
String sql = "SELECT * FROM t_order"; try (HintManager hintManager = HintManager.getInstance(); Connection conn = dataSource.getConnection(); PreparedStatement preparedStatement = conn.prepareStatement(sql)) { hintManager.setWriteRouteOnly(); try (ResultSet rs = preparedStatement.executeQuery()) { while (rs.next()) { // ... } } }
3. Используйте HINT
для реализации маршрута к указанной базе данных
Получить HintManager
Это то же самое, что и разделение данных на основе HINT
, описанное выше.
Установить маршрут к указанной базе данных
Используйте hintManager.setWriteRouteOnly
для установки имени базы данных.
Пример полного фрагмента кода выглядит следующим образом:
String sql = "SELECT * FROM t_order"; try (HintManager hintManager = HintManager.getInstance(); Connection conn = dataSource.getConnection(); PreparedStatement preparedStatement = conn.prepareStatement(sql)) { hintManager.setDataSourceName("ds_0"); try (ResultSet rs = preparedStatement.executeQuery()) { while (rs.next()) { // ... } } }
Удалить значение принудительного маршрута
Это то же самое, что и разделение данных на основе HINT
, описанное выше.
После понимания метода ручного программирования, основанного на HintManager
, давайте взглянем на другое HINT
решение, предоставляемое ShardingSphere на основе распределенного SQL.
СОВЕТ на основе DistSQL
DistSQL HINT
, предоставляемый Apache ShardingSphere, состоит из двух функций: одна называется SQL HINT
и основана на аннотациях SQL, а другая — функция, которая воздействует на HintManager
через реализацию DistSQL.
СОВЕТ SQL
SQL HINT
— это метод HINT
для реализации принудительной маршрутизации путем добавления аннотаций к операторам SQL, что снижает стоимость модификации кода для пользователей. Это означает, что на него не распространяются ограничения Java API, и он доступен как в ShardingSphere-JDBC, так и в ShardingSphere-Proxy.
Возьмем в качестве примера следующую инструкцию SQL. Даже если пользователь настроит соответствующий алгоритм сегментирования для t_order
, оператор SQL будет выполняться непосредственно в базе данных ds_0
и будет возвращен результат выполнения.
/* ShardingSphere hint: dataSourceName=ds_0 */ SELECT * FROM t_order;
С помощью аннотаций мы можем легко отправить оператор SQL непосредственно в указанную базу данных для выполнения без необходимости рассмотрения другой логики сегментирования.
В качестве примера возьмем сценарий с несколькими арендаторами. Пользователям больше не нужно настраивать сложную логику сегментирования базы данных или изменять бизнес-логику, а нужно только добавить указанную базу данных в аннотацию.
Далее я хотел бы объяснить принцип реализации SQL HINT
.
- Реализация SQL HINT
Если вы уже слышали об Apache ShardingSphere, вы должны быть знакомы с его движком SQL Parser. Первым шагом к реализации SQL HINT
является извлечение аннотаций SQL.
С каналом доступа в ANTLR4 аннотацию SQL можно отправлять отдельно на определенный скрытый канал. ShardingSphere также использует эту функцию для извлечения аннотаций в скрытом канале при создании результата синтаксического анализа.
Конкретная реализация показана в следующем фрагменте кода:
- Поток комментариев SQL в скрытый канал:
lexer grammar Comments; import Symbol; BLOCK_COMMENT: '/*' .*? '*/' -> channel(HIDDEN); INLINE_COMMENT: (('-- ' | '#') ~[\r\n]* ('\r'? '\n' | EOF) | '--' ('\r'? '\n' | EOF)) -> channel(HIDDEN);
- Получите доступ к дереву синтаксиса и добавьте извлечение аннотации
public <T> T visit(final ParseContext parseContext) { ParseTreeVisitor<T> visitor = SQLVisitorFactory.newInstance(databaseType, visitorType, SQLVisitorRule.valueOf(parseContext.getParseTree().getClass()), props); T result = parseContext.getParseTree().accept(visitor); appendSQLComments(parseContext, result); return result; } private <T> void appendSQLComments(final ParseContext parseContext, final T visitResult) { if (!parseContext.getHiddenTokens().isEmpty() && visitResult instanceof AbstractSQLStatement) { Collection<CommentSegment> commentSegments = parseContext.getHiddenTokens().stream().map(each -> new CommentSegment(each.getText(), each.getStartIndex(), each.getStopIndex())) .collect(Collectors.toList()); ((AbstractSQLStatement) visitResult).getCommentSegments().addAll(commentSegments); } }
После извлечения информации аннотаций SQL нам необходимо выполнить соответствующую обязательную маршрутизацию на основе этой информации. Для маршрутизации обычно используется механизм маршрутизатора Apache ShardingSphere.
Мы внесли некоторые изменения для HINT
в движок маршрутизатора.
public RouteContext route(final LogicSQL logicSQL, final ShardingSphereMetaData metaData) { RouteContext result = new RouteContext(); Optional<String> dataSourceName = findDataSourceByHint(logicSQL.getSqlStatementContext(), metaData.getResource().getDataSources()); if (dataSourceName.isPresent()) { result.getRouteUnits().add(new RouteUnit(new RouteMapper(dataSourceName.get(), dataSourceName.get()), Collections.emptyList())); return result; } for (Entry<ShardingSphereRule, SQLRouter> entry : routers.entrySet()) { if (result.getRouteUnits().isEmpty()) { result = entry.getValue().createRouteContext(logicSQL, metaData, entry.getKey(), props); } else { entry.getValue().decorateRouteContext(result, logicSQL, metaData, entry.getKey(), props); } } if (result.getRouteUnits().isEmpty() && 1 == metaData.getResource().getDataSources().size()) { String singleDataSourceName = metaData.getResource().getDataSources().keySet().iterator().next(); result.getRouteUnits().add(new RouteUnit(new RouteMapper(singleDataSourceName, singleDataSourceName), Collections.emptyList())); } return result; }
ShardingSphere сначала находит аннотации SQL, соответствующие определению, и после проверки напрямую возвращает результат маршрутизации, указанный пользователем, тем самым реализуя функцию принудительной маршрутизации.
Далее я хотел бы продемонстрировать, как использовать SQL HINT
.
- Как использовать подсказку SQL
SQL HINT
легко использовать с ShardingSphere-JDBC и ShardingSphere-Proxy.
Шаг 1. Включите анализатор аннотаций и установите для параметра sqlCommentParseEnabled
значение true.
Шаг 2. Добавьте комментарии SQL. В настоящее время SQL HINT
поддерживает указание маршрутизации источника данных и маршрутизации первичной базы данных.
- Маршрутизация, указанная источником данных: в настоящее время поддерживается только маршрутизация к одному источнику данных. Формат комментария пока поддерживает только
/* */
и начинается сShardingSphere hint:
с именем атрибутаdataSourceName
.
/* ShardingSphere hint: dataSourceName=ds_0 */ SELECT * FROM t_order;
- Маршрутизация первичной базы данных: формат комментариев пока поддерживает только
/* */
. Содержимое должно начинаться сShardingSphere hint:
, а имя атрибута —writeRouteOnly
.
/* ShardingSphere hint: writeRouteOnly=true */ SELECT * FROM t_order;
СОВЕТ DistSQL
DistSQL также предоставляет HINT
функций, позволяющих пользователям реализовывать сегментирование и принудительную маршрутизацию через ShardingSphere-Proxy.
- Принцип реализации DistSQL ПОДСКАЗКА
Давайте сначала рассмотрим принцип реализации DistSQL Hint.
Принцип реализации DistSQL HINT
очень прост: это функция HINT
, реализованная с помощью операции HintManager
.
В качестве примера возьмем разделение чтения/записи HINT
. Когда пользователь выполняет следующий SQL-запрос с помощью ShardingSphere-Proxy, ShardingSphere фактически выполняет операции (как показано ниже) с оператором SQL:
- Принудительное чтение-запись первичной базы данных
set readwrite_splitting hint source = write
set readwrite_splitting hint source = write @RequiredArgsConstructor public final class SetReadwriteSplittingHintExecutor extends AbstractHintUpdateExecutor<SetReadwriteSplittingHintStatement> { private final SetReadwriteSplittingHintStatement sqlStatement; @Override public ResponseHeader execute() { HintSourceType sourceType = HintSourceType.typeOf(sqlStatement.getSource()); switch (sourceType) { case AUTO: HintManagerHolder.get().setReadwriteSplittingAuto(); break; case WRITE: HintManagerHolder.get().setWriteRouteOnly(); break; default: break; } return new UpdateResponseHeader(new EmptyStatement()); } } @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class HintManagerHolder { private static final ThreadLocal<HintManager> HINT_MANAGER_HOLDER = new ThreadLocal<>(); /** * Get an instance for {@code HintManager} from {@code ThreadLocal},if not exist,then create new one. * * @return hint manager */ public static HintManager get() { if (HINT_MANAGER_HOLDER.get() == null) { HINT_MANAGER_HOLDER.set(HintManager.getInstance()); } return HINT_MANAGER_HOLDER.get(); } /** * remove {@code HintManager} from {@code ThreadLocal}. */ public static void remove() { HINT_MANAGER_HOLDER.remove(); } }
После того, как пользователь выполнит оператор SQL, механизм синтаксического анализатора DistSQL сначала определит, что оператор SQL имеет подсказку разделения чтения/записи, и извлечет поля, которые пользователь хочет автоматически направить или принудительно направить в базу данных записи.
После этого он будет использоватьSetReadwriteSplittingHintExecutor
для выполнения оператора SQL, чтобы задать правильную работу в HintManager, реализуя функцию принудительной маршрутизации первичной базы данных.
- Как использовать DistSQL HINT
Ниже приведены соответствующие операторы DistSQL HINT
.
В этом блоге подробно представлены два метода и основные принципы HINT
. Как только вы разовьете базовое понимание HINT
, вы сможете лучше выбрать наиболее подходящий метод.
Ссылки на проект Apache ShardingSphere:
Автор
Чусин ЧЕНЬ
Инженер промежуточного программного обеспечения SphereEx и коммиттер Apache ShardingSphere
В настоящее время Чен в основном занимается разработкой модуля ядра Apache ShardingSphere.