综述

数据库类型问题解决结论
Oscar1. RestultSet返回Count
2. 无SQL方言元数据加载器,执行默认模式速度慢
3. Sharding配置表名需为大写
3. 使用的PG SQL解析器,解析不了部分SQL
1. 数据类型切换为PG
2. 未解决
3. 已解决
3. 未解决
X
Dm1. 使用的SQL92 SQL解析器,解析不了部分SQL1. 数据类型切换为Mysql
Kingbase1. ResultSet返回Count1. 数据类型切换为PG
Highgo1. ResultSet返回Count
2. Sharding配置表名需为小写
1. 数据类型切换为PG
2. 已解决

环境

版本

<dependency>
  <groupId>org.apache.shardingsphere</groupId>
  <artifactId>shardingsphere-jdbc-core</artifactId>
  <version>5.3.2</version>
</dependency>

数据库

神通

</dependency>
  <groupId>com.os</groupId>
  <artifactId>oscarJDBC16</artifactId>
  <version>2.0.3</version>
</dependency>

遇到的问题


1. 由于 ResultSet 结果集引发的异常

在 ShardingJdbc 对 SQL 语句执行结果解析的流程中,会把 JDBC 原生的 ResultSet 封装为一个新的 ResultSet 对象org.apache.shardingsphere.driver.jdbc.core.resultset.ShardingSphereResultSet 交给 Mybatis 。

ShardingSphereResultSet 里面缓存着原生 ResultSet 中 LabelColumn 和 Index 的对应关系,Mybatis 在通过ShardingSphereResultSet 获取 Index 的时候就会直接在缓存中拿,拿到后再进行返回。

public final class ShardingSphereResultSet extends AbstractResultSetAdapter {

    @Override
    public long getLong(final String columnLabel) throws SQLException {
        return getLong(getIndexFromColumnLabelAndIndexMap(columnLabel));
    }

    @Override
    public double getDouble(final String columnLabel) throws SQLException {
        return getDouble(getIndexFromColumnLabelAndIndexMap(columnLabel));
    }

    private Integer getIndexFromColumnLabelAndIndexMap(final String columnLabel) throws SQLException {
        Integer result = columnLabelAndIndexMap.get(columnLabel);
        //检测是否存在
        ShardingSpherePreconditions.checkState(null != result, () -> new SQLFeatureNotSupportedException(String.format("Can not get index from column label `%s`.", columnLabel)));
        return result;
    }

}

在生成缓存的过程中,是通过对org.apache.shardingsphere.infra.binder.segment.select.projection.Projection进行解析的,Projection 是 ShardingSphere 对原生 ResultSet 的进一步封装

但是,各大数据库厂商针对于 ResultSet 的具体实现不同,Mybatis 拿到的 columnLabel 存在差异性,比如:

select count(*) from table_name

MySQL 的 ResultSet 的具体实现类 com.mysql.cj.jdbc.result.ResultSetImpl 返回的是 count(*)

Oscar 的 ResultSet 的具体实现类 com.oscar.jdbc.Oscar42ResultSetV2 返回的是 count,而在解析 Projection 生成缓存的过程中,生成的是 count(*) ,通过 count 去 ShardingSphereResultSet 中去拿 index 的时候,就会因为缓存找不到的问题报错

在进行更细粒度的对 ShardingSphere 分析的过程中,发现它在通过 Projection 生成缓存的过程中,会去根据数据库的类型来决定生成 count 还是 count(*)。由于ShardingSphere 目前适配了 PG 和高斯数据库,检测到如果是这两种数据库的话,会生成 count 并加入缓存。

public class AggregationProjection implements Projection {
    
    @Override
    public String getColumnLabel() {
        boolean isPostgreSQLOpenGaussStatement = databaseType instanceof PostgreSQLDatabaseType || databaseType instanceof OpenGaussDatabaseType;
        return getAlias().orElseGet(() -> isPostgreSQLOpenGaussStatement ? type.name().toLowerCase() : getExpression());
    }
    
}

默认情况下,神通数据库会自动匹配为SQL92类型,这就需要我们自定义数据库类型为PG类型。

public final class OscarDatabaseType implements SchemaSupportedDatabaseType, BranchDatabaseType {
    
    @Override
    public Collection<String> getJdbcUrlPrefixes() {
        return Collections.singleton(String.format("jdbc:%s:", getType().toLowerCase()));
    }
    
    @Override
    public PostgreSQLDataSourceMetaData getDataSourceMetaData(final String url, final String username) {
        return new PostgreSQLDataSourceMetaData(url);
    }
    
    @Override
    public String getType() {
        return "OSCAR";
    }

    @Override
    public DatabaseType getTrunkDatabaseType() {
        return TypedSPILoader.getService(DatabaseType.class, "PostgreSQL");
    }
    
}
2. 方言元数据加载器引发的异常

ShardingSphere 在启动的时候会通过 connection 获取当前 schema 的所有表

    private static Collection<SchemaMetaData> load(final SchemaMetaDataLoaderMaterial material) throws SQLException {
    	//获取SQL方言模式元数据加载器(暂不清楚用途)
        Optional<DialectSchemaMetaDataLoader> dialectSchemaMetaDataLoader = TypedSPILoader.findService(DialectSchemaMetaDataLoader.class, material.getStorageType().getType());
        if (dialectSchemaMetaDataLoader.isPresent()) {
            try {
                return dialectSchemaMetaDataLoader.get().load(material.getDataSource(), material.getActualTableNames(), material.getDefaultSchemaName());
            } catch (final Exception ex) {
                log.debug("Dialect load schema meta data error.", ex);
            }
        }
        //找不到则走默认模式,扫描每一个表,判断是否存在
        return loadByDefault(material);
    }
}

找不到当前 OSCAR 的方言模式元数据加载器(暂不清楚用途,大概率是获取表的详细信息),就会加载默认方式,扫描每一张表,判断表是否存在(速度很慢,自定义方言模式应该会通过简单的几个SQL把所有表元信息都查询到,效率高,但由于不用数据库语法不同,需根据不同的数据库定制开发),构造表元数据,默认模式下,由于分片表配置的是小写字母,会存在找不到的存在,给过滤掉,因此需要修改配置文件,把分片表名修改为大写

原始:

rules:
  - !SHARDING
    tables:
      aud_rest_operation_log:
        actualDataNodes: ds.aud_rest_operation_log_$->{2023..2023}_$->{05..06}

修改后:

rules:
  - !SHARDING
    tables:
      AUD_REST_OPERATION_LOG:
        actualDataNodes: ds.AUD_REST_OPERATION_LOG_$->{2023..2023}_$->{05..06}
3. SQL 解析器引发的异常

关于 SQL 解析器的问题,由于使用的父数据库类型为 PG,因此会匹配使用 PG 的 SQL 解析器。PG 解析器在处理部分 SQL 存在问题SQL 解析器需要定制化开发,暂无解决方案

达梦

接入注意点

  • 分片表需要把主键 id 的自增功能取消掉

    error->仅当指定列列表,且SET IDENTITY_INSERTON时,才能对自增列赋值
    //SET IDENTITY_INSERT = ON生命周期为当前连接,使用此参数进行解决,不现实
    

遇到的问题

1. SQL92解析器

由于默认会匹配为SQL92类型数据库类型,因此使用的 SQL 解析器为 SQL92 解析器,它解析不了部分 SQL

  1. 复杂子查询

    insert into "xxxx" ("a_id", "b_id", "distance") 
    select "a_id", "b_id", "distance" from ((select "a_id", ? as "b_id", ("distance" + 1) as "distance" from "xxxx" where "b_id" = ?) union all (select ?, ?, 0 from dual)) as "a"
    

修改当前数据库类型的父类型为 MYSQL,使用 MySQL 的解析器,可成功运行上述 SQL

public final class DmDatabaseType implements BranchDatabaseType {

    @Override
    public Collection<String> getJdbcUrlPrefixes() {
        return Collections.singleton(String.format("jdbc:%s:", "dm"));
    }

    @Override
    public MySQLDataSourceMetaData getDataSourceMetaData(final String url, final String username) {
        return new MySQLDataSourceMetaData(url);
    }

    @Override
    public String getType() {
        return "DM";
    }

    @Override
    public DatabaseType getTrunkDatabaseType() {
        return TypedSPILoader.getService(DatabaseType.class, "MySQL");
    }

}

金仓

遇到的问题

1. 金仓驱动未实现部分元数据接口

Shardingsphere 启动会通过元数据接口获取数据库的相应数据,启动会弹出错误,但不影响进程启动

java.sql.SQLFeatureNotSupportedException: Method com.kingbase8.jdbc.KbDatabaseMetaData.getRowIdLifetime() is not yet implemented.
	at com.kingbase8.Driver.notImplemented(Driver.java:726)
	at com.kingbase8.jdbc.KbDatabaseMetaData.getRowIdLifetime(KbDatabaseMetaData.java:2670)
	at com.zaxxer.hikari.pool.HikariProxyDatabaseMetaData.getRowIdLifetime(HikariProxyDatabaseMetaData.java)
2. ResultSet

和神通一样,金仓的Result返回的是 count,需要自定义数据类型,转为 PG

3. 方言SQL解析器(未出异常)

翰高

遇到的问题

1. 瀚高驱动未实现部分元数据接口

和金仓问题一致,Shardingsphere 启动会通过元数据接口获取数据库的相应数据,启动会弹出错误,但不影响进程启动

java.sql.SQLFeatureNotSupportedException: 这个 com.highgo.jdbc.jdbc.PgDatabaseMetaData.getRowIdLifetime() 方法尚未被实作。
	at com.highgo.jdbc.Driver.notImplemented(Driver.java:856)
	at com.highgo.jdbc.jdbc.PgDatabaseMetaData.getRowIdLifetime(PgDatabaseMetaData.java:2687)
2. ResultSet

和金仓一样,金仓的 Result 返回的是 Count,需要自定义数据类型,转为 PG

3. 查询表大小写

瀚高数据库表名不能用大写,因此 Shardingsphere 配置的分片表名需要为小写,这个和神通相反

rules:
  - !SHARDING
    tables:
      aud_rest_operation_log:
        actualDataNodes: ds.aud_rest_operation_log_$->{2023..2023}_$->{05..06}
4. 方言SQL解析器(未出异常)
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐