ShardingSphere启动慢?别急着升级,先试试调这个参数(max.connections.size.per.query详解)
ShardingSphere启动慢别急着升级先试试调这个参数max.connections.size.per.query详解最近在优化一个使用ShardingSphere 4.x版本的项目时遇到了服务启动特别慢的问题。日志显示元数据加载耗时接近一分钟这对于需要快速扩容或重启的线上服务来说简直是噩梦。经过一番排查发现调整max.connections.size.per.query这个参数就能显著改善启动速度而且完全不需要升级到5.x版本。下面我就来详细分享这个参数的调优经验。1. 问题定位与参数背景首先来看一个典型的启动日志片段2024-01-10 10:12:25:527[main][INFO][][ShardingSphere-metadata.load(70)]Loading 4947 tables meta data. 2024-01-10 10:13:14:312[main][INFO][][ShardingSphere-metadata.createMetaData(59)]Meta data load finished, cost 49078 milliseconds.从日志可以看出系统正在加载近5000张表的元数据耗时约49秒。这种情况在分表数量较多的场景下尤为常见。1.1 元数据加载机制ShardingSphere在启动时需要加载所有逻辑表和物理表的元数据包括表结构信息列名、类型等索引信息分片规则在4.x版本中元数据加载默认采用单线程串行方式这是导致启动慢的根本原因。而max.connections.size.per.query参数正是控制这一行为的关键。2. max.connections.size.per.query参数详解2.1 参数定义与默认值max.connections.size.per.query表示每个查询请求在每个分片中能够使用的最大连接数。它的默认值为1这意味着启动时元数据加载是单线程串行执行SQL执行时对每个分片的查询也是串行执行2.2 参数对启动速度的影响通过分析ShardingSphere 4.1.1的源码我们发现元数据加载的并行度直接受此参数控制public static SchemaMetaData load(final DataSource dataSource, final int maxConnectionCount, final String databaseType) throws SQLException { // ... ListListString tableGroups Lists.partition(tableNames, Math.max(tableNames.size() / maxConnectionCount, 1)); MapString, TableMetaData tableMetaDataMap 1 tableGroups.size() ? load(dataSource.getConnection(), tableGroups.get(0), databaseType) : asyncLoad(dataSource, maxConnectionCount, tableNames, tableGroups, databaseType); // ... }当maxConnectionCount即max.connections.size.per.query大于1时系统会将表名列表分成多个组使用多线程并行加载不同组的元数据显著减少总加载时间2.3 参数对SQL执行的影响除了启动阶段该参数还会影响SQL执行时的行为。以一个查询10个分表的SQL为例-- 逻辑SQL SELECT * FROM user WHERE name 张三 -- 实际执行的物理SQL SELECT * FROM user_0 WHERE name 张三 SELECT * FROM user_1 WHERE name 张三 ... SELECT * FROM user_9 WHERE name 张三参数值会影响连接获取方式并行或串行结果归并模式内存限制或连接限制3. 参数调优实战3.1 配置方式3.1.1 Spring Boot配置文件方式在application.yml中添加配置spring: shardingsphere: datasource: ds0: max-connections-size-per-query: 10 ds1: max-connections-size-per-query: 103.1.2 Java Config方式对于自定义数据源配置Bean(name shardingDataSource) public DataSource getShardingDataSource(Qualifier(ds0) DataSource ds0) { ShardingRuleConfiguration shardingRuleConfig new ShardingRuleConfiguration(); // 配置分片规则... Properties properties new Properties(); properties.put(ConfigurationPropertyKey.MAX_CONNECTIONS_SIZE_PER_QUERY.getKey(), 10); return ShardingDataSourceFactory.createDataSource( Collections.singletonMap(ds0, ds0), shardingRuleConfig, properties ); }3.2 推荐值设置根据实践经验建议按照以下原则设置初始值设置为CPU核心数的2-4倍上限不超过数据源连接池的最大连接数分表数量如果分表数量特别大如超过100建议设置较低值一个参考公式推荐值 min(CPU核心数 × 2, 数据源最大连接数 / 2, 分表数量 / 5)4. 调优注意事项4.1 必须遵守的规则连接池限制确保max.connections.size.per.query≤ 数据源最大连接数否则可能导致连接耗尽引发其他业务异常全表扫描风险避免不带分片键的查询这类查询会扫描所有分片连接消耗呈指数增长内存使用小值可能导致大量数据暂存内存大值可能增加数据库连接压力4.2 监控指标调整参数后建议监控以下指标指标名称正常范围异常表现启动时间10s30s活跃连接数最大连接数80%接近100%查询耗时100ms明显波动4.3 性能对比测试在我的测试环境中不同配置下的启动时间对比参数值500表加载时间5000表加载时间1默认5.2s49.1s52.1s12.3s101.8s8.7s201.5s6.9s可以看到适当增大该参数能显著提升启动速度但超过一定值后收益递减。5. 替代方案比较除了调整这个参数还有其他优化启动速度的方法5.1 升级到5.x版本5.x版本对元数据加载做了重大优化采用多线程加载相同分表只加载一次支持SQL查询方式获取元数据但升级需要考虑兼容性问题配置变更测试成本5.2 减少分表数量如果业务允许可以考虑合并小表使用范围分片替代哈希分片归档历史数据5.3 懒加载策略对于非核心表可以实现启动时不加载元数据首次访问时动态加载需要修改ShardingSphere源码相比之下调整max.connections.size.per.query是最简单、风险最低的方案特别适合需要快速见效的生产环境。