ShardingSphere--Spring boot启动时在ShardingDataSource加载数据库表元数据卡死(慢)的问题
记一次在使用ShardingSphere出现的问题。先说下项目的环境:Spring Boot 2.2.9,ShardingSphere 4.1.1。接下来看下问题现象,不报错就是卡死在加载表元数据那布,项目直接起不来,一直在处理表元数据的加载。![image.png](https://img-blog.csdnimg.cn/img_convert/d0b790579c7e248bc5ac023b9
记一次在使用ShardingSphere
出现的问题。
先说下项目的环境:Spring Boot 2.2.9
,ShardingSphere 4.1.1
。
接下来看下问题现象,不报错就是卡死在加载表元数据那布,项目直接起不来,一直在处理表元数据的加载。
先贴下我的配置
spring:
shardingsphere:
datasource:
znyd:
driverClassName: com.mysql.jdbc.Driver
password: 你的密码
type: com.zaxxer.hikari.HikariDataSource(改成你的DataSource类型)
jdbcUrl: jdbc:mysql://你的数据库地址:3306/znyd?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false
username: 你的用户名
min-idle: 1
max-active: 500
max-wait: 2000
time-between-eviction-runs-millis: 3000
min-evictable-idle-time-millis: 300000
validation-query: SELECT 'x'
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: false
filters: stat,wall,log4j,config
names: znyd
sharding:
tables:
blacklist_check_record:
# 真实表
actual-data-nodes: znyd.blacklist_check_record_$->{0..31}
# 分表策略
table-strategy:
inline:
sharding-column: app_id
algorithm-expression: blacklist_check_record_$->{app_id % 32}
app_info:
actual-data-nodes: znyd.app_info
props:
# 显示sql日志 便于查看分表执行的实际表
sql.show: true
直接在这步停住了。
可以从日志上看到,ShardingSphere
在Spring启动时,加载了2两个逻辑表(即我在配置文件配置了两个表的分表规则),但是加载表的元数据竟然有1470个(我测试库的表数据量正好为1470个),因此当时的想法就是把我这个库的表元数据都加载了。但是当时就很纳闷为什么会加载那么多,毕竟我只配置了两个表。
在这里先说下结论:由于配置中只配置了一个数据源,**ShardingSphere**
会默认将这个数据源当做默认数据源,可以看到我的配置里面是没有配置**default-data-source-name**
这个配置的,而**ShardingSphere**
加载默认数据源的表是全库的表都加载的,即是日志上看到的加载了1470个表。就是因为其将我配置的那个数据源当做了默认数据源。
解决办法:多写一个数据源(和前面配置的数据源相同或者不相同都可以)。
下面是我定位到这个原因的一些步骤:
刚开始我没有根据日志去对应的打印日志的地方,而是怀疑是Spring在启动的时候,在哪一步死循环了。然后去debug Spring的启动,最后看到在创建ShardingDataSource
后没有返回,直接就没了,当时就坚定的相信自己前面的判断,是在哪一步发生死循环了。然后就一直盯着Spring的启动流程看,然后就一直没找到原因。
后面我换了个数据库,这个数据库表比较少,有230个表,然后过了76过秒加载完了。也就是像前面1470张表的那个数据库大概要450秒。如果表的数量在多点估计能时间长的你怀疑人生。
然后换了个思路,找打印Loading 230 tables' meta data.
的位置。然后找到这个类SchemaMetaDataLoader
然后注意第68行代码位置,_loadAllTableNames_(connection, databaseType)
,加载所有的表,‘卡死’的地方是76行代码中的load方法中,其会将tableNames
中的所有表的元数据都加载出来,慢就慢在这个位置上。这个方法里面只有两种结果,要不然获取不到链接tableNames
为0,或者为整个库的所有表。
因此,去上一层调用看,到ShardingMetaDataLoader
这个类,
private SchemaMetaData loadDefaultSchemaMetaData(final DatabaseType databaseType) throws SQLException {
Optional<String> actualDefaultDataSourceName = shardingRule.findActualDefaultDataSourceName();
return actualDefaultDataSourceName.isPresent()
? SchemaMetaDataLoader.load(dataSourceMap.get(actualDefaultDataSourceName.get()), maxConnectionsSizePerQuery, databaseType.getName())
: new SchemaMetaData(Collections.emptyMap());
}
在141行代码处,其会判断默认数据源是否存在,如果存在就会走到我们上面看到打印日志的那个地方(即SchemaMetaDataLoader.load
)。如果不存在则不会去加载表的元数据信息。
然后在看actualDefaultDataSourceName
的来源,发现是从shardingRule.findActualDefaultDataSourceName()
处来的,代码详情如下:
public Optional<String> findActualDefaultDataSourceName() {
String defaultDataSourceName = shardingDataSourceNames.getDefaultDataSourceName();
if (Strings.isNullOrEmpty(defaultDataSourceName)) {
return Optional.empty();
}
Optional<String> masterDefaultDataSourceName = findMasterDataSourceName(defaultDataSourceName);
return masterDefaultDataSourceName.isPresent() ? masterDefaultDataSourceName : Optional.of(defaultDataSourceName);
}
shardingRule
就是ShardingSphere
中用来存放咱们配置的类。类似Mybatis
中的Configuration
类。可以看到其会首先去获取配置的默认数据源。如果shardingDataSourceNames.getDefaultDataSourceName()
第一眼看这个方法你会想只会获取defaultDataSourceName
字段的值,但是其里面内有乾坤,这里面就有造成这个问题的罪魁祸首。让我们看下其具体的逻辑:
/**
* Get default data source name.
*
* @return default data source name
*/
public String getDefaultDataSourceName() {
return 1 == dataSourceNames.size() ? dataSourceNames.iterator().next() : shardingRuleConfig.getDefaultDataSourceName();
}
逻辑很简单就一行代码,其中`dataSourceNames`就是咱们配置的数据源对应的就是`spring.shardingsphere.datasource.names`配置的数据源名称个数,可以看到如果`dataSourceNames`的数量为1,就会将自己作为默认的数据源,如果不为1,才会获取我们用`spring.shardingsphere.sharding.default-data-source-name`配置配置的默认数据源。如果我们配置的数据源个数不为1,且没有配置默认数据源的话,就不会走到前面说的`SchemaMetaDataLoader.load`方法中,而就不会去加载整个库的表元数据。
总结:总的来说这个问题最终也是解决了,但是我自己感觉**ShardingSphere**
上还是有一些瑕疵,对于一些语法的支持不太好。比如insert into …update 、replace into等一些其他操作。但是对于正常的基础使用还是可以的。而且其支持的分表规则多种多样。
更多推荐
所有评论(0)