摘要: small is beautiful,small is powerful
http://click.aliyun.com/m/40815/
大数据计算服务(MaxCompute,原名 ODPS)是一种快速,完全托管的 GB/TB/PB 级数据仓库解决方案.
https://help.aliyun.com/document_detail/27800.html?spm=5176.7840267.6.539.po3IvS
主要有三种操作数据的方式 SQL,UDF,MapReduce,了解 hadoop 的同学就比较熟悉这些东西了.
那么Maxcompute的SQL和标准SQL最大的区别就是在Maxcompute中SQL会被解析成MapReduce去执行,当然也可以直接去写MapReduce去计算数据,UDF就是当自带的一些sql引用的函数不能满足业务计算的时候,自己通过代码编写一个函数,sql执行的时候引用.
由此可见实际上底层的计算都是依靠 MapReduce 这个计算引擎去执行.首先了解下什么是 MapReduce.一份数据很大的时候在 MaxCompute 上是分布式存储的,也就是会分开存放到很多服务器,当一个任务执行的时候会从这些数据所在的服务器上启动一个进程读取这些数据,进行计算等操作,还会启动一个进程把这些数据进行汇总分析并输出.那前者进程叫做 Map,后者进程叫做 Reduce,合起来叫 MapReduce 任务.
使用 sql 操作数据的时候,会经常用到 join.比如 select * from A a join B b on a.id=b.id,这句 sql 在转换成 MapReduce 任务执行的时候:
1,map 任务读数据,并对两个表的数据打上不同的 tag 用来区分
2,reduce 端接收打标记的数据,将不同标记的表数据相同关联字段的数据放在一起输出
假设有两个表,我们暂且叫做 Big 表和 Small 表,其中 Big 表数据量比较大,分布式存在 n 台实例服务器上,Small 表存在于一台服务器就放下了.
首先 MaxCompute 会启动一些 Map 的进程(Map 任务)去读取这些数据分别打上标记,Map 的个数是由一个参数控制的这里暂时不解释了.注意对于读取 Big 表的每个 Map 任务有可能在其他服务器上,那么这时候就需要到数据所在的服务器上把数据拉过来,Small 表也会启动一个或者几个 map 任务读取文件系统中的数据,读取完成后会到 Reduce 端接收数据进行关联,判断关联字段相等的就放在一起输出,达到关联效果.
我们可以看一个例子,我准备了一个相对大的表 train_user_lt,5G 大小,数据大概 7 亿条.
准备了一个比较小的表 map_join_test,只有 3 条数据.
select a.* from train_user_lt a left outer join map_join_test b on a.user_id = b.user_id;
执行了这句 sql,如图
这个执行的过程图是 Maxcompute 特有的可以帮助用户来查看任务执行的过程等叫做 logview, 是一个在 ODPS Job 提交后查看和 Debug 任务的工具 https://help.aliyun.com/document_detail/27987.html
从图中可以看出分为三部分
1,大的表 train_user_lt 启动了 39 个 map 任务去读取数据 707025259 条
2,小的表启动一个 map 任务读取 3 条数据.
3,reduce 阶段接收了 3+707025259=707025262 条数据,输出了 707025259 条数据,left outer join 按照左边的大表输出.
但是看下消耗的时间是 40 分钟,这样来说算是很长的时间的.那么怎么优化提高速度呢,有没有一种比较方便,比较直接暴力的方式进行优化呢
那么本文的重点就来了-Mapjoin:
MAPJION 会把小表全部读入内存中,把小表拷贝多份分发到大表数据所在实例上的内存里,在 map 阶段直接拿另外一个表的数据和内存中表数据做匹配,由于在 map 是进行了 join 操作,省去了 reduce 运行的效率会高很多.
使用的条件就是当一个大表和一个或多个小表做 join 时.SQL 会将用户指定的小表全部加载到执行 join 操作的程序的内存中,从而加快 join 的执行速度.需要注意,在 Maxcompute 使用 mapjoin 时:
left outer join 的左表必须是大表;
right outer join 的右表必须是大表;
inner join 左表或右表均可以作为大表;
full outer join 不能使用 mapjoin;
mapjoin 支持小表为子查询;
使用 mapjoin 时需要引用小表或是子查询时,需要引用别名;
在 mapjoin 中,可以使用不等值连接或者使用 or 连接多个条件;
目前 MaxCompute 在 mapjoin 中最多支持指定 8 张小表,否则报语法错误;
如果使用 mapjoin,则所有小表占用的内存总和不得超过 512MB.请注意由于 MaxCompute 是压缩存储,因此小表在被加载到内存后,数据大小会急剧膨胀.此处的 512MB 限制是加载到内存后的空间大小;
多个表 join 时,最左边的两个表不能同时是 mapjoin 的表.
那么为什么说 left outer join 的左表必须是大表呢,
因为左表是大表的时候,会拿小表的全部数据和大表所在的实例服务器中的数据匹配一遍,刚好小表就在内存里.如果是左表是小表,那么需要把大表所有的数据拉过来跟小表匹配一遍,试想一下性能会如何.
来看下写法
select /* + mapjoin(b) */ a.* from train_user_lt a left outer join map_join_test b on a.user_id = b.user_id;
//就是在sql语句前加一个标记说这是mapjoin,把小表别名写在括号里
看下优化后的效果
任务变成了两个部分,map 端直接读取数据和内存里的小表进行关联,然后输出,少了一步 reduce.也就是说关联从 reduce 转到 map 端进行 join,省去了 reduce 这一步,所以叫做:mapjoin.
看下执行时间 1 分钟 20 多秒.之前是 40 分钟.当然我这边测试是把两个比较极端的数据进行比较,所以效果比较明显.由此看来大表关联小表的时候可以使用 mapjoin 进行优化查询.
那么 mapjoin 除了优化性能,还可以干什么呢.
MaxCompute SQL 不支持支持在普通 join 的 on 条件中使用不等值表达式,or ,like 等逻辑等复杂的 join 条件,但是在 mapjoin 中可以进行如上操作.例如
select /*+ mapjoin(a) */
a.total_price,
b.total_price
from shop a join sale_detail b
on a.total_price < b.total_price or a.total_price + b.total_price < 500;
总结:mapjoin 看似很小的操作变化,实际上可以带来很大效率提升,另外还可以解决一些不等关联的业务场景.
正如马云经常说的一句话:
small is beautiful,small is powerful !
来源: http://geek.csdn.net/news/detail/252613