服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - Shardingjdbc启动优化,你学会了吗?

Shardingjdbc启动优化,你学会了吗?

2023-11-07 18:04政采云技术 Java教程

在分析启动问题的过程中对 Sharding-JDBC 查询过程进行了简单的了解,规避了线上可能引发的问题,同时也提醒了自己在改动一些配置时需要对配置所涉及的影响面进行充分评估后再进行改动。

一.Sharding-JDBC 启动优化

问题分析

最近在本地调试的时候发现,项目本地启动比较慢,对启动日志进行分析,Sharding-JDBC 在加载元数据的过程中中耗时 116 秒 ,占用了项目启动时间的一半。

[org.apache.shardingsphere.core.log.ConfigurationLogger:104] : [0||0] Properties:
max.connections.size.per.query: '1'

[org.apache.shardingsphere.core.metadata.ShardingMetaDataLoader:131] : [0||0] Loading 2 logic tables' meta data.
[org.apache.shardingsphere.sql.parser.binder.metadata.schema.SchemaMetaDataLoader:70] : [0||0] Loading 401 tables' meta data.
[org.apache.shardingsphere.shardingjdbc.jdbc.core.context.MultipleDataSourcesRuntimeContext:59] : [0||0] Meta data load finished, cost 115930 milliseconds.

分析加载流程,核心部分在 SchemaMetaDataLoader#load。

List> tableGroups = Lists.partition(tableNames, Math.max(tableNames.size() / maxConnectionCount, 1));
Map tableMetaDataMap = 1 == tableGroups.size()
        ? load(dataSource.getConnection(), tableGroups.get(0), databaseType) : asyncLoad(dataSource, maxConnectionCount, tableNames, tableGroups, databaseType);

进入代码看一下,发现这里会根据 maxConnectionCount 对数据库的表分组进行不同的加载策略,往上游看一下这个 maxConnectionCount 来源于 Sharding-JDBC 的配置,默认为 1。

public enum ConfigurationPropertyKey implements TypedPropertyKey {
  //.......

  /**
   * Max opened connection size for each query.
   */
  MAX_CONNECTIONS_SIZE_PER_QUERY("max.connections.size.per.query", String.valueOf(1), int.class)

  //......
}

那是不是把这个配置扩展一下就可以提高启动速度了?在将配置随意设置成 8 之后由 116s 提升至 15s。

从这里来看启动问题解决了,同时也产生了疑问,为什么 max.connections.size.per.query 默认值设置为 1。

这里是官网对于配置的解释,物理数据库为每次查询分配的最大连接数量。

Shardingjdbc启动优化,你学会了吗?

光看字面意思无法评估影响面,那就从 Sharding-JDBC 的查询入口开始看一下 ShardingPreparedStatement#executeQuery

public ResultSet executeQuery() throws SQLException {
  ResultSet result;
  try {
  clearPrevious();
  prepare();
  initPreparedStatementExecutor();
  MergedResult mergedResult = mergeQuery(preparedStatementExecutor.executeQuery());
  result = new ShardingResultSet(preparedStatementExecutor.getResultSets(), mergedResult, this, executionContext);
  } finally {
  clearBatch();
  }
  currentResultSet = result;
  return result;
}

......

SQLExecutePrepareTemplate#getSQLExecuteGroups

private List> getSQLExecuteGroups(
  final String dataSourceName,
  final List sqlUnits,
  final SQLExecutePrepareCallback callback) throws SQLException {
  List> result = new LinkedList<>();
  int desiredPartitionSize = Math.max(0 == sqlUnits.size() % maxConnectionsSizePerQuery ? sqlUnits.size() / maxConnectionsSizePerQuery : sqlUnits.size() / maxConnectionsSizePerQuery + 1, 1);
  List> sqlUnitPartitions = Lists.partition(sqlUnits, desiredPartitionSize);
  ConnectionMode connectionMode = maxConnectionsSizePerQuery < sqlUnits.size() ? ConnectionMode.CONNECTION_STRICTLY : ConnectionMode.MEMORY_STRICTLY;
  List connections = callback.getConnections(connectionMode, dataSourceName, sqlUnitPartitions.size());
  int count = 0;
  for (List each : sqlUnitPartitions) {
      result.add(getSQLExecuteGroup(connectionMode, connections.get(count++), dataSourceName, each, callback));
  }
  return result;
}

这里可以看到,根据 maxConnectionsSizePerQuery 选择了不同的模式 CONNECTION_STRICTLY 、 MEMORY_STRICTLY

这里先说一下 sqlUnits 是什么,在分表之后进行查询,如果查询参数中不带分片参数的话,Sharding-JDBC 会将 SQL 进行处理,例:

select * from table where field= “测试查询”;

实际执行的过程中会被处理成

select * from table0 where field = “测试查询”;
 select * from table1 where field = “测试查询”;
 select * from table2 where field = “测试查询”;
 ......
 select * from table(n-1) where field = “测试查询”;

Sharding-JDBC 会将真实 SQL 查询的数据进行聚合,聚合的方式根据 maxConnectionsSizePerQuery 配置有两种,即 CONNECTION_STRICTLY、MEMORY_STRICTLY。

CONNECTION_STRICTLY 可以理解为对同一数据源最多创建 maxConnectionsSizePerQuery 个连接。

MEMORY_STRICTLY 则是对一次操作的数据库连接不做限制,同一数据源 n 张分表就创建 n 个连接,多线程并发处理。

从理论上看,是不是可以将 max.connections.size.per.query 设置的大一点,只要单次操作创建的数据源不超过数据库连接上限就可以了?其实不一定,这里进入 UPDATE 的方法看一下。

public int executeUpdate() throws SQLException {
  try {
  clearPrevious();
  prepare();
  initPreparedStatementExecutor();
  return preparedStatementExecutor.executeUpdate();
  } finally {
  clearBatch();
  }
}

发现这里在执行 SQL 前同 SELECT 执行了一样的预处理逻辑 initPreparedStatementExecutor() ,那么在 max.connections.size.per.query > 1 的情况下,无论是那种模式都可能会根据配置的不同获取多个数据源,执行 UPDATE 就有可能存在死锁问题。

例:

@Transactional
public void test(){
  updateByID(1);
  updateByKey(1);
}

Shardingjdbc启动优化,你学会了吗?图片

所以最终得出结论目前状态下,测试环境、预发环境可对max.connections.size.per.query 进行配置,提高启动速度,在线上环境 max.connections.size.per.query 默认为 1 保证应用的稳定。

总结

在分析启动问题的过程中对 Sharding-JDBC 查询过程进行了简单的了解,规避了线上可能引发的问题,同时也提醒了自己在改动一些配置时需要对配置所涉及的影响面进行充分评估后再进行改动。

参考资料

  • Sharding-JDBC 官方文档 https://shardingsphere.apache.org/document/current/cn/overview/

原文地址:https://mp.weixin.qq.com/s/v0Q37rEVX9YhaI3ex6_Cow

延伸 · 阅读

精彩推荐
  • Java教程Java中支持可变参数详解

    Java中支持可变参数详解

    那个可变参数的就是个数组,你传多少个参数都被放到那个数组里面。这样方便了程序员,因为如果不确定要传的参数的个数的话,我们要写带1个参数的,...

    hebedich4362019-12-17
  • Java教程Java连接各种数据库的方法

    Java连接各种数据库的方法

    这篇文章主要介绍了Java连接各种数据库的方法,实例分析了java连接MySQL、SQL Server、Sysbase、Oracle、PostgreSQL及DB2等数据库的技巧,具有一定参考借鉴价值,需要的...

    深水蓝2662019-12-10
  • Java教程实例讲解Java HashSet

    实例讲解Java HashSet

    这篇文章主要介绍了Java HashSet的相关资料,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下 ...

    菜鸟教程4562020-07-07
  • Java教程SpringBoot @JsonDeserialize自定义Json序列化方式

    SpringBoot @JsonDeserialize自定义Json序列化方式

    这篇文章主要介绍了SpringBoot @JsonDeserialize自定义Json序列化方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...

    小飞萌8672022-02-16
  • Java教程SpringBoot整合Netty实现WebSocket的示例代码

    SpringBoot整合Netty实现WebSocket的示例代码

    本文主要介绍了SpringBoot整合Netty实现WebSocket的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友...

    JAVA·D·WangJing5802022-12-12
  • Java教程Spring整合Quartz Job以及Spring Task的实现方法

    Spring整合Quartz Job以及Spring Task的实现方法

    下面小编就为大家分享一篇Spring整合Quartz Job以及Spring Task的实现方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    java的探索之旅5522021-03-11
  • Java教程MyBatis3用log4j在控制台输出SQL的方法示例

    MyBatis3用log4j在控制台输出SQL的方法示例

    本篇文章主要介绍了MyBatis3用log4j在控制台输出SQL的方法示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    微wx笑5292021-03-29
  • Java教程使用SpringSecurity设置角色和权限的注意点

    使用SpringSecurity设置角色和权限的注意点

    这篇文章主要介绍了使用SpringSecurity设置角色和权限的注意点,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...

    张占岭5782022-09-16