Spark菜鸟学习营Day5

分布式程序开发

这一章会和我们前面进行的需求分析进行呼应,完成程序的开发。

开发步骤

分布式系统开发是一个复杂的过程,对于复杂过程,我们需要分解为简单步骤的组合。

  • 针对每个简单步骤,难度会降低,学习成本降低

  • 每个步骤都可以作为里程碑,可以反馈进展,同时,有助树立目标感。

  • Step1:需求分析

    • 1.1 拆分程序,形成步骤

      • 以语句为单位拆分,一般一个语句就是一个步骤
    • 1.2 对步骤进行分类
      • 根据需求分析指南,分入A1、A2、B1等规则小类
    • 1.3 分析每个步骤的输入输出
  • Step2:建表

    • 使用PojoMaker工具
  • Step3:新建测试程序

    • 具体可以看测试案例编写的指南
  • Step4:测试数据准备

    • 4.1 数据文件建立

      • 使用DataPrepareUtil工具
    • 4.2 将数据传入程序
      • 使用mock方法

开发准备

  • Step1:首先在测试类中,新建对处理类的调用。
    public void runTest(ComputeBatchNode cbn, JavaSparkContext sc, Hashtable params, ComputeResult result) {
prepareData(cbn, sc, params, result);
new QtslProcessor(cbn, sc, params, result).process();
}
  • Step2:新建处理类

在标红处Alt+Enter,新建处理类

输入参数:

  • cbn 运行环境,会存公共对象,比如数据库连接、redis连接等
  • sc Spark实例,调用各种Spark命令
  • params 参数,可以保存内部参数,也可以保存外部参数
  • result 输出,程序的对外输出

    ![2016-07-27 13-43-31](http://o6jujlzry.bkt.clouddn.com/2016-09-03-2016-07-27 13-43-31.jpg)

建立如下的初始代码:

public class QtslProcessor extends SplitProcessor {
public QtslProcessor(ComputeBatchNode cbn, JavaSparkContext sc, Hashtable params, ComputeResult result) {
super(cbn, sc, params, result);
}
@Override
public String process() {
return null;
}
}

开发开发开发

A.数据清理

  • 因为不同的数据库的删除逻辑不一致,所以采用统一api的方式调用,可以将不同数据库的写法统一

    • 语句入口方法是delete()
    • 语句需要通过appendDeleteCondition来进行输出

B.批量数据转换

B1.Dataframe方式

分为几个步骤:

  • Step1 对SQL语句进行规范化
  • Step1.1 列名对准
        insert into tab1(a1,a2,a3)
select b1,b2,b3 from tab2

替换为

        insert into tab1(a1,a2,a3)
select b1 a1,b2 a2,b3 a3 from tab2
  • Step1.2 替换逻辑
  • IN语句
           Select a from tab1 t
where t.a in (select a from tab1)

替换为

           Select a from tab1 t , (select distinct a from tab1) t2
where t.a = t1.a
  • Step1.3 替换函数

    • 待补充
  • Step1.4 替换变量

    • 在变量外面增加#{}
    select a from tab1 t where a = v_a

替换为

   select a from tab1 t where a = #{v_a}
  • Step2 初始化变量

    • 调用putSqlParam
  • Step3 执行Sql

    • 调用runSparkSql方法
  • Step4 输出结果

    • 调用appendResultDataframe方法

C.逐笔循环数据转换

  • C1.Cursor转RDD

    这是最为复杂的一个步骤,需要掌握RDD的开发基础

  • C2.单行数据过滤

    采用fiter方法,内部采用where方法来定义条件

            JavaRDD<QtslTempPojo> perparedQtslRDD = filtedQtslRDD.filter(
v1 -> where(() -> v1.getZQZH().equals("0"))
.or(() -> v1.getZQZH().equals(""))
.get());

对于in,exists,not in , not exists 这样的单行过滤条件,我们需要采用anyMatch方法来进行判断

可以使用comparePojo方法对两个pojo进行比较

如下是一个not exists逻辑

                perparedQtslRDD
.filter(v1 -> where().and_not(
() -> oracleData.stream().anyMatch(
record -> comparePojo(record, v1)
))
.get()
)
  • C3.过滤重复数据

    采用groupBy方法,对每个分组只返回一条记录
    .groupBy(
v1 -> new Tuple2<>(v1.getFundCode(), v1.getSecurityId()))
.map(
v1 -> toList(v1._2).get(0));
  • C4.单行数据删除

    待补充

  • C5.单行数据输出

    实际就是从一个pojo转换成另外一个pojo,考虑到可能出现数据异常的情况,推荐采用flatMap方法实现

    可以使用clonePojo方法,将两个pojo中相同的字段自动转换,差异字段需要额外赋值

        .flatMap(new FlatMapFunction<QtslTempPojoExtend, OutTrdQtslSubPojo>() {
@Override
public Iterable<OutTrdQtslSubPojo> call(QtslTempPojoExtend v1) {
OutTrdQtslSubPojo outTrdQtslSubPojo = clonePojo(v1, OutTrdQtslSubPojo.class).orElseGet(null);
if (outTrdQtslSubPojo != null) {
outTrdQtslSubPojo.setDEAL_FLAG("0");
outTrdQtslSubPojo.setSEQ_NO(Long.toString(v1.getSeqNo()));
outTrdQtslSubPojo.setSUB_NO("1");
outTrdQtslSubPojo.setSUB_NO_PRE("0");
}
return result(outTrdQtslSubPojo);
}

D. 优化处理

  • D1.从Oracle取数

    第一步,需要在sqlmap文件中配置sql语句
    <select id="selectFundCjqsTmp" resultType="java.util.Map" parameterType="HashMap">
<![CDATA[
SELECT t.*
FROM dat_fund_cjqs_tmp t
WHERE t.bcrq = #{businessDate}
AND t.comfirm_status = '1')
]]>
</select>

第二步,通过getPojoListFromMybatis方法获取数据

        List<OutTrdQtslHisPojo> oracleData = this
.getPojoListFromMybatis("splitSqlMapper.getQtslHis", OutTrdQtslHisPojo.class);

样例代码1

  • 步骤1:清理中间表+结果数据表(A3+A4) *
        appendDeleteCondition(
delete("out_trd_qtsl").where(field("rq").eq(lastDate))
);
  • 步骤2:输出数据表,清理Oracle(A4) *
appendDeleteCondition(
delete("out_trd_qtsl_sub").where(field("rq").eq(splitDate)));
  • 步骤3:输出数据表,清理Oracle(A4) *
        appendDeleteCondition(
delete("out_trd_qtsl_his").where(field("rq").eq(splitDate)));
  • 步骤4:使用Dataframe的select语句来进行处理(B1) *
        putSqlParam("v_scdm", "001");
putSqlParam("v_last_date", lastDate);
DataFrame df2 = runSparkSql(" SELECT scdm, hydm, sjlx, zqzh, xwh, zqdm, zqlb, ltlx, qylb, gpnf, sl1, sl2,\n" +
" bh1, bh2, fzdm, rq, bcsm, byn\n" +
" FROM qtsl_temp a, (SELECT distinct partner_code\n" +
" FROM par_fund_partner\n" +
" WHERE market_code = #{v_scdm}\n" +
" AND sub_partner_code = '000000'\n" +
" AND #{v_last_date} BETWEEN inure_begin_date AND inure_end_date) b\n" +
" WHERE a.rq = #{v_last_date}\n" +
" AND a.zqzh = b.partner_code\n" +
" ");
appendResultDataframe(df2, OutTrdQtslPojo.class);

待续...

练习3

题目

进行RDD去重操作的训练

  1. 读取交易记录
  2. 按照fundCode+SecurityId进行分组
  3. 取出quantity最小的那条记录
  4. 输出结果
  • 步骤1:对RDD数据进行分组,groupBy方法传入的是分组条件,请注意这里是对两个字段分组,所以我们输出一个Tuple2。
return this.getInputRDD(PracticePojo.class)
.groupBy(
v1 -> new Tuple2<>(v1.getFundCode(), v1.getSecurityId()))

这里需要注意的是,groupBy方法的返回是:

JavaPairRDD<Tuple2<String, String>, Iterable>

这是一个key-value结果,其中key是分组的键值,而value是一个数组。

比如下:输入[ (1 2 3); (1 2 4); (2 3 4) ]

如果按照字段1进行groupBy

结果为: (1, [(1 2 3 ) , (1,2,4)])

(2, [(2 3 4)])

  • 步骤2:对数据的迭代数据排序,并返回第一条记录。比较算法,采用if逻辑可以防止变量溢出。
 .map(
v1 -> toOrderedList(v1._2,
(t1, t2) -> {
//t1比t2大,返回正数
//t1比t2小,返回负数
//t1和t2一样大,返回0
if (t1.getQuantity() > t2.getQuantity()) return 1;
if (t1.getQuantity() < t2.getQuantity()) return -1;
return 0;
}
).get(0));

练习4

题目:数据关联过滤练习

  1. 读取交易记录
  2. 按照PracticeSecurity对交易数据进行过滤
  3. 输出结果

思路1:采用join操作

  • 步骤1:获取输入RDD
        JavaRDD<PracticePojo> inputRDD = this.getInputRDD(PracticePojo.class);
JavaRDD<PracticeSecurityPojo> securityRDD = this.getInputRDD(PracticeSecurityPojo.class);
  • 步骤2:将两个RDD转换为PairRDD,因为仅有PairRDD支持join操作
        JavaPairRDD<String, PracticePojo> pairInputRDD = inputRDD.mapToPair(
practicePojo -> new Tuple2(practicePojo.getSecurityId(), practicePojo)); JavaPairRDD<String, PracticeSecurityPojo> pairSecurityRDD = securityRDD.mapToPair(
practiceSecurityPojo -> new Tuple2<>(practiceSecurityPojo.getSecurityId(), practiceSecurityPojo));
  • 步骤3:执行join操作,并返回结果
return pairInputRDD
.join(pairSecurityRDD)
.map(new Function<Tuple2<String, Tuple2<PracticePojo, PracticeSecurityPojo>>, PracticePojo>() {
@Override
public PracticePojo call(Tuple2<String, Tuple2<PracticePojo, PracticeSecurityPojo>> v1) throws Exception {
return v1._2._1;
}
});

这里没有采用lambda表达式方式,因为类型提示比较有用。

我们可以看到join方法的返回是Tuple2<String, Tuple2<PracticePojo, PracticeSecurityPojo>>

举个例子说明:

数据集1:[(1,3,4) ; (1,5,6) ]

数据集2:[(1,2)]

如果按照第一个字段进行join

结果为:[ (1, ( (1,3,4) , (1,2) ) ,

(1, ( (1,5,6) , (1,2) ) ]

在练习中,需要返回的是[(1,3,4);(1,5,6)],所以需要获得 v1._2._1

Spark菜鸟学习营Day5 分布式程序开发的更多相关文章

  1. Spark菜鸟学习营Day6 分布式代码运行调试

    Spark菜鸟学习营Day6 分布式代码运行调试 作为代码调试,一般会分成两个部分 语法调试,也就是确定能够运行 结果调试,也就是确定程序逻辑的正确 其实这个都离不开运行,所以我们说一下如何让开发的S ...

  2. Spark菜鸟学习营Day2 分布式系统需求分析

    Spark菜鸟学习营Day2 分布式系统需求分析 本分析主要针对从原有代码向Spark的迁移.要注意的是Spark和传统开发有着截然不同的思考思路,所以我们需要首先对原有代码进行需求分析,形成改造思路 ...

  3. Spark菜鸟学习营Day1 从Java到RDD编程

    Spark菜鸟学习营Day1 从Java到RDD编程 菜鸟训练营主要的目标是帮助大家从零开始,初步掌握Spark程序的开发. Spark的编程模型是一步一步发展过来的,今天主要带大家走一下这段路,让我 ...

  4. Spark菜鸟学习营Day4 单元测试程序的编写

    Spark菜鸟学习营Day4 单元测试程序的编写 Spark相比于传统代码是比较难以调试的,单元测试的编写是非常必要的. Step0:需求分析 在测试案例编写前,需完成需求分析工作,明确程序所有的输入 ...

  5. Spark菜鸟学习营Day3 RDD编程进阶

    Spark菜鸟学习营Day3 RDD编程进阶 RDD代码简化 对于昨天练习的代码,我们可以从几个方面来简化: 使用fluent风格写法,可以减少对于中间变量的定义. 使用lambda表示式来替换对象写 ...

  6. 微信小程序开发笔记02

    今天学习了微信小程序开发用到的语言,wxml与wxss语言基本语法与html和css基本语法相似,学习起来相对简单.在小程序主要的语言是js(javascript,跟准确的说是jqery) ,由于这种 ...

  7. 【Spark深入学习 -14】Spark应用经验与程序调优

    ----本节内容------- 1.遗留问题解答 2.Spark调优初体验 2.1 利用WebUI分析程序瓶颈 2.2 设置合适的资源 2.3 调整任务的并发度 2.4 修改存储格式 3.Spark调 ...

  8. spark之java程序开发

    spark之java程序开发 1.Spark中的Java开发的缘由: Spark自身是使用Scala程序开发的,Scala语言是同时具备函数式编程和指令式编程的一种混血语言,而Spark源码是基于Sc ...

  9. 13本热门书籍免费送!(Python、SpingBoot、Entity Framework、Ionic、MySQL、深度学习、小程序开发等)

    七月第一周,网易云社区联合清华大学出版社为大家送出13本数据分析以及移动开发的书籍(Python.SpingBoot.Entity Framework.Ionic.MySQL.深度学习.小程序开发等) ...

随机推荐

  1. Web Api 2 用户认证模板解析-----外部用户认证模式

    一般的社交提供商不提供一个Web Service进行身份验证(有很好的理由),而提供一个身份验证的界面,其中包含了某种协议如OpenID(连接)或使用OAuth2认证.这意味着客户端应用必须使用一个浏 ...

  2. 翻译:WebApi 认证--用户认证Oauth解析

        The Web API v2用户认证模板提供了流行的应用用户认证场景,如.使用本地帐号的用户名密码认账 (包括创建用户.设置和修改密码)以及使用第三方的认证方式,如facebook,googl ...

  3. [Java] JSTL格式化时间计算时差

    引入JSLT标签: <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> ...

  4. 【Python Lib】解析HTML利器 BeautifulSoup

    - - 官方API文档,中文版 http://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html - 以后会把常用的摘录出来

  5. struts2+jquery 实现ajax登陆

    一.新建一个web项目:test,配置好struts2的环境(详细配置见:http://www.cnblogs.com/wuweidu/p/3841297.html)       导入Jquery的j ...

  6. 10 Best TV Series Based On Hacking And Technology

    Technology is rapidly becoming the key point in human lives. Here we have discussed top TV shows whi ...

  7. IO输入输出 3

    编写BinIoDemo.java的Java应用程序,程序完成的功能是:完成1.doc文件的复制,复制以后的文件的名称为自己的学号姓名.doc. package com.hanqi.test; impo ...

  8. 如何提高手机APP的用户体验?

    详细内容请点击 随着移动互联网如日中天,如火如荼的时候,手机APP开发日益高涨了起来,关于手机APP的用户体验,也是一个老话长谈的话题.从事这行业也很久了,以下是我个人在工作中的一些关于APP的用户体 ...

  9. 每天一道LeetCode--409 .Longest Palindrome

    Given a string which consists of lowercase or uppercase letters, find the length of the longest pali ...

  10. SQL_UNPIVOT(行列转换)

    --临时表 insert into ##table([column1],S1, S2,S3)VALUES('VALUE','VALUE','VALUE','VALUE') --把原S1, S2,S3列 ...