作者回复: 1)SQL也是可以的哈~ 不过得用到spark-sql CLI,或是Beeline这样的客户端 2)2.x还不行,这里需要利用到3.x才有的DPP机制
作者回复: 好问题,colocated joins很有意思,它其实特指的是这种情况,参与Join的两张表: 1. 并行度一样 2. 以Join Key为基准,数据分布一致 满足这两个条件的两张表参与的Join,就叫colocated joins。 其实,说白了,就是两张表的数据分布完全一致(相对Join Key来说)。因此,对他们做关联,自然不再需要Shuffle环节,因为两张表的数据分片都已经相互“配好对”了,两边Join Keys相同的数据记录,都分布在同样Executors中。
作者回复: 对,非常好~ 满分💯!你说的这个,其实就是collocated joins,collocated joins是一种特殊的joins,就是参与Join的两表提前按照Join Key都做了重分布,那么再次关联的时候,自然不会引入Shuffle~ 不过,对于两个bucket表,要求他们的bucket id是一致的,都要包含Join Key,否则关联的时候还是会有Shuffle。
作者回复: 唯一的办法是把CPJ转化为BNLJ,如果内表不够小,没法广播,必须要用CPJ来实现,坦白说,还真没什么好办法。这也是我们为什么要极力避免CPJ的原因~
作者回复: 好问题,DPP确实有这方面的限制,就是要求Join Key本身是分区键,这个要求确实比较苛刻。Join Key和分区键确实本身是一对矛盾,因为通常来说,Join Key的cardinality都不小,而分区键却要求cardinality不能太大。 你说的是对的,我们这个例子其实举的不好,这里为了demo DPP,我们选择了orderID,但实际上就像你说的,orderID本身的cardinality太大了,不适合做分区键。 所以说,利用DPP机制有个很重要的前提,就是数仓的设计,也就是结合后续常用的查询,提前把表结构设计好。这个表结构的设计,就需要考虑Join Keys和分区键的博弈~
作者回复: 还有一种思路,就是系统集成。 之前有一个内存分布式文件系统,叫Tachyon,后来改名叫Alluxio,可以理解为内存里面的HDFS。它提供内存级别的文件系统。 Alluxio不少公司在用,就用来加速数据访问的,他们提前把“热数据”从HDFS加载到Alluxio,然后Spark再从Alluxio访问分布式内存文件,把磁盘I/O变成了内存访问。读取效率自然能提升不少。 当然,做这种系统集成的前提是“有钱”,得有足够的预算来购置大量内存,不过,还是那句话,调优最重要的是思路和手段,每种方法都有其优缺点。确实有大厂宁愿多花钱买内存来节省时间。 总之,这种系统集成的思路也是一种优化选项,在条件具备的时候,不妨加以考虑~ 其实沿着这个思路,你还可以发现更多集成的可能,Alluxio这里仅仅是举个例子加以说明,其实咱们作为开发者,平时要多关注技术动态和进展,开阔眼界和思路,我觉得这一点非常重要,个人观点哈~ 不一定对~ 一个新技术,不管将来用不用得上,多了解、熟悉一些,总是不吃亏的~
作者回复: 需要结合DPP机制才行哈~
作者回复: 这样做确实能降低分区键的Cardinality,从而缓解分区存储的压力。不过按照尾号对orderId做关联,业务逻辑上似乎不成立。因为一般来说,肯定是orderId要完全一致Join在一起才会有意义,否则,仅仅是尾号相同、但实际是不同的orderId,他们join在一起其实业务逻辑上说不通的~ 不过,话说回来,如果确实关联条件就是orderId的尾号,那么在运行时也是会触发DPP的,毕竟你说的这种场景是满足DPP的各种前提条件的~
作者回复: 是的,主要是表大小,通常来说,大多数数据库引擎,都会把尺寸比较大的表,当作外表;而尺寸较小的表,当作内表。
作者回复: 这里面tx表是事实表哈,它的分区键是orderId,如下所示: //lineitems表的关键字段 orderId: Int //分区键 txId: Int itemId: Int price: Float quantity: Int Join key是orderId,也就是on tx.orderId = o.orderId,因此就这个例子来说,是可以触发DPP的,当然,tx表选orderId作为分区键,是值得商榷的。