共计 5396 个字符,预计需要花费 14 分钟才能阅读完成。
前段时间把 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。
一路往上看代码,最终定位到了这个获取 schema
的中央,也就是元数据去 getDefaultSchema
获取默认的 schema
名称改的时候拿到了一个空值。
进入这个办法后发现通过 schema
去schemas
这个 map
里获取名称的时候是个空值,debug
到这个中央其实发现了问题。
咱们的 schemaName
配置的是 orderTrade
蕴含有大写字符的,所以 name
传进来的是 orderTrade
,然而问题是这个schemas
确是ordertrade
。
所以很显然,这里获取不到正确的 schema
名称,导致了这个 NPE 的异样,那么问题是这个 schemas
是怎么加载进来的呢?
咱们发现 schemas
是在创立元数据的时候,通过构造函数赋值的,那么只有找到这个赋值的中央应该就能发现问题了。
通过一番查找,找到了调用的中央,这个 schemas
值就是 databaseMap
中的 value
,那么咱们要持续看这个databaseMap
是如何初始化来的。
持续看源码,找到了 databaseMap
进行初始化的中央,原来是通过 DatabaseLoader
去加载元数据的时候初始化的,那么这个 load
办法是怎么解决的呢?
从代码来看他蕴含了两局部的信息,第一个是咱们本人通过 schema
配置的一些分库分表的配置信息,另外一部分则是数据库默认的一些表的元数据,比方 mysql
、information_schema
这些,那咱们只有看本人配置的那局部就能够了,也就是 SchemaLoader.load(dataSourceMap, rules, props)
办法。
看他实际上就是获取数据库是什么类型,比方 mysql
,而后去加载表的元数据,最初new
进去 ShardingSphereSchema
,间接看最初的new
局部代码就行了。
进入这个办法,霎时就水落石出了,原来在 put
的时候对所有的 schemaName
进行了小写解决,所以在最下面咱们去 get
的时候必定会拿到一个空值,最终导致 merge
的时候产生了 NPE 异样。
解决方案
当初问题起因曾经发现了,那么该如何解决呢?总不能不让他人配置的时候不让写大写吧,本着能不能白嫖一个 PR 的想法,又去给 Sharding 提了一个 Issue。
就我点了根烟的功夫,回头就给我回复说新版本曾经修复了,心愿落空了,修复计划就是查问的时候也做小写解决了,好吧,那就这样吧。