所以我做的事情应该很简单,但显然它不在Spark SQL中。
如果我在MySQL中运行以下查询,查询将在几分之一秒内完成:
SELECT ua.address_id
FROM user u
inner join user_address ua on ua.address_id = u.user_address_id
WHERE u.user_id = 123;
但是,在Spark(1.5.1)下的HiveContext中运行相同的查询需要超过13秒。添加更多联接会使查询运行很长时间(超过10分钟)。我不确定我在这里做错了什么,以及如何加快速度。
这些表是作为临时表加载到Hive上下文中的MySQL表。这是在单个实例中运行,数据库在远程计算机上运行。
这些表具有外键字段,但在db中没有定义明确的fk关系。我正在使用InnoDB。
Spark中的执行计划:
安排:
扫描 JDBCRelation(JDBC:MySQL的:// 。用户,[Lorg.apache.spark.Partition; @ 596f5dfc, {user = ,password = ,url = jdbc:mysql:// ,dbtable = user}) [ADDRESS_ID#0L,user_address_id#27L]
过滤器(user_id#0L = 123)扫描 JDBCRelation(JDBC:MySQL的:// .user_address, [Lorg.apache.spark.Partition; @ 2ce558f3,{user = ,密码= , url = jdbc:mysql:// ,dbtable = user_address})[address_id#52L]
ConvertToUnsafe ConvertToUnsafe
TungstenExchange hashpartitioning(address_id#52L)TungstenExchange hashpartitioning(user_address_id#27L)TungstenSort [address_id#52L ASC],false,0 TungstenSort [user_address_id#27L ASC],false,0
SortMergeJoin [user_address_id#27L],[address_id#52L]
==物理计划== TungstenProject [address_id#0L]
答案 0 :(得分:4)
首先执行的查询类型效率极低。至于现在(Spark 1.5.0 *)执行这样的连接,每次执行查询时都必须对两个表进行混洗/散列分区。如果users
表中user_id = 123
谓词最有可能被推下但仍需要user_address
完全随机播放,则不应该出现问题。
此外,如果表只是注册而不是缓存,那么每次执行此查询都会将整个user_address
表从MySQL提取到Spark。
我不确定我在这里做错了什么以及如何加快速度。
目前还不清楚为什么要将Spark用于应用程序,但单机设置,小数据和查询类型表明Spark不适合这里。
一般来说,如果应用程序逻辑需要单个记录访问,那么Spark SQL就不会表现良好。它专为分析查询而设计,而不是作为OLTP数据库的替代品。
如果单个表格/数据框小得多,您可以尝试广播。
import org.apache.spark.sql.DataFrame
import org.apache.spark.sql.functions.broadcast
val user: DataFrame = ???
val user_address: DataFrame = ???
val userFiltered = user.where(???)
user_addresses.join(
broadcast(userFiltered), $"address_id" === $"user_address_id")
*这应该在Spark 1.6.0中使用SPARK-11410进行更改,这应该启用持久表分区。
答案 1 :(得分:3)
在类似的情况下我遇到了同样的问题(Spark 1.5.1,PostgreSQL 9.4)。
给出两个表格,如
val t1 = sqlContext.read.format("jdbc").options(
Map(
"url" -> "jdbc:postgresql:db",
"dbtable" -> "t1")).load()
val t2 = sqlContext.read.format("jdbc").options(
Map(
"url" -> "jdbc:postgresql:db",
"dbtable" -> "t2")).load()
然后在已注册的临时表上连接HQL会导致对其中一个表进行全表扫描(在我的例子中,它是孩子)。
无论如何,解决方法是将查询推送到底层RDBMS:
val joined = sqlContext.read.format("jdbc").options(
Map(
"url" -> "jdbc:postgresql:db",
"dbtable" -> "(select t1.*, t2.* from t1 inner join t2 on ...) as t")).load()
这样底层RDBMS的查询优化器就会启动,在我的情况下,它会切换到索引扫描。另一方面,Spark推下了两个独立的查询,而RDBMS无法真正优化它。