ShardingSphereDataSource的Connection元数据误用,如何避免分库分表数据源问题?
摘要:背景 对于分库分表应用来说,使用org.apache.shardingsphere.driver.jdbc.core.datasource.ShardingSphereDataSource是一个不错的解决方案,你可以通过配置文件编写分库分表
背景
对于分库分表应用来说,使用org.apache.shardingsphere.driver.jdbc.core.datasource.ShardingSphereDataSource是一个不错的解决方案,你可以通过配置文件编写分库分表规则,从而在编码时透明地使用分表(当然,路由规则的相关字段还是要传的,之前也有文章分析过这些字段的处理过程:深入理解Mybatis分库分表执行原理)。
但是,在一些场景中是需要绕过mybatis直接做一些操作的,特别是和数据库元数据相关的操作(包括表的结构变更)。
比如我遇到的场景:先查询各个分库中有哪些前缀为table_的表,并给这些表加一列col_x。
我结合现有代码和大语言模型,先写了一版,线下运行良好,但是线上的某些分库死活找不到对应的分表,没法进行后续的处理。这个问题查了很久,昨天终于解决了,因此分享出来。
存在问题的代码
@Componet
public class TableAlterHandler {
@Resource private ShardingSphereDataSource dataSource;
public List<String> findTablesByPrefix(String prefix, String physicalSchemaName) {
if (StringUtils.isBlank(prefix) || StringUtils.isBlank(physicalSchemaName)) {
throw new RuntimeException("分表前缀或分库名为空");
}
List<String> tableNames = Lists.newArrayList();
try (HintManager hintManager = HintManager.getInstance();
Connection conn = dataSource.getConnection()) {
hintManager.setDataSourceName(DBUtil.queryLogicalSchemaName(physicalSchemaName));
DatabaseMetaData metaData = conn.getMetaData();
try (ResultSet rs = metaData.getTables(physicalSchemaName, null, prefix + "%", new String[] {"TABLE"})) {
while (rs.next()) {
String tableName = rs.getString("TABLE_NAME");
tableNames.add(tableName);
}
}
} catch (SQLException e) {
throw new RuntimeException("处理大结果集失败", e);
}
return tableNames;
}
}
逻辑库和物理库
在分析问题之前,首先要明确两个概念:物理库名physicalSchemaName和逻辑库名logicalSchemaName,如果用错了,可能会让你没办法发现后续问题的本质原因。上面的代码,hintManager必须用逻辑库名,而metaData.getTables必须用物理库名。
所谓物理库和逻辑库,可以看作是我定义的概念。正如其名,物理库名就是你jdbcUrl里的库名,比如一个典型的阿里云Mysql的JDBC链接jdbc:mysql://``rm-bpxxxx.mysql.rds.aliyuncs.com/bizcenter_1?useSSL=false&autoReconnect=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
其中的bizcenter_1就是物理库名。
