使用阿里云ODPS之点滴

标签: 阿里云 odps 点滴 | 发表时间:2014-10-07 11:48 | 作者:mozhenghua
出处:http://www.iteye.com

    趁着这次做网聚宝搜索服务化这个项目,体验了一下阿里云的重量级云产品ODPS,也就是传说中的云梯2。项目几近结束,现在回过头来看当初选择使用OPDS的决定还是相当正确的,ODPS在网聚宝搜索上云这个项目中起到了非常至关重要的作用,一句话总结:ODPS是非常靠谱的。从刚开始里了解ODPS开始,到引入项目中使用,进而通过优化加速在项目中使用得游刃有余,经历了一个过程,中间也走了不少弯路。在此将之前碰到问题点做一下总结,希望对有和我们一样需求的团队或者个人有所帮助。

 

终搜为什么选择ODPS

    搜索引擎都是为了解决业务方数据多维度随意组合的条件搜索的需求,终搜的产品也不例外,在构建索引的过程中有非常重要的一个步骤就是将业务数据库中的数据导入搜索引擎中。搜索引擎中保存的数据结构是单维度的记录,而网聚宝搜索上云项目中所用到的数据源是来自多个数据库的多张数据库表。对象实体是由一对多,多对多的表组成的。所以在数据导入到搜索引擎之前对数据进行扁平化处理成为一个必不可少的环节。

 

    实现这个操作在java社区有非常成熟的hadoop可以使用,基于hadoop之上的hive通过执行命令SQL化可以很好地屏蔽底层技术细节。这个是一个不错的选择,同时我们在方案选型的过程中了解到阿里集团在公有云中正在推广ODPS,并且已经开始在公有云中售卖。抱着试试看的想法,开启了ODPS之旅。

 

   其实现在总结起来,使用ODPS还有一个非常重要的原因,就是阿里云在云基础设施配套这块做得比较到位,比如,针对ODPS数据导入有CDP(Cloud Data pipeling)这个产品来负责数据导入工作。通过CDP,只需要简地将单数据的reader和writer通过XML描述发送给CDP中间件,就可以轻松将几个G甚至几个T的数据从RDS在短短几十分钟之内DUMP到ODPS中。有了CDP可以免去我们很多额外工作。

 

   在ODPS上执行多表数据扁平化工作,通俗地讲就是“打宽表”,举个最简单的例子,例如有一张会员表,一张交易表,两表的关系是一对多,那我们可以按照会员维度将两张表打成一张宽表。最终打好的宽表结构就变成,user.id,user.name,”trade.id,trade.id  …….” As tradeids。这对于ODPS来说小菜一碟,有现成的聚合函数可以使用,如wm_concat。终搜将宽表构建成索引,用户可以很方便地通过trade.id来反查user.id。这是一个最简单的例子,或许你会问处理这种问题数据库也是可以的。那我要提醒你,数据库处理的数量级和ODPS处理的数据级完全不在一个等级上。但凡数据上G上T之后,数据库只能干瞪眼了,而像ODPS这样的MR工具却可以轻松搞定,再多数据也只需要通过集群节点的水平扩容轻松搞定。

 

ODPS的收费

   对于收费这个问题,我们是使用的是集团弹外环境,按计算量来收费的,终搜因为是集团内部团队,可以通过申请ODPS抵价券来使用。基本上,打一个2亿条记录的宽表(需要关联多张表,且有比较复杂的聚合),一次收费大概是100元左右,在开发测试阶段是非常昂贵的,因为测试阶段往往是修改一点测试一下,确认结果是否符合预期。一个功能点往往要来来回回折腾好几次,因此而产生的费用可想而知了,所以在测试阶段不要用全部数据来进行测试开发,抽取一个样本子集来进行测试。这样,因为数据量少了,可以缩短计算时间,缩短测试周期,并且降低费用。

 

单个Select进行多维度统计

 

   在ODPS可以基于SQL来执行的,值得一提的是虽是SQL,但和一般关系数据库上执行的SQL还是有很多区别的。举一个例子,可以通过一个SQL语句来多维度统计。举个例子:有两张表,班级表和学生表,表结构:

  1.    class(cid,name, dues(班会费),grade(年级))
  2.   student(sid,cid(班级外键),gender(性别))

 

   统计结果的结构如下:

班级名称

班级所在年级班会费总和

年级拥有的班级数据

班级男生人数

班级女生人数

 

   在这个统计中需要用两个维度来统计,年级,和班级,按照Mysql这样的关系型数据库是不可能用一个SQL语句得到统计结果,需要分几个SQL按照不同维度进行统计,最后将统计结果组装起来。但是在ODPS中可以利用ODPS的窗口函数或者聚合函数来统计,SQL如下:

 

SELECT c.name as class_name,

      SUM(c.dues ) OVER ( PARTITION BY c.grade )  dues_sum_grade,

      Count(c.id) OVER(PARTITION BY c.grade)   class_count _grade,

      Count( CASE WHEN s.gender=’f’ THEN 1 END ) as female_count,

      Count( CASE WHEN s.gender=’m’ THEN 1 END ) as male_count

FROM class c INNER JOIN student s ON c.cid = s.cid

GROUP BY c.cid, c.name

 

ODPS SQL 窗口函数( http://odps.alibaba-inc.com/doc/prddoc/odps_sql/odps_sql_func.html#id5

ODPS SQL聚合函数( http://odps.alibaba-inc.com/doc/prddoc/odps_sql/odps_sql_func.html#odps-sql-aggr

大任务分解

    习惯于写在关系数据库上写SQL来得到统计结果的开发开发者,往往习惯于把一个统计过程写成一个SQL语句,但是随着统计逻辑变得复杂,这个SQL也会变得非常复杂,可读性也急剧下降。另外,调试也变得麻烦,在开发过程中的一个体验是,写SQL脚本稍有不慎,执行过程就会执行很长时间也不能得到结果,或者直接报错,往往从出错信息上很难看出到底哪儿写错了,让人一头雾水,往往只能凭感觉去纠错。

 

    解决的办法,是将一个大的SQL语句拆解成一个主表和多个分表,且主表和每张分表之间的关系要做到一对一,这里一对一的关系非常重要,因为只有一对一的关系才能使得各个表在join过程中出错的概率降到最低,在最后一步将主表和分表组合起来。该过程可以类比成汽车组装工厂的操作流程,各个车间先组装好每个模块构建,最后再由组装车间将每个构建组装起来。

 

    这样做有一个好处显而易见,那就是,非常容易定位问题,因为无论发生什么错误,都能将问题范围缩小到某个子语句中,这样能大大提高开发和维护的效率。

 

    将一个大的任务分而治之,分步骤执行,好处显而易见,同时也带来了一个问题需要将所有的各个子任务分步骤有序执行,因为有些子任务没有前后依赖关系可以并发执行,而有些任务有前后依赖关系,一个执行完了,才能触发另外一个执行。

 

防止聚合爆炸

       ODPS官方文档上提供了非常丰富的聚合函数,例如wm_concat,sum,count 等等。在使用聚合函数时,要注意一点,要防止聚合函数执行结果爆炸。举个例子,会员(user[userid,username])和购买记录(trade[tradeid,userid,payment,item_title])是一对多的关系。对这两张表以会员的维度进行聚合组合的记录为user_trade[userid,username,payment_sum,item_titles],item_titles 这个字段是用wm_concat(distinct trade.item_title)以userid分组聚合而成的,SQL如下:

SELECT userid,username,wm_concat(DISTINCT ‘,’,item_title) as item_titles

FROM user u

INNER JOIN trade t ON (u.userid = t.userid)

GROUP BY u.userid,u.username

 

    正常情况下,一个用户不会超过几十笔购买记录。但是,不得不承认林子大了什么鸟都会有,就有这样一些奇葩用户,有上万笔购买记录。导致ODPS脚本执行得非常慢,而且最终统计结果超过ODPS限定的字符串长度上限而使整个计算过程失败。

解决办法是需要将这些奇葩用户在计算之前先过滤掉,在计算之前先需要生成一张中间表用来表示所有合法的用户id集合,中间表SQL如下:

CREATE TABLE tmp_user_filter AS

SELECT tt.userid

 FROM(

SELECT userid ,count(t.tradeid) as tcount

FROM user u INNER JOIN user_trade t ON(u.userid = t.userid)

GROUP BY u.userid )tt

WHERE tt.tcount < 200

     通过这个SQL创建了一个临时表,将所有所有交易笔数少于200笔交易的用户过筛出来,当然这个200这个阀值可以按照业务的需要进行调节。之后在其他脚本中可以与tmp_user_filter这个表进行INNER JOIN,再用聚合函数统计,那就保证不会聚合爆炸了。

改造后的SQL如下:

SELECT userid,username,wm_concat(DISTINCT ‘,’,item_title) as item_titles

FROM user u

   INNER JOIN tmp_user_filter f ON (u.userid = f.userid)

INNER JOIN trade t ON (u.userid = t.userid)

GROUP BY u.userid,u.username

 

巧妙使用row_number()函数

     row_number()函数是一个不错的工具,能解决很多场景下的实际问题。举个例子在mysql中,有一张表student(sid,name,gender ,score),要求班级中男生,女生考试分数最高同学的姓名及分数。这个SQL用mysql来写也很费劲必须要依赖表中的一个外键,需要依赖外键的唯一性先求出id,然后再进行一次JOIN查询( http://stackoverflow.com/questions/12699799/mysql-selecting-max-record-in-group-by

   在ODPS中这个问题可以使用row_number()函数轻松搞定,sql如下:

SELECT aa.sid,aa.name,aa.gender,aa.score

 FROM(

SELECT row_number() over(partition by gender order by score desc) AS row_num,

       sid,name,gender,score

FROM student) aa

WHERE aa.row_num<2

    举一反三,也可以查询分数排前三的同学的记录,只需要改变WHERE条件的row_num值即可。

 

合理使用partition加速计算

       ODPS是基于文件系统的,存在ODPS中的数据会以partition(分区)为单位存在ODPS的计算集群的节点中。在ODSP执行计算逻辑时,能够有效提升计算速度的原则是,尽可能将数据分成足够多的分区,将单个分区的数据量足够小。写SQL的时候避免跨分区做join计算。

比如有a,b两张表,他们的分区策略是相同,a、b表之间存在一对多的逻辑关系。表的分区键是‘ps’。

Select *

From a inner join b on (a.id= b.f_id ANDa.ps=b.ps)

WHERE a.ps=’xxx’

    这样写虽然最后执行结果是正确的,但是需要花比较长的时间,原因是对于b表扫描的目标记录不止‘xxx’这一个分区而是b表的全部分区,正确的写法应该是,如下:

 

Select *

From a inner join b on (a.id= b.f_id )

WHERE a.ps=’xxx’ and b.ps=’xxx’



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [阿里云 odps 点滴] 推荐:

使用阿里云ODPS之点滴

- - SQL - 编程语言 - ITeye博客
    趁着这次做网聚宝搜索服务化这个项目,体验了一下阿里云的重量级云产品ODPS,也就是传说中的云梯2. 项目几近结束,现在回过头来看当初选择使用OPDS的决定还是相当正确的,ODPS在网聚宝搜索上云这个项目中起到了非常至关重要的作用,一句话总结:ODPS是非常靠谱的. 从刚开始里了解ODPS开始,到引入项目中使用,进而通过优化加速在项目中使用得游刃有余,经历了一个过程,中间也走了不少弯路.

阿里云手机:淘宝手机马甲?

- Chengkun - cnBeta.COM
按阿里云总裁王坚说法,它指的是使用阿里操作系统的手机,而不是阿里自己造手机了――王坚坚称,永远不做手机. 把手机一些信息同步到服务器上,是一种“云功能”. 在阿里云手机中,可以把手机联系人、通话记录、相册等手机信息存储到云端账号. 我觉得,这个功能背后,有一系列隐私问题. 阿里会不会利用这些信息展开进一步商业动作,是需要打个问号的.

阿里云OS将于本月中首次升级

- Qian - cnBeta.COM
据阿里云方面透露,阿里云OS将于本月中旬进行首次升级,预计将新增消息通知块,以及支持在阿云浏览器安装apk应用. 据悉,目前已经进入版本稳定性测试阶段. 近日,阿里云OS人士在官方论坛透露,新版OS将于9月中旬发布,并给出了15条相关升级内容. 同时他也表示,这仅仅是新版中的一部分.

云主机初体验(盛大云和阿里云)

- - ITeye博客
近来时常听到云计算、云主机的概念,它们一度挑战我的认知能力,在国外云主机已经非常流行,国内才刚刚兴起. 近日,朋友给了个邀请码,卢松松这才有机会体验了把云主机,才算基本搞清楚是怎么回事. 也希望为主机犯愁的朋友提供些思路. 亚马逊是云主机应用的开创者,在国内传统的IDC公司,如万网、华夏名网也有此业务,国内做云计算较快的也就是阿里和盛大了,所以我介绍的是盛大云和阿里云.

谷歌反对阿里云:不兼容将毁掉Android

- - 月光博客
  据新浪科技 报道,谷歌高级副总裁、Android系统创始人安迪·鲁宾(Andy Rubin)今日在Android官方博客上 发布声明,称Android发展过程中的不兼容将毁掉这个系统,“尽管Android完全免费,但只有兼容设备才能充分享受整个生态系统带来的益处”,“为同一个Android平台添砖加瓦,而不是推出一大堆不兼容版本”.

亚马逊AWS, Linode, 阿里云等云服务对比

- - idea's blog
最近, 有消息称亚马逊云服务(AWS)要进入中国, 在中国建立 IDC. 一石激起千层浪, 国内云服务纷纷降价. 根据我使用和了解到的云服务, 我觉得有必要对比下各个厂商的云服务, 希望支大家有帮助.. 网络传输 – $0.12/GB. 看起来, 似乎阿里云的价格最低, 但是, 阿里云的 CPU 和内存非常弱, 我在上面编译 PHP 源码, 竟然花了近一个小时.

阿里云即将开源的AliSQL什么来头?

- - 运维派
阿里云近日宣布启动AliSQL数据库开源项目. AliSQL是基于MySQL官方版本的一个分支,由阿里云数据库团队维护,目前也应用于阿里巴巴集团业务以及阿里云数据库服务. 该版本性能优于社区版MySQL 70%左右,可帮助中小企业和开发者提升数据运营能力. 8月9日,在2016云栖大会·北京峰会上,阿里云宣布启动AliSQL数据库开源项目.

阿里云发布智能客服机器人“云小蜜”

- - cnBeta全文版
10月12日,在2017杭州•云栖大会上, 阿里云正式发布云小蜜 —— 一款智能会话客服机器人. 智能客服机器人云小蜜具备36个预置的细分领域知识包,支持中文英文会话,可以7*24小时在线工作. 目前,已覆盖阿里巴巴生态圈二十余个业务线,每天服务600w客户,问题解决率达到95%. 阿里巴巴于2015年底正式推出无线端多领域私人助理阿里小蜜,一款人工智能购物助理 虚拟机器人.

感觉,我可能发现了阿里云的一个秘密

- - V2EX - 技术
我维护了几十个公立医院的官方网站服务器……. 每个网站一个 ECS,每个网站独立账号,医院主体注册方便发票直接开给他们. 所有网站附件都在七牛,静态资源都在 cdn 服务器,webserver 只提供动态内容. 大部分都是单一 ECS,25Mbps 峰值带宽,按流量计费,预存一点钱,按流量计费,流量都很少,都是纯动态内容 少量医院不接受这种预存费之后消费开发票的,采用 5Mbps 带宽方式购买.

阿里云手机真机效果图曝光,原来是化身

- Bingnan - Engadget 中国版
继续阅读全文 阿里云手机真机效果图曝光,原来是化身. 此文章网址 | 转寄此文章 | 回应.