Spark菜鸟学习营Day5 分布式程序开发
Spark菜鸟学习营Day5
分布式程序开发
这一章会和我们前面进行的需求分析进行呼应,完成程序的开发。
开发步骤
分布式系统开发是一个复杂的过程,对于复杂过程,我们需要分解为简单步骤的组合。
针对每个简单步骤,难度会降低,学习成本降低
每个步骤都可以作为里程碑,可以反馈进展,同时,有助树立目标感。
Step1:需求分析
- 1.1 拆分程序,形成步骤
- 以语句为单位拆分,一般一个语句就是一个步骤
- 1.2 对步骤进行分类
- 根据需求分析指南,分入A1、A2、B1等规则小类
- 1.3 分析每个步骤的输入输出
- 1.1 拆分程序,形成步骤
Step2:建表
- 使用PojoMaker工具
Step3:新建测试程序
- 具体可以看测试案例编写的指南
Step4:测试数据准备
- 4.1 数据文件建立
- 使用DataPrepareUtil工具
- 4.2 将数据传入程序
- 使用mock方法
- 4.1 数据文件建立
开发准备
- 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 输出,程序的对外输出

建立如下的初始代码:
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去重操作的训练
- 读取交易记录
- 按照fundCode+SecurityId进行分组
- 取出quantity最小的那条记录
- 输出结果
- 步骤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
题目:数据关联过滤练习
- 读取交易记录
- 按照PracticeSecurity对交易数据进行过滤
- 输出结果
思路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 分布式程序开发的更多相关文章
- Spark菜鸟学习营Day6 分布式代码运行调试
Spark菜鸟学习营Day6 分布式代码运行调试 作为代码调试,一般会分成两个部分 语法调试,也就是确定能够运行 结果调试,也就是确定程序逻辑的正确 其实这个都离不开运行,所以我们说一下如何让开发的S ...
- Spark菜鸟学习营Day2 分布式系统需求分析
Spark菜鸟学习营Day2 分布式系统需求分析 本分析主要针对从原有代码向Spark的迁移.要注意的是Spark和传统开发有着截然不同的思考思路,所以我们需要首先对原有代码进行需求分析,形成改造思路 ...
- Spark菜鸟学习营Day1 从Java到RDD编程
Spark菜鸟学习营Day1 从Java到RDD编程 菜鸟训练营主要的目标是帮助大家从零开始,初步掌握Spark程序的开发. Spark的编程模型是一步一步发展过来的,今天主要带大家走一下这段路,让我 ...
- Spark菜鸟学习营Day4 单元测试程序的编写
Spark菜鸟学习营Day4 单元测试程序的编写 Spark相比于传统代码是比较难以调试的,单元测试的编写是非常必要的. Step0:需求分析 在测试案例编写前,需完成需求分析工作,明确程序所有的输入 ...
- Spark菜鸟学习营Day3 RDD编程进阶
Spark菜鸟学习营Day3 RDD编程进阶 RDD代码简化 对于昨天练习的代码,我们可以从几个方面来简化: 使用fluent风格写法,可以减少对于中间变量的定义. 使用lambda表示式来替换对象写 ...
- 微信小程序开发笔记02
今天学习了微信小程序开发用到的语言,wxml与wxss语言基本语法与html和css基本语法相似,学习起来相对简单.在小程序主要的语言是js(javascript,跟准确的说是jqery) ,由于这种 ...
- 【Spark深入学习 -14】Spark应用经验与程序调优
----本节内容------- 1.遗留问题解答 2.Spark调优初体验 2.1 利用WebUI分析程序瓶颈 2.2 设置合适的资源 2.3 调整任务的并发度 2.4 修改存储格式 3.Spark调 ...
- spark之java程序开发
spark之java程序开发 1.Spark中的Java开发的缘由: Spark自身是使用Scala程序开发的,Scala语言是同时具备函数式编程和指令式编程的一种混血语言,而Spark源码是基于Sc ...
- 13本热门书籍免费送!(Python、SpingBoot、Entity Framework、Ionic、MySQL、深度学习、小程序开发等)
七月第一周,网易云社区联合清华大学出版社为大家送出13本数据分析以及移动开发的书籍(Python.SpingBoot.Entity Framework.Ionic.MySQL.深度学习.小程序开发等) ...
随机推荐
- Android小项目之十 应用程序更新的签名问题
------- 源自梦想.永远是你IT事业的好友.只是勇敢地说出我学到! ---------- 按惯例,写在前面的:可能在学习Android的过程中,大家会和我一样,学习过大量的基础知识,很多的知识点 ...
- uva 327 Evaluating Simple C Expressions 简易C表达式计算 stl模拟
由于没有括号,只有+,-,++,--,优先级简单,所以处理起来很简单. 题目要求计算表达式的值以及涉及到的变量的值. 我这题使用stl的string进行实现,随便进行练手,用string的erase删 ...
- [转]在PHP语言中使用JSON
本文转自:http://www.ruanyifeng.com/blog/2011/01/json_in_php.html 作者: 阮一峰 日期: 2011年1月14日 目前,JSON已经成为最流行的数 ...
- C++复习笔记
好多东西都忘了,现在重新复习一遍,把遇到的要点都记录下来.随时更新. 指针 C保证在为数组分配存储空间的时候,指向数组之后的第一个位置的指针也是合法的.也就是说保证指针 a + SIZE 是合法的,但 ...
- 51nod-1686 第K大区间(二分+尺取法)
题目链接: 第K大区间 基准时间限制:1 秒 空间限制:131072 KB 定义一个区间的值为其众数出现的次数.现给出n个数,求将所有区间的值排序后,第K大的值为多少. Input 第一行两个数 ...
- hdu 3111 DLX解数独
思路:裸的DLX解数独.关键是建图,感觉还不如写个dfs直接,DLX写这个的代码很烦. #include<set> #include<map> #include<cmat ...
- Table of Contents - Jersey
Jersey 1.19.1 Getting Started Get started with Jersey using the embedded Grizzly server Get started ...
- SQLServer排序时与读取的记录会影响到结果?
这是在做程序的时候发现的,我用到了一个分页存储过程,在翻看第二页的时候发现结果竟然与第一页有很多重复的内容, 下面开始测试一下吧: 创建表 create table abc ( id int prim ...
- C# DateTime 日期加1天 减一天 加一月 减一月 等方法(转)
//今天 DateTime.Now.Date.ToShortDateString(); //昨天,就是今天的日期减一 DateTime.Now.AddDays(-).ToShortDateString ...
- swift 闭包简写实际参数名$0、$1等理解
Swift 自动对行内闭包提供简写实际参数名,你也可以通过 $0 , $1 , $2 等名字来引用闭包的实际参数值. 如果你在闭包表达式中使用这些简写实际参数名,那么你可以在闭包的实际参数列表中忽略对 ...