Два предыдущих сообщения в блоге нашего сообщества о функции формата SQL и высокой доступности (HA) представили всесторонний обзор обновлений Apache ShardingSphere.

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

В этом посте автор нашего сообщества собирается продемонстрировать на конкретных примерах SQL, как значительно оптимизирована производительность Apache ShardingSphere Executor Engine.

Проблема

Возьмем в качестве примера таблицу t_order с 10 сегментами в базе данных, а max-connections-size-per-queryиспользует конфигурацию по умолчанию 1.

Если пользователь выполнит оператор SELECT * FROM t_order, это приведет к полной маршрутизации. Поскольку в одной и той же базе данных для каждого запроса может быть создано только одно соединение с базой данных, базовые фактические результаты SQL будут заранее загружены в память для обработки. Этот сценарий не только накладывает ограничение на потребление ресурсов соединения с базой данных, но и занимает больше ресурсов памяти.

Однако, если пользователь изменит значение max-connections-size-per-query на 10, то при выполнении фактического SQL может быть создано десять подключений к базе данных. Поскольку соединения с базой данных могут содержать наборы результатов, в этом сценарии дополнительные ресурсы памяти не используются. Тем не менее, этот метод требует больше ресурсов для подключения к базе данных.

Чтобы лучше решить эту проблему, мы оптимизировали производительность SQL Executor Engine в только что выпущенной версии 5.1.0: SQL Rewriter Engine теперь поддерживает перезапись, ориентированную на оптимизацию, что означает возможность объединения нескольких реальных операторов SQL в одном и том же источнике данных. через оператор UNION ALL.

Обновления эффективно сокращают потребляемые ресурсы подключения к базе данных в Executor Engine и предотвращают слияние памяти, дополнительно повышая производительность SQL-запросов в сценариях обработки транзакций в сети (OLTP).

Каков механизм Apache ShardingSphere Executor Engine?

Во-первых, лучше рассмотреть микроядро Apache ShardingSphere и принцип, который объясняет, как Executor Engine работает в процессах. Как показано на рисунке ниже, микроядро Apache ShardingSphere включает основные процессы: SQL Parser, SQL Router, SQL Rewriter, SQL Executor и Result Merger.

SQL Parser Engine может анализировать операторы SQL, введенные пользователем, и генерировать операторы SQL, содержащие контекстную информацию.

Затем SQL Router Engine извлекает условия сегментирования в соответствии с контекстом, объединяет правила сегментирования, настроенные пользователем, для расчета источника данных, необходимого фактическому SQL для выполнения, а затем генерирует результаты маршрутизации.

Механизм SQL Rewriter Engine перезаписывает исходный SQL в соответствии с результатами, возвращаемыми SQL Router Engine. Существует два типа перезаписи: ориентированная на корректность и ориентированная на оптимизацию.

SQL Executor Engine может безопасно и эффективно отправлять SQL, возвращенный SQL Router and Rewriter, в базовый источник данных для выполнения.

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

Из процесса выполнения ясно, что механизм исполнения SQL может напрямую взаимодействовать с базовой базой данных и хранить выполненный набор результатов. Поскольку производительность и потребление ресурсов всей Apache ShardingSphere связаны с работой Executor Engine, сообщество решило внедрить автоматический механизм выполнения SQL, чтобы сбалансировать производительность выполнения и потребление ресурсов.

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

Кроме того, этот метод также может помочь избежать преждевременной загрузки набора результатов запроса в память. Независимое соединение с базой данных может содержать ссылку на позицию курсора в наборе результатов запроса, и поэтому, когда необходимо получить данные, пользователю нужно только переместить курсор.

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

Чтобы разрешить конфликт между производительностью выполнения и контролем ресурсов, Apache ShardingSphere предлагает концепцию Connection Mode. Вот определение Connection Mode в исходном коде.

/**
* Connection Mode.
*/
public enum ConnectionMode {
MEMORY_STRICTLY, CONNECTION_STRICTLY
}

Основываясь на именах членов в классе перечисления Connection Mode, мы видим, что SQL Executor Engine делит соединение с базой данных на два режима: MEMORY_STRICTLY и CONNECTION_STRICTLY.

  • MEMORY_STRICTLY - режим ограничения памяти. Когда пользователь выбирает режим, например, для одного и того же источника данных, если виртуальная таблица соответствует 10 реальным таблицам, SQL Executor Engine создаст 10 подключений для параллельного выполнения. Поскольку все результирующие наборы осколков удерживаются их соединениями, нет необходимости заранее загружать результирующие наборы в память, что эффективно снижает использование памяти;
  • CONNECTION_STRICTLY используется для ограничения подключений. Когда используется режим ограничения подключений, SQL Executor Engine создаст только одно подключение к источнику данных, чтобы строго контролировать потребление ресурсов подключения к базе данных. Однако результирующий набор загружается в память сразу после выполнения настоящего SQL, поэтому он займет некоторое место в памяти.

Как исполнительный механизм SQL Apache ShardingSphere помогает пользователю выбрать подходящий режим подключения? Принцип, лежащий в основе этого, показан на рисунке ниже:

Пользователи могут указать максимально допустимое количество подключений к одному и тому же источнику данных для каждого оператора, настроив maxConnectionSizePerQuery. В соответствии с приведенной выше формулой расчета, когда количество операторов SQL, которые должны выполняться каждым соединением с базой данных, меньше или равно 1, каждому фактическому оператору SQL назначается независимое соединение с базой данных. В это время будет выбран режим ограничения памяти, а источник базы данных позволит создавать несколько подключений к базе данных для параллельного выполнения. В противном случае будет выбран режим ограничения соединений, и тот же источник данных позволит создать только одно соединение с базой данных для выполнения, а затем загрузить набор результатов в набор результатов памяти, а затем предоставить его механизму слияния.

Что оптимизировано?

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

Итак, можно ли использовать для выполнения как можно меньше подключений к базе данных и памяти?

Очевидно, что основным фактором при выборе режима выполнения является количество результатов маршрутизации на одном и том же источнике данных. Таким образом, наиболее прямой оптимизацией является объединение результатов маршрутизации в одном и том же источнике данных. Операторы SQL поддерживают объединение нескольких операторов запроса через UNION ALL, поэтому мы используем UNION ALL в качестве метода оптимизации: несколько реальных операторов SQL в одном и том же источнике данных переписываются в один оператор SQL, который представляет собой переписывание, ориентированное на оптимизацию. Этот метод может значительно сократить количество подключений к базе данных, а также может преобразовывать наборы результатов памяти в потоковые, чтобы уменьшить использование памяти.

Учитывая, что разные диалекты баз данных имеют ограничения на оператор UNION ALL, нам нужно проанализировать документы MySQL, PostgreSQL, Oracle и SQL Server, и тогда мы получим следующую информацию:

MySQL: ОБЪЕДИНИТЬ ВСЕ

Для MySQL советы по использованию UNION ALL включают:

  • Имена столбцов после UNION должны использовать имя столбца первого оператора SELECT.
  • Когда оператор UNION содержит ORDER BY и LIMIT, пользователь должен использовать круглые скобки, чтобы заключить каждый оператор запроса. Поскольку UNION не может гарантировать правильный порядок окончательных наборов результатов. Если вам нужно отсортировать набор результатов UNION, необходимо добавить предложение ORDER BY LIMIT в конце оператора UNION.
# The UNION result set order is not guaranteed
(SELECT a FROM t1 WHERE a=10 AND B=1 ORDER BY a LIMIT 10) UNION (SELECT a FROM t2 WHERE a=11 AND B=2 ORDER BY a LIMIT 10);
# The UNION result set order is guaranteed
(SELECT a FROM t1 WHERE a=10 AND B=1) UNION (SELECT a FROM t2 WHERE a=11 AND B=2) ORDER BY a LIMIT 10;
  • UNION не поддерживает операторы SELECT HIGH_PRIORITY и SELECT INTO file.

PostgreSQL: ОБЪЕДИНИТЬ ВСЕ

  • Имена столбцов после UNION должны быть именами столбцов первого оператора SELECT.
  • Когда оператор UNION содержит ORDER BY и LIMIT, пользователь должен использовать круглые скобки, чтобы заключить каждый оператор запроса. Последнее предложение UNION не может иметь круглых скобок. Без круглых скобок предложение ORDER BY LIMIT применяется ко всему результату UNION.
  • Оператор UNION не поддерживает FOR NO KEY UPDATE, FOR UPDATE, FOR SHARE и FOR KEY SHARE.

Oracle: ОБЪЕДИНИТЬ ВСЕ

  • Оператор UNION не поддерживает типы BLOB, CLOB, BFILE, VARRAY, LONG или вложенные таблицы.
  • UNION statement не поддерживает for_update_clause.
  • Оператор UNION не поддерживает order_by_clause в предложении выбора. Пользователь может добавить order_by_clause только в конце оператора UNION.
SELECT product_id FROM order_items UNION SELECT product_id FROM inventories ORDER BY product_id;
  • Оператор UNION не поддерживает операторы SELECT с выражениями TABLE коллекции;

SQL Server: ОБЪЕДИНИТЬ ВСЕ

  • Когда предложение ORDER BY используется в операторе UNION, оно должно быть помещено над последним предложением SELECT для сортировки результатов UNION.

Основываясь на упомянутых выше стандартах, мы видим, что разные диалекты базы данных могут поддерживать простой оператор SELECT * FROM table WHERE, а с настройкой синтаксиса также может поддерживаться оператор ORDER BY LIMIT (однако есть некоторые синтаксические различия).

Учитывая, что переписывание, ориентированное на оптимизацию, требует совместимости с SQL, Apache ShardingSphere 5.1.0 разработан только для перезаписи простого оператора SELECT * FROM table WHERE для быстрого повышения производительности запросов в сценариях OLTP.

Вот последняя логика механизма переписывания RouteSQLRewriteEngine. В Apache ShardingSphere 5.1.0 добавлена ​​оптимальная логика перезаписи для оператора SELECT * FROM table WHERE: сначала NeedAggregateRewrite используется для оценки строк, и только когда количество результатов маршрутизации в одном и том же источнике данных больше 1 и когда фактический оператор SQL следует за структурой SELECT * FROM table WHERE, будет выполнена перезапись ее в оператор UNION ALL .

/**
* Rewrite SQL and parameters.
*
* @param sqlRewriteContext SQL rewrite context
* @param routeContext route context
* @return SQL rewrite result
*/
public RouteSQLRewriteResult rewrite(final SQLRewriteContext sqlRewriteContext, final RouteContext routeContext) {
Map<RouteUnit, SQLRewriteUnit> result = new LinkedHashMap<>(routeContext.getRouteUnits().size(), 1);
for (Entry<String, Collection<RouteUnit>> entry : aggregateRouteUnitGroups(routeContext.getRouteUnits()).entrySet()) {
Collection<RouteUnit> routeUnits = entry.getValue();
if (isNeedAggregateRewrite(sqlRewriteContext.getSqlStatementContext(), routeUnits)) {
result.put(routeUnits.iterator().next(), createSQLRewriteUnit(sqlRewriteContext, routeContext, routeUnits));
} else {
result.putAll(createSQLRewriteUnits(sqlRewriteContext, routeContext, routeUnits));
}
}
return new RouteSQLRewriteResult(result);
}

Из-за функции перезаписи UNION ALL логика суждения для queryResults в механизме слияния также должна быть скорректирована синхронно. Первоначально несколько queryResults могут быть объединены в один queryResults с помощью UNION ALL. В этом сценарии слияние все еще необходимо выполнить.

@Override
public MergedResult merge(final List<QueryResult> queryResults, final SQLStatementContext<?> sqlStatementContext, final ShardingSphereSchema schema) throws SQLException {
if (1 == queryResults.size() && !isNeedAggregateRewrite(sqlStatementContext)) {
return new IteratorStreamMergedResult(queryResults);
}
Map<String, Integer> columnLabelIndexMap = getColumnLabelIndexMap(queryResults.get(0));
SelectStatementContext selectStatementContext = (SelectStatementContext) sqlStatementContext;
selectStatementContext.setIndexes(columnLabelIndexMap);
MergedResult mergedResult = build(queryResults, selectStatementContext, columnLabelIndexMap, schema);
return decorate(queryResults, selectStatementContext, mergedResult);
}

Чтобы вам было проще понять оптимизацию, мы используем следующую конфигурацию сегментирования и SELECT * FROM t_order, чтобы показать эффект оптимизации. В приведенном ниже примере параметр max-connections-size-per-query имеет значение по умолчанию 1.

rules:
- !SHARDING
tables:
t_order:
actualDataNodes: ds_${0..1}.t_order_${0..1}
tableStrategy:
standard:
shardingColumn: order_id
shardingAlgorithmName: t_order_inline
databaseStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: database_inline
shardingAlgorithms:
database_inline:
type: INLINE
props:
algorithm-expression: ds_${user_id % 2}
t_order_inline:
type: INLINE
props:
algorithm-expression: t_order_${order_id % 2}

В Apache ShardingSphere версии 5.0.0 после выполнения оператора SELECT * FROM t_order мы можем получить следующий результат маршрутизации: есть два источника данных, ds_0 и ds_1, и каждый из них содержит два результата маршрутизации. Поскольку для max-connections-size-per -query установлено значение 1, для каждого реального оператора SQL невозможно иметь соединение с базой данных, поэтому выбирается режим ограничения числа соединений.

Поскольку при этом используется режим лимита соединений, результирующий набор загружается в память после параллельного выполнения, а для хранения используется JDBCMemoryQueryResult. Поэтому, когда пользовательский набор результатов большой, он будет занимать больше памяти. Использование результирующих наборов в памяти также приводит только к слиянию в памяти, но не к потоковому слиянию.

private QueryResult createQueryResult(final ResultSet resultSet, final ConnectionMode connectionMode) throws SQLException {
return ConnectionMode.MEMORY_STRICTLY == connectionMode ? new JDBCStreamQueryResult(resultSet) : new JDBCMemoryQueryResult(resultSet);
}

Теперь, в версии 5.1.0, мы можем использовать UNION ALL для оптимизации выполняемого SQL: несколько результатов маршрутизации в одном и том же источнике данных объединяются в один SQL для выполнения. Режим ограничения памяти выбран потому, что одно соединение с базой данных может содержать один набор результатов. В режиме ограничения памяти набор результатов потоковой передачи JDBCStreamQueryResult object используется для хранения набора результатов, поэтому рассматриваемые данные могут быть запрошены методом запроса потоковой передачи.

Тестирование производительности

Из примера в предыдущем сеансе мы узнали, как UNION ALL used для перезаписи, ориентированной на оптимизацию, может эффективно сократить потребление соединений с базой данных и избежать чрезмерного использования памяти за счет преобразования наборов результатов в памяти в потоковые наборы результатов.

Мы провели стресс-тестирование, чтобы лучше оценить улучшение производительности. Детали реализации следующие:

Конфигурации машины следующие:

Ссылаясь на структуру таблицы sysbench, мы создали 10 осколков таблицы, то есть sbtest1~sbtest10. Каждый осколок таблицы разделен на 5 баз данных, а каждая база данных разделена на 10 таблиц.

Файл config-sharding.yaml configuration выглядит следующим образом.

schemaName: sbtest_sharding
dataSources:
ds_0:
url: jdbc:mysql://127.0.0.1:3306/sbtest?useSSL=false&useServerPrepStmts=true&cachePrepStmts=true&prepStmtCacheSize=8192&prepStmtCacheSqlLimit=1024
username: root
password: 123456
connectionTimeoutMilliseconds: 10000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
ds_1:
url: jdbc:mysql://127.0.0.1:3306/sbtest?useSSL=false&useServerPrepStmts=true&cachePrepStmts=true&prepStmtCacheSize=8192&prepStmtCacheSqlLimit=1024
username: root
password: 123456
connectionTimeoutMilliseconds: 10000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
ds_2:
url: jdbc:mysql://127.0.0.1:3306/sbtest?useSSL=false&useServerPrepStmts=true&cachePrepStmts=true&prepStmtCacheSize=8192&prepStmtCacheSqlLimit=1024
username: root
password: 123456
connectionTimeoutMilliseconds: 10000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
ds_3:
url: jdbc:mysql://127.0.0.1:3306/sbtest?useSSL=false&useServerPrepStmts=true&cachePrepStmts=true&prepStmtCacheSize=8192&prepStmtCacheSqlLimit=1024
username: root
password: 123456
connectionTimeoutMilliseconds: 10000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
ds_4:
url: jdbc:mysql://127.0.0.1:3306/sbtest?useSSL=false&useServerPrepStmts=true&cachePrepStmts=true&prepStmtCacheSize=8192&prepStmtCacheSqlLimit=1024
username: root
password: 123456
connectionTimeoutMilliseconds: 10000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
rules:
- !SHARDING
tables:
sbtest1:
actualDataNodes: ds_${0..4}.sbtest1_${0..9}
tableStrategy:
standard:
shardingColumn: id
shardingAlgorithmName: table_inline_1
keyGenerateStrategy:
column: id
keyGeneratorName: snowflake
sbtest2:
actualDataNodes: ds_${0..4}.sbtest2_${0..9}
tableStrategy:
standard:
shardingColumn: id
shardingAlgorithmName: table_inline_2
keyGenerateStrategy:
column: id
keyGeneratorName: snowflake
sbtest3:
actualDataNodes: ds_${0..4}.sbtest3_${0..9}
tableStrategy:
standard:
shardingColumn: id
shardingAlgorithmName: table_inline_3
keyGenerateStrategy:
column: id
keyGeneratorName: snowflake
sbtest4:
actualDataNodes: ds_${0..4}.sbtest4_${0..9}
tableStrategy:
standard:
shardingColumn: id
shardingAlgorithmName: table_inline_4
keyGenerateStrategy:
column: id
keyGeneratorName: snowflake
sbtest5:
actualDataNodes: ds_${0..4}.sbtest5_${0..9}
tableStrategy:
standard:
shardingColumn: id
shardingAlgorithmName: table_inline_5
keyGenerateStrategy:
column: id
keyGeneratorName: snowflake
sbtest6:
actualDataNodes: ds_${0..4}.sbtest6_${0..9}
tableStrategy:
standard:
shardingColumn: id
shardingAlgorithmName: table_inline_6
keyGenerateStrategy:
column: id
keyGeneratorName: snowflake
sbtest7:
actualDataNodes: ds_${0..4}.sbtest7_${0..9}
tableStrategy:
standard:
shardingColumn: id
shardingAlgorithmName: table_inline_7
keyGenerateStrategy:
column: id
keyGeneratorName: snowflake
sbtest8:
actualDataNodes: ds_${0..4}.sbtest8_${0..9}
tableStrategy:
standard:
shardingColumn: id
shardingAlgorithmName: table_inline_8
keyGenerateStrategy:
column: id
keyGeneratorName: snowflake
sbtest9:
actualDataNodes: ds_${0..4}.sbtest9_${0..9}
tableStrategy:
standard:
shardingColumn: id
shardingAlgorithmName: table_inline_9
keyGenerateStrategy:
column: id
keyGeneratorName: snowflake
sbtest10:
actualDataNodes: ds_${0..4}.sbtest10_${0..9}
tableStrategy:
standard:
shardingColumn: id
shardingAlgorithmName: table_inline_10
keyGenerateStrategy:
column: id
keyGeneratorName: snowflake
defaultDatabaseStrategy:
standard:
shardingColumn: id
shardingAlgorithmName: database_inline
shardingAlgorithms:
database_inline:
type: INLINE
props:
algorithm-expression: ds_${id % 5}
allow-range-query-with-inline-sharding: true
table_inline_1:
type: INLINE
props:
algorithm-expression: sbtest1_${id % 10}
allow-range-query-with-inline-sharding: true
table_inline_2:
type: INLINE
props:
algorithm-expression: sbtest2_${id % 10}
allow-range-query-with-inline-sharding: true
table_inline_3:
type: INLINE
props:
algorithm-expression: sbtest3_${id % 10}
allow-range-query-with-inline-sharding: true
table_inline_4:
type: INLINE
props:
algorithm-expression: sbtest4_${id % 10}
allow-range-query-with-inline-sharding: true
table_inline_5:
type: INLINE
props:
algorithm-expression: sbtest5_${id % 10}
allow-range-query-with-inline-sharding: true
table_inline_6:
type: INLINE
props:
algorithm-expression: sbtest6_${id % 10}
allow-range-query-with-inline-sharding: true
table_inline_7:
type: INLINE
props:
algorithm-expression: sbtest7_${id % 10}
allow-range-query-with-inline-sharding: true
table_inline_8:
type: INLINE
props:
algorithm-expression: sbtest8_${id % 10}
allow-range-query-with-inline-sharding: true
table_inline_9:
type: INLINE
props:
algorithm-expression: sbtest9_${id % 10}
allow-range-query-with-inline-sharding: true
table_inline_10:
type: INLINE
props:
algorithm-expression: sbtest10_${id % 10}
allow-range-query-with-inline-sharding: true
keyGenerators:
snowflake:
type: SNOWFLAKE
props:
worker-id: 123
We use the JMH test program to test different CASEs:
@State(Scope.Thread)
public class QueryOptimizationTest {
private PreparedStatement unionAllForCaseOneStatement;
private PreparedStatement unionAllForCaseTwoStatement;
@Setup(Level.Trial)
public void setup() throws Exception {
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3307/sharding_db?useSSL=false", "root", "123456");
// CASE 1
unionAllForCaseOneStatement = connection.prepareStatement("SELECT COUNT(k) AS countK FROM sbtest1 WHERE id < ?;");
// CASE 2
unionAllForCaseTwoStatement = connection.prepareStatement("SELECT SUM(k) AS sumK FROM sbtest1 WHERE id < ?;");
}
@Benchmark
public void testUnionAllForCaseOne() throws SQLException {
unionAllForCaseOneStatement.setInt(1, 200);
unionAllForCaseOneStatement.executeQuery();
}
@Benchmark
public void testUnionAllForCaseTwo() throws SQLException {
unionAllForCaseTwoStatement.setInt(1, 200);
unionAllForCaseTwoStatement.executeQuery();
}
}

В тесте производительности каждый CASE должен был протестировать 3 группы, а затем было взято среднее значение.

Потом мы перешли на старую версию, aab226b72ba574061748d8f94c461ea469f9168f на для компиляции и упаковки, а также протестировали 3 группы и взяли среднее значение.

Окончательные результаты испытаний представлены ниже.

Тесты CASE 1 и CASE 2 основаны на структуре таблицы sysbench с объемом данных 1 миллион. Количество осколков в тестовых таблицах относительно велико, но общая производительность все же улучшилась примерно в 4 раза. Теоретически, чем больше осколков, тем лучше производительность.

Сводка

В Apache ShardingSphere 5.1.0 достигнута значительная оптимизация производительности как на уровне протокола, так и на уровне ядра.

Этот блог посвящен только SQL Executor Engine и его оптимизации. В будущем сообщество создаст более полные руководства по оптимизации производительности.

Рекомендации

Автор

Дуань Чжэнцян

Старший инженер промежуточного программного обеспечения SphereEx и коммиттер Apache ShardingSphere

Дуан участвует в Apache ShardingSphere с 2018 года, а ранее был ведущим инженером в многочисленных проектах по сегментированию данных.

Он любит открытый исходный код и делится своими техническими историями и опытом с другими разработчиками. Сейчас он посвятил себя разработке модуля ядра Apache ShardingSphere.