写点什么

ShardingSphere 分库分表 schema 名称导致 NPE 问题排查记录

作者:小小怪下士
  • 2023-01-10
    湖南
  • 本文字数:3103 字

    阅读完需:约 10 分钟

前段时间把 ShardingSphere 升级到了 5.1.1 版本,奈何官方版本升级太快跟不上速度,这不最近又发现了一个 BUG。

问题现象

数据库做了分库分表,在需要查询多表数据进行 merge 的时候发生了一个 NPE 的异常。


Caused by: java.lang.NullPointerException    at org.apache.shardingsphere.sharding.merge.dql.orderby.OrderByValue.getOrderValuesCaseSensitiveFromTables(OrderByValue.java:73) ~[shardingsphere-sharding-core-5.1.1.jar:5.1.1]    at org.apache.shardingsphere.sharding.merge.dql.orderby.OrderByValue.getOrderValuesCaseSensitive(OrderByValue.java:64) ~[shardingsphere-sharding-core-5.1.1.jar:5.1.1]    at org.apache.shardingsphere.sharding.merge.dql.orderby.OrderByValue.<init>(OrderByValue.java:58) ~[shardingsphere-sharding-core-5.1.1.jar:5.1.1]    at org.apache.shardingsphere.sharding.merge.dql.orderby.OrderByStreamMergedResult.orderResultSetsToQueue(OrderByStreamMergedResult.java:56) ~[shardingsphere-sharding-core-5.1.1.jar:5.1.1]    at org.apache.shardingsphere.sharding.merge.dql.orderby.OrderByStreamMergedResult.<init>(OrderByStreamMergedResult.java:50) ~[shardingsphere-sharding-core-5.1.1.jar:5.1.1]    at org.apache.shardingsphere.sharding.merge.dql.ShardingDQLResultMerger.build(ShardingDQLResultMerger.java:89) ~[shardingsphere-sharding-core-5.1.1.jar:5.1.1]    at org.apache.shardingsphere.sharding.merge.dql.ShardingDQLResultMerger.merge(ShardingDQLResultMerger.java:63) ~[shardingsphere-sharding-core-5.1.1.jar:5.1.1]    at org.apache.shardingsphere.infra.merge.MergeEngine.executeMerge(MergeEngine.java:90) ~[shardingsphere-infra-merge-5.1.1.jar:5.1.1]    at org.apache.shardingsphere.infra.merge.MergeEngine.merge(MergeEngine.java:80) ~[shardingsphere-infra-merge-5.1.1.jar:5.1.1]    at org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSpherePreparedStatement.mergeQuery(ShardingSpherePreparedStatement.java:487) ~[shardingsphere-jdbc-core-5.1.1.jar:5.1.1]    at org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSpherePreparedStatement.getResultSet(ShardingSpherePreparedStatement.java:435) ~[shardingsphere-jdbc-core-5.1.1.jar:5.1.1]    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.getFirstResultSet(DefaultResultSetHandler.java:237) ~[mybatis-3.5.3.jar:3.5.3]    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSets(DefaultResultSetHandler.java:187) ~[mybatis-3.5.3.jar:3.5.3]    at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:65) ~[mybatis-3.5.3.jar:3.5.3]    at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:79) ~[mybatis-3.5.3.jar:3.5.3]    at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:63) ~[mybatis-3.5.3.jar:3.5.3]    at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:324) ~[mybatis-3.5.3.jar:3.5.3]    at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156) ~[mybatis-3.5.3.jar:3.5.3]    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:109) ~[mybatis-3.5.3.jar:3.5.3]    at jdk.internal.reflect.GeneratedMethodAccessor346.invoke(Unknown Source) ~[?:?]    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]    at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?]    at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:63) ~[mybatis-3.5.3.jar:3.5.3]    at com.sun.proxy.$Proxy410.query(Unknown Source) ~[?:?]    at com.github.pagehelper.PageInterceptor.intercept(PageInterceptor.java:108) ~[pagehelper-5.1.11.jar:?]    at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:61) ~[mybatis-3.5.3.jar:3.5.3]    at com.sun.proxy.$Proxy410.query(Unknown Source) ~[?:?]    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:147) ~[mybatis-3.5.3.jar:3.5.3]    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140) ~[mybatis-3.5.3.jar:3.5.3]    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]    at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?]    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:427) ~[mybatis-spring-2.0.6.jar:2.0.6]    ... 95 more
复制代码

问题排查

跟踪到报错的地方,发现是这个地方的schema是个null,从而引发了 NPE。


null


一路往上看代码,最终定位到了这个获取schema的地方,也就是元数据去getDefaultSchema获取默认的schema名称改的时候拿到了一个空值。


null


进入这个方法后发现通过schemaschemas这个map里获取名称的时候是个空值,debug到这个地方其实发现了问题。


我们的schemaName配置的是orderTrade包含有大写字符的,所以name传进来的是orderTrade,但是问题是这个schemas确是ordertrade


null


所以很显然,这里获取不到正确的schema名称,导致了这个 NPE 的异常,那么问题是这个schemas是怎么加载进来的呢?


我们发现schemas是在创建元数据的时候,通过构造函数赋值的,那么只要找到这个赋值的地方应该就能发现问题了。


null


通过一番查找,找到了调用的地方,这个schemas值就是databaseMap中的value,那么我们要继续看这个databaseMap是如何初始化来的。


null


继续看源码,找到了databaseMap进行初始化的地方,原来是通过DatabaseLoader去加载元数据的时候初始化的,那么这个load方法是怎么处理的呢?


null


从代码来看他包含了两部分的信息,第一个是我们自己通过schema配置的一些分库分表的配置信息,另外一部分则是数据库默认的一些表的元数据,比如mysqlinformation_schema这些,那我们只要看自己配置的那部分就可以了,也就是SchemaLoader.load(dataSourceMap, rules, props)方法。


null


看他实际上就是获取数据库是什么类型,比如mysql,然后去加载表的元数据,最后new出来ShardingSphereSchema,直接看最后的new部分代码就行了。


null


进入这个方法,瞬间就真相大白了,原来在put的时候对所有的schemaName进行了小写处理,所以在最上面我们去get的时候肯定会拿到一个空值,最终导致merge的时候发生了 NPE 异常。


null

解决方案

现在问题原因已经发现了,那么该如何解决呢?总不能不让别人配置的时候不让写大写吧,本着能不能白嫖一个 PR 的想法,又去给 Sharding 提了一个 Issue。


null


就我点了根烟的功夫,回头就给我回复说新版本已经修复了,希望落空了,修复方案就是查询的时候也做小写处理了,好吧,那就这样吧。


null


用户头像

还未添加个人签名 2022-09-04 加入

热衷于分享java技术,一起交流学习,探讨技术。 需要Java相关资料的可以+v:xiaoyanya_1

评论

发布
暂无评论
ShardingSphere分库分表schema名称导致NPE问题排查记录_Java_小小怪下士_InfoQ写作社区