记一次在使用ShardingSphere出现的问题。
先说下项目的环境:Spring Boot 2.2.9ShardingSphere 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等一些其他操作。但是对于正常的基础使用还是可以的。而且其支持的分表规则多种多样。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐