一 、MapReduce实现sql操作原理

1.1  join实现原理  

 对于 join 操作

SELECT pv.pageid, u.age FROM page_view pv JOIN user u ON pv.userid = u.userid;

实现过程

        

       Map

          1、以 JOIN ON 条件中的列作为 Key,如果有多个列,则 Key 是这些列的组合

          2、以 JOIN 之后所关心的列作为 Value,当有多个列时,Value 是这些列的组合。在 Value 中还会包含表的 Tag 信息,用于标明此 Value 对应于哪个表

          3、按照 Key 进行排序

      Shuffle

         1、根据 Key 的值进行 Hash,并将 Key/Value 对按照 Hash 值推至不同对 Reduce 中

      Reduce

         1、 Reducer 根据 Key 值进行 Join 操作,并且通过 Tag 来识别不同的表中的数据

具体实现过程

二、小表与大表关联的性能分析

例如user u join page p ON (u.userid = p.userid) ,假设A表和B表都有1条userid = 222的记录。排序时可以保证user表的记录在page表的记录的前面。而在reduce做处理时,把userid = 222的放在同一个value list中,形成 key = 111,value list = [user表userid=222的记录,page表userid=222的记录] 

        首先来看当两个表做关联时reduce做了什么。Reduce会一起处理key相同的所有记录.我们把value list用数组来表示。

       1)   Reduce先读取第一条记录v[0],如果发现v[0]是page表的记录,那说明没有user表的记录,最终不会关联输出,因此不用再继续处理这个userid了,读取v[0]用了1次读取操作。 
       2)   如果发现v[0]到v[length-1]全部是user表的记录,那说明没有page表的记录,同样最终不会关联输出,但是这时,已经对value做了length次的读取操作。

       3)   例如user表userid=222有1条记录,page表userid=222有10条记录。首先读取v[0]发现是user表的记录,用了1次读取操作。然后再读取v[1]发现是page表的操作,这时v[0]和v[1]可以直接关联输出了,累计用了2次操作。这时候reduce已经知道从v[1]开始后面都是page表的记录了,因此可以直接用v[0]依次和v[2],v[3]……v[10]做关联操作并输出,累计用了11次操作。 
       4)   换过来,假设user表userid=222有10条记录,page表userid=222有1条记录。首先读取v[0]发现是user表的记录,用了1次读取操作。然后再读取v[1]发现依然是user表的记录,累计用了2次读取操作。以此类推,读取v[9]时发现还是user表的记录,累计用了10次读取操作。然后读取最后1条记录v[10]发现是page表的记录,可以将v[0]和v[10]进行关联输出,累计用了11次操作。接下来可以直接把v[1]~v[9]分别与v[10]进行关联输出,累计用了20次操作。

       5)  注意 ,当reduce检测user表的记录时,还要记录user表同一个key的记录的条数,当发现同一个key的记录个数超过hive.skewjoin.key的值(默认为1000000)时,会在reduce的日志中打印出该key,并标记为倾斜的关联键。 
       最终得出的结论是:写在关联左侧的表每有1条重复的关联键时底层就会多1次运算处理,假设A表有一千万个id,平均每个id有3条重复值,那么把A表放在前面做关联就会多做三千万次的运算处理,这时候谁写在前谁写在后就看出性能的差别来了。