Spark 2.1.1~2.3.2查询远程Hive表返回空结果问题咨询
问题背景
测试覆盖Spark 2.1.1到Spark 2.3.2多个版本,通过JDBC方式查询远程Hive表时出现返回空结果的异常,复现过程如下:
- 复现代码:
import org.apache.spark.sql.SQLContext val sqlContext = new SQLContext(sc) val dataframe_mysql = sqlContext.read.format("jdbc") .option("url", "jdbc:hive2://10.0.0.28:10000/default") .option("driver", "org.apache.hive.jdbc.HiveDriver") .option("dbtable", "load1") .load() dataframe_mysql.show()
- Hive侧实际接收的查询语句:
- 元数据拉取阶段执行:
SELECT * FROM load1 WHERE 1=0 - 执行
dataframe_mysql.show()触发数据拉取阶段执行:SELECT "load1.data_id","load1.id","load1.gender","load1.totcount","load1.fname" FROM load1
- 元数据拉取阶段执行:
- 实际返回结果:仅展示表字段名,无任何数据行,输出如下:
scala> dataframe_mysql.show() +--------+-----------+------------+--------------+-------------+ |load1.id|load1.fname|load1.gender|load1.totcount|load1.data_id| +--------+-----------+------------+--------------+-------------+ +--------+-----------+------------+--------------+-------------+
问题成因
该问题是Spark JDBC方言和Hive SQL语法不匹配导致的:
- Spark JDBC数据源默认使用通用SQL方言生成查询语句,引用列名、表名等标识符时默认使用双引号,但Hive默认配置下双引号用于包裹字符串字面量,标识符引用需使用反引号,双引号包裹的内容不会被解析为列引用。
- Spark生成列引用时逻辑存在缺陷,将
表名.列名整体放入双引号包裹,而非分别引用表名和列名,生成的语句不符合Hive的解析规则,最终查询仅能返回字段元信息,拉取不到实际数据行。
解决方案
可根据实际场景选择以下任意一种方案修复:
- 方案1:通过子查询传入dbtable参数,绕过Spark自动拼接列的逻辑
不要直接将表名传入dbtable参数,改用明确指定列的子查询,让Spark直接执行手写的合法Hive SQL,避免自动生成语法错误的语句,示例代码:
val dataframe_mysql = sqlContext.read.format("jdbc") .option("url", "jdbc:hive2://10.0.0.28:10000/default") .option("driver", "org.apache.hive.jdbc.HiveDriver") .option("dbtable", "(SELECT data_id, id, gender, totcount, fname FROM load1) AS tmp") .load()
- 方案2:调整Hive配置兼容双引号标识符
在Hive集群配置中开启hive.support.sql11.reserved.keywords=true,开启后Hive支持使用双引号引用标识符,可兼容Spark自动生成的查询语句。注意该配置会修改Hive的默认语法规则,需提前评估对现有线上任务的影响。 - 方案3:使用Spark原生Hive集成替代JDBC连接
不需要通过JDBC方式访问Hive,直接将Hive集群的hive-site.xml配置文件放到Spark的conf目录下,启用Spark的Hive支持后,直接通过sqlContext.table("load1")即可读取Hive表数据,该方式不存在语法兼容问题,且读取性能远高于JDBC方式。
内容的提问来源于stack exchange,提问作者SUBIN K SOMAN
相关产品推荐
相关产品推荐

