Apache Phoenix自定义UDF在WHERE子句中报错:类未找到
我之前也碰到过类似的问题,Phoenix的UDF在SELECT里正常,但放到WHERE子句就报类找不到,核心原因其实是执行位置的差异:
- 当UDF在SELECT中使用时,逻辑是在客户端(Phoenix Query Server或你的JDBC客户端)执行的,所以客户端能加载到jar里的类就没问题;
- 但放到WHERE子句时,Phoenix会把过滤逻辑下推到HBase RegionServer端执行,这时候RegionServer需要能找到UDF的类文件,而你的配置或操作可能没让RegionServer成功加载到这个jar。
先帮你理清楚当前的情况:
- 你已经将UDF jar上传到了
hbase.dynamic.jars.dir指定的HDFS目录hdfs://localhost:57000/user/hbase/lib/ - 创建函数的语句正确指定了jar路径
- SELECT调用UDF正常,但WHERE子句触发以下错误:
Caused by: org.apache.hadoop.hbase.DoNotRetryIOException: BooleanExpressionFilter failed during reading: java.lang.ClassNotFoundException: com.myCompany.phoenix.MyCustomFunction at org.apache.phoenix.util.ServerUtil.createIOException(ServerUtil.java:96) at org.apache.phoenix.util.ServerUtil.throwIOException(ServerUtil.java:62) at org.apache.phoenix.filter.BooleanExpressionFilter.readFields(BooleanExpressionFilter.java:109) at org.apache.phoenix.filter.SingleKeyValueComparisonFilter.readFields(SingleKeyValueComparisonFilter.java:133) at org.apache.hadoop.hbase.util.Writables.getWritable(Writables.java:131) at org.apache.hadoop.hbase.util.Writables.getWritable(Writables.java:101) at org.apache.phoenix.filter.SingleCQKeyValueComparisonFilter.parseFrom(SingleCQKeyValueComparisonFilter.java:50) ... 16 more
下面是针对性的解决步骤:
1. 验证动态jar目录的配置与权限
首先确认hbase-site.xml中的配置是否生效,特别是:
<property> <name>hbase.dynamic.jars.dir</name> <value>${hbase.rootdir}/lib/</value> </property> <property> <name>phoenix.functions.allowUserDefinedFunctions</name> <value>true</value> </property>
然后检查HDFS上的jar权限是否允许RegionServer读取:
hdfs dfs -ls hdfs://localhost:57000/user/hbase/lib/
确保jar文件的权限是-rw-r--r--(644),目录权限是drwxr-xr-x(755),如果权限不对,执行:
hdfs dfs -chmod 755 hdfs://localhost:57000/user/hbase/lib/ hdfs dfs -chmod 644 hdfs://localhost:57000/user/hbase/lib/my.jar
2. 重启HBase RegionServer
虽然HBase支持动态加载hbase.dynamic.jars.dir中的jar,但部分版本需要重启RegionServer才能识别新上传的jar包。重启所有RegionServer节点后,再执行你的测试SQL:
select PK FROM "my.custom.view" where MY_FUN(ARRAY["COLF"."COL1"], 'SOMEPARAM') limit 1;
3. 检查UDF jar的完整性
确认你的my.jar是胖jar(包含所有依赖类),如果UDF依赖了第三方库,这些依赖也需要被RegionServer加载。如果不是胖jar,把依赖的jar也上传到hbase.dynamic.jars.dir目录,或者重新打包UDF jar时包含所有依赖。
4. 确认jar路径的集群可访问性
如果是分布式集群环境,创建函数时指定的hdfs://localhost:57000/user/hbase/lib/my.jar中的localhost可能只在客户端有效,RegionServer节点可能无法通过localhost访问namenode。建议换成集群的namenode hostname或IP,比如:
CREATE FUNCTION MY_FUN(BINARY[], VARCHAR) RETURNS BOOLEAN as 'com.myCompany.phoenix.MyCustomFunction' using jar 'hdfs://namenode-host:57000/user/hbase/lib/my.jar';
5. 备选方案:将jar放到RegionServer本地lib目录
如果动态加载始终有问题,可以采用更稳妥的方式:把my.jar复制到所有RegionServer节点的$HBASE_HOME/lib/目录下,然后重启RegionServer。这种方式能确保RegionServer启动时就加载到UDF类,避免动态加载的问题。
附:你提供的相关操作与配置
手动上传jar的命令:
hdfs dfs -copyFromLocal -f /my.jar hdfs:///user/hbase/lib/my.jar
创建函数的SQL:
CREATE FUNCTION MY_FUN(BINARY[], VARCHAR) RETURNS BOOLEAN as 'com.myCompany.phoenix.MyCustomFunction' using jar 'hdfs://localhost:57000/user/hbase/lib/my.jar';
内容的提问来源于stack exchange,提问作者user234233

