ShardingSphere对国产数据库的支持
ShardingSphere对国产数据库金仓、翰高、达梦和神通的支持
综述
数据库类型 | 问题 | 解决 | 结论 |
---|---|---|---|
Oscar | 1. RestultSet返回Count 2. 无SQL方言元数据加载器,执行默认模式速度慢 3. Sharding配置表名需为大写 3. 使用的PG SQL解析器,解析不了部分SQL | 1. 数据类型切换为PG 2. 未解决 3. 已解决 3. 未解决 | X |
Dm | 1. 使用的SQL92 SQL解析器,解析不了部分SQL | 1. 数据类型切换为Mysql | √ |
Kingbase | 1. ResultSet返回Count | 1. 数据类型切换为PG | √ |
Highgo | 1. 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_INSERT为ON时,才能对自增列赋值 //SET IDENTITY_INSERT = ON生命周期为当前连接,使用此参数进行解决,不现实
-
…
遇到的问题
1. SQL92解析器
由于默认会匹配为SQL92类型数据库类型,因此使用的 SQL 解析器为 SQL92 解析器,它解析不了部分 SQL
-
复杂子查询
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解析器(未出异常)
更多推荐
所有评论(0)