大数据23年3月笔记
DSL代码风格使用
学习日期: 3.2
所学内容概述
看小白学苑的解法,发现DSL是能解决很多sql语句繁琐需要写字段名的代码。学习了spark的sql方法
sparksql常用方法
withColumn
自己使用一般只用到两种用法
- 添加列
- 修改列
1 | withColumn("列名","数据") |
如上如果列名存在,则是对该列修改。如果列名不存在,则是添加新列
drop
使用过一次,删除一列
1 | drop("etl_date") |
lit
创建一个常量列 如下lit代表数据为常量结合withColumn使用
1 | withColumn("etl_date",lit("20230302")) |
col
col中选中为列的数据,如下col内的字段表示该列 oerate_time列
1 | withColumn("operate_time", coalesce(col("operate_time"), col("create_time"))) |
coalesce
该函数和mysql和hive的用法一样,返回第一个不为null的值
1 | coalesce(col("operate_time"), col("create_time")) |
createOrReplaceTempView
方法是创建一个临时表和with as 临时表名
差不多
- 如果两次建立的临时表名字一样第一次的会被第二次的数据覆盖
1 | spark.sql("select * from ods.user_info").createOrReplaceTempView("tempTable") |
partitionBy
设置分区字段,如果表中有该字段自动设置为分区
1 | partitionBy("etl_date") |
write
写入表的方法,将df.wirte开始写入表,修改参数即可
mode
修改写入表的形式
“overwrite” “append”
saveAsTable
保存DataFrame数据到指定hive表中。分两种情况:
表已经存在
和表不存在
如果表不存在,则会自动创建表结构。
如果表已经存在,则此函数的行为取决于由mode函数指定的保存模式(默认情况下抛出异常)。
a.mode=Overwrite时
,
- 当dataframe的schema与已存在的schema个数相同:DataFrame中的列顺序不需要与现有表的列顺序相同,与insertInto不同,saveAsTable将使用列名称查找正确的列位置。(与insertInto区别点)
- 当dataframe的schema与已存在的schema个数不同:会撇弃原有的schema,按照dataframe的schema重新创建并插入。
b.mode=Append时
,
- 当dataframe的schema与已存在的schema个数相同:DataFrame中的列顺序不需要与现有表的列顺序相同。
- 当dataframe的schema与已存在的schema个数不同:会报错。
insertInto
保存DataFrame数据到指定hive表中,但要求满足以下两点:
- 指定的hive表是存在的;
- DataFrame的schema结构顺序与指定Hive表的schema结构顺序是一致的。
日总结
今天看小白的七月清洗答案,自己豁然开朗了,可以不全使用纯sql写清洗的代码了,之前自己纯sql写完七月的清洗部分代码量是十分巨大的,而且需要写字段,字段名还要一一对齐,不然无法加入表中,今天的任务把dsl风格的代码中数据清洗常用的代码都理解,就花了不少的时间。明天自己写写试。七月的清洗纯sql的无法在规定时间完成的,必须结合DSL。
使用本地VM镜像
学习日期: 3.3
所学内容概述
台式机安装VM,并导入镜像,ssh顺利连接
使用asbru中sftp
使用sftp导出文件到本地,get 需要的文件 导入本地的地址
BUG点
一直报错,看报错信息大致原因是没有一个什么jar包,本地的镜像好像使用的并不是mysql还是org.mariadb库,但是支持的上mysql,就不去搞原理了,把连接jar包放入spark的jars目录下,修改driver为org.mariadb.jdbc.Driver,BUG顺利解决
日总结
今天大量的时间在安装本地虚拟机,h3c服务器坏了,就只能在本地安装了,上午装好了虚拟机,下午使用的时候出了BUG,差不多到晚上才解决,然后进去把环境和sql文件导入了一下,今天的任务就完成了,本来想今天使用新方法做七月的数据清洗的,只能放到明天了。
SQL结合DSL表清洗
学习日期: 3.4
所学内容概述
因为对清洗部分中user表是最难的,后面的表的操作模仿着user就能写完所以只放user表的代码了
1 | def odsClearUser(sparkSession: SparkSession,odsName:String,dwdName:String): Unit ={ |
BUG点
BUG1
无法将数据导入该表
写进dwd的dim_user_info的时候,就会一直报这个错误,我直接从ods抽取前20行数据的时候,写入方法也是这样就是正常的。 找了一上午的原因没解决,没办法只能问小白的老师了。原因是:被写入的表,正在被读取,所以不能写入。先把数据写到一个中间表,再读取中间表写入目标表 。解决办法是先直接写入saveAsTable一个临时表,通过spark.table读取该表数据导入dwd层的表中,再删除表
BUG2
数据导入无法查询,之前就有这样的情况 ,发现除了decimal类型的,其他都能正常查询,搜索以后 说是hive和spark类型需要一致性
解决办法:
创建spark的df 时候加入配置
1 | config("spark.sql.parquet.writeLegacyFormat", "true") |
日总结
今天的任务是使用DSL结合SQL风格的spark代码,完成任务7中的数据清洗部分。参考了别人的思路,结合自己的想法将代码优化,写了能使用的方法,将表名封装为参数,能直接使用,如果需要的关键字不一样,只要复制方法略微修改一下就好了, 上午解决BUG浪费了很多时间,在晚上才完成一次成功的清洗。
7月任务1指标计算
学习日期: 3.6
所学内容概述
指标计算3题
1 | package gz07.real.subject1 |
BUG点
BUG没有截图,就是在用spark提交jar包的时候,跑起来,没有报错,但是会莫名其妙的被杀死。也没找到原因,整了一上午有时候好,有时候就不行。试着重启了hadoop和hive的元数据,就很长一段时间没有再报错了,估计是spark中我存了临时表,spark没杀死,导致缓存多了,超出内存,就自动杀死了。
日总结
今天的任务其实主要是为了熟悉7月份的表结构,每个字段代表什么,以及多表连接的公共字段,选择去做了指标计算,认为难度一般,7月份的表其实是比4月好一点的,订单表里面就有省份id,在求省份或者地区的订单金额的时候会方便很多,就不需要像4月份一样,订单表连接user表再通过user找province和region。总得来说理清楚表结构,指标计算部分还是很简单的。
7月任务3
学习日期: 3.7
所学内容概述
完整版七月任务书 2小时40分钟完成
抽取
1 | package gz07.real.subject3 |
清洗
1 | package gz07.real.subject3 |
指标计算
1 | package gz07.real.subject3 |
BUG点
指标计算最后一题的时候,是之前没怎么做过的类型,是列转行,想起来了collect_set和concat_ws两个方法能实现任务目标,但是报错了,看报错信息,明白了似乎是province_id的类型不对需要是array
扩展学习
想得到这样的表
多行变一行 有公共列
1).先使用collect_set函数使多行成为一行数组
1 | hive> select name,collect_set(subject) res1 , |
2). 加上concat_ws函数可以取出数组中的每一个元素的值在用分隔符连接
1 | hive> select name,concat_ws('@',collect_set(subject)) res1 , |
1 | hive>select substr('qwertyuio',0,4) |
日总结
今天做题的状态极佳,应该是前几天都在研究七月任务7,因此前面的抽取和清洗只花了大概1小时20分钟,剩下时间在做指标计算,指标计算已经熟悉表结构了而且连接的表不是很多就一两个,所以我认为难度一般,最后一题只是刚开始被吓到了,其实只要使用两个函数再结合分组就能很顺利的解决,整体做下来,将近快3个小时了,还是比四月份难很多的。过程中也是出现了几次小错误,也没浪费多少时间在修改上。
7月任务4 指标计算
学习日期: 3.9
所学内容概述
其他几题没什么问题,在第二题的时候犯了难。
t1
简单的表连接和分组。
1 | //TODO t1 |
t2
任务如下
最开始的想法是将销售量和销售额前十的表都先查找出来,然后结合,结合以后排序即可。
- 结果如下,没有达到自己的要求,不是直接合并,而是出现了笛卡尔积,销售量表和销售额表的数据是直接相乘了。
- 又想到一种方法,将两个表都多出一行根据 各自需要降序排序的数据,列出排名,根据排名进行join,这样就能达到两个表的连接,然后使用drop删除左边的排序信息。实现代码如下
1 | //TODO t2 |
加入MySQL表 以及完成任务
###t3
这个是要求求中位数的之前没有使用过,但是题目提供了函数方法。
percentile(col,doule) 该方法第一个参数是列,列的要求是整数bigint或者array(doule),因为是中位数,第二个参数就是0.5,如果要3/4的值的话就是0.75,以此类推
问题就不会很难了,求两个表一个省份中位数,一个地区中位数,join即可
- 代码实现如下
1 | //TODO t3 |
mysql执行命令以及结果
BUG点
想将两个表给合并起来,就想到了之前使用过的select t1.x,t2.x from t2,t3报了错误,看报错信息是hive不支持笛卡尔积的使用,在hive中开启笛卡尔积就能解决报错。开启方法如下在截图最后两行。如果放在spark中提交的话,还是会报错。报错信息是crossJoin也是笛卡尔,需要用如下的代码解决
1 | spark.conf.set("spark.sql.crossJoin.enabled","true") |
日总结
今天完成了任务4的指标计算部分,上午花时间和小白老师以及江苏省第一的朋友探讨了一下,对题目模糊不清的要求的看法,清洗部分对dwd表是overwrite还是append等。改天会具体列出来,还有最后指标计算对order表要不要取最新分区的看法还没讨论出结果。然后就是任务四,一路上除了第二题出了点问题,还是顺利的,而且第二题也顺利解决了问题,今天学习状态也不错,做题顺利。
7月任务书讨论
学习日期: 3.10
所学内容概述
问题疑惑点如下:
### 问题一
因为我们是不知道dwd表中的数据以及分区是什么样子的,只能推测。根据业务理解,可以认为ods的数据是比赛日期的昨天的,而dwd的数据是昨天之前的,也就是前天或者更早的,题目要求ods取昨天的分区,dwd取最新的分区,假设这个最新的分区就是前天的,那和ods通过id去重以后的数据再放入dwd的时候,是append还是overwrite,还是说append然后将重复的id删除了。
经过讨论认为直接append的就好了,虽然会有重复的id但是分区是不一样的。如果是真实的业务上的话,其实是应该对表的id去重的,因为userid是主键是唯一值。
问题二(未解决)
在指标计算中都会有这么一句话,以前没有注意,看到以后发现是要取分区的,但是我又去看了小白的代码,发现他并没有对order表取最新分区。
小白给我的解释是order表是事实表不是维表所以不取分区,问了福建比赛的朋友,他也说不用取,但是江苏第一说他比赛的时候order取了最新的分区,一时间不知道听哪个的,我自己认为表的确是有维表和事实表的区别,所以题目要求维表,order的确是不用取最新分区的。但是江苏第一说他比赛的时候取的,结果是全对的。…等后面问四合的老师吧。
扩展学习
将7月任务书综合分析部分都使用chatgpt,将问题回答完整并做了总结。总结完以后发现也就6个问题,每天背一下记一下,比赛前应该都能记住了。
日总结
对任务书疑惑点进行讨论,结合大家看法,找到最合理的解决办法,以及题目理解。比较突出的其实就我列出来的两个问题,其他的大家的理解都基本一致。总结差不多都写上面了。然后就是7月任务书综合分析部分都使用chatgpt,将问题回答完整并做了总结。总结完以后发现也就6个问题,每天背一下记一下,比赛前应该都能记住了。
7月任务书5 指标计算
学习日期: 3.13
所学内容概述
第一题
其他很多任务书都有这题,就不细说了
1 | sql( |
第二题
计算连续两天下单的用户与已下单用户的占比,将结果存入MySQL数据库shtd_result的userrepurchasedrate表中
分析题目要求,求出下单用户,和连续两天下单的用户,然后占比,也就是一行即可,那就可以使用dsl的getLong获取数量,再最后导入表的时候使用常量值就行了。代码如下:
1 | val l1: Long = sql( |
第三题
第三题比较难,行转列,需要用scala中柯里化函数,因为scala学的不是很深入,所以这题如果自己做的话,应该是用union或者join来实现了。问了朋友明白代码用法,实现了功能,foldLeft其实还不是很理解。
1 | val data = sql( |
BUG点
修了个hexo博客的bug,问题点在我文章h1标签,点击工作台会报错,而且无法跳转。查看博客源代码发现在main.js和util.js中有使用,一直深拔源码,发现是hexo-renderer-markdown-it这个插件的问题,因为seo标题就是h1标签,所以源码中跳转特意从h2开始的,源码改不了就只能改配置覆盖,在_config.yml中加入,问题顺利解决。
1 | markdown: |
日总结
今天的任务是任务书5的指标计算,任务5的指标计算和之前的很不同,之前的都是分组完了然后调用函数,任务5后面的两题就是比较新颖的,一题是单行的,另外一题列转行就比较难了,需要用scala函数柯里化。解决了之前博客点击跳转的BUG。新版博客链接https://u7u7.top
任务4修改
学习日期: 3.14
所学内容概述
刚开始的题目理解都是使用detail商品表,但是忘记了订单额,应该是算最终订单,也就是info表中的final_total_amount,所以之前的代码是有疑问的,而且因为两个表order_id是不相同的,讨论过后选择使用join,用两个表都有的id,修改以后如下:
1 | //TODO t2 |
日总结
明天开始每天都是要去做几道算法题了,蓝桥杯算法比赛快开始了,最近一些算法使用已经有点生疏了,今天讨论的时候又解决了一个任务不清晰的要求,今天主要是复习了一下java算法和解决大数据的问题以及背了一下综合分析。
任务5以及算法
学习日期: 3.15
所学内容概述
编号2120
题目不难,找到规律就行了。
1 | public static void main(String[] args) { |
BUG点
任务书5第三题,是使用rdd算子实现的,在idea中能打印,能show,都是蛮正常的,但是放到服务器spark提交,就报很多错误,而且找不到关键信息,回到IDEA的时候,发现最上面是有报错信息的,代码却能正常执行。搞了一下午没解决,推测是因为环境使用的是mariadb的问题,因为IDEA中出现很多这个报错,Spark无法显示的原因,应该是mariadb的报错没有过滤,导致一报错就自动杀死自己的进程。
扩展学习
- 研究了一下自己博客插件的源代码,并安装了看板娘在博客中,自己自定义了位置以及大小,写yml配置中
- 其他的live2d有些比较吃性能,自己的没有买云服务器所以只能找一个资源占用不大的模型:yml代码如下
1 | live2d: |
效果如下:
日总结
今天下午是大数据学习,可惜一直在解决BUG,而且没解决完。等后面服务器来了再测试一下吧,我怀疑是小白服务器的问题,真要改起来肯定会花费大量的时间和精力,所以打算先放一放。上午改了博客学习了一下Vue,vue没看几集时间不是很充裕,就没有记笔记。做了一道简单的算法题,做第二题的时候,难度上去了,一中午没做完,下午要大数据了,就先放明天弄了。今天学习状态一般。
Vue TodoList案例完善
学习日期: 3.17
所学内容概述
全局事件总线(GlobalEventBus)
-
一种组件间通信的方式,适用于任意组件间通信。
-
安装全局事件总线:
1
2
3
4
5
6
7new Vue({
......
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
},
......
}) -
使用事件总线:
-
接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
1
2
3
4
5
6
7methods(){
demo(data){......}
}
......
mounted() {
this.$bus.$on('xxxx',this.demo)
} -
提供数据:
this.$bus.$emit('xxxx',数据)
-
-
最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
消息订阅与发布(pubsub)
-
一种组件间通信的方式,适用于任意组件间通信。
-
使用步骤:
-
安装pubsub:
npm i pubsub-js
-
引入:
import pubsub from 'pubsub-js'
-
接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
1
2
3
4
5
6
7methods(){
demo(data){......}
}
......
mounted() {
this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
} -
提供数据:
pubsub.publish('xxx',数据)
-
最好在beforeDestroy钩子中,用
PubSub.unsubscribe(pid)
去取消订阅。
-
nextTick
- 语法:
this.$nextTick(回调函数)
- 作用:在下一次 DOM 更新结束后执行其指定的回调。
- 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
扩展学习
力扣算法练习 两个一般难度 暴力做的出来 算法优解理解了很久 两题做了一下午
日总结
今天的学习内容不是很难,还是对案例的优化。重点在全局事件总线,它和消息订阅所实现的功能差不多的,但是还是全局事件比较方便而且不依靠第三方插件。然后是对文本框点击编辑按钮后,对文本框的聚焦,会发现是实现功能上先后逻辑顺序的问题导致无法实现的,我想到的是用一个setTimeout函数延时执行,也能实现,但是功能实用性不强就是了,nextTick就能很好的解决这个问题,在下一次DOM更新结束后执行,学习状态还不错。
Vuex
学习日期: 3.20
所学内容概述
1.概念
在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
2.使用情况
多个组件需要共享数据时
3.搭建vuex环境
-
创建文件:
src/store/index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions对象——响应组件中用户的动作
const actions = {}
//准备mutations对象——修改state中的数据
const mutations = {}
//准备state对象——保存具体的数据
const state = {}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
}) -
在
main.js
中创建vm时传入store
配置项1
2
3
4
5
6
7
8
9
10
11......
//引入store
import store from './store'
......
//创建vm
new Vue({
el:'#app',
render: h => h(App),
store
})
4.基本使用
-
初始化数据、配置
actions
、配置mutations
,操作文件store.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//引用Vuex
Vue.use(Vuex)
const actions = {
//响应组件中加的动作
jia(context,value){
// console.log('actions中的jia被调用了',miniStore,value)
context.commit('JIA',value)
},
}
const mutations = {
//执行加
JIA(state,value){
// console.log('mutations中的JIA被调用了',state,value)
state.sum += value
}
}
//初始化数据
const state = {
sum:0
}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
}) -
组件中读取vuex中的数据:
$store.state.sum
-
组件中修改vuex中的数据:
$store.dispatch('action中的方法名',数据)
或$store.commit('mutations中的方法名',数据)
备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写
dispatch
,直接编写commit
5.getters的使用
-
概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。
-
在
store.js
中追加getters
配置1
2
3
4
5
6
7
8
9
10
11
12
13......
const getters = {
bigSum(state){
return state.sum * 10
}
}
//创建并暴露store
export default new Vuex.Store({
......
getters
}) -
组件中读取数据:
$store.getters.bigSum
6.四个map方法的使用
-
mapState方法:用于帮助我们映射
state
中的数据为计算属性1
2
3
4
5
6
7computed: {
//借助mapState生成计算属性:sum、school、subject(对象写法)
...mapState({sum:'sum',school:'school',subject:'subject'}),
//借助mapState生成计算属性:sum、school、subject(数组写法)
...mapState(['sum','school','subject']),
}, -
mapGetters方法:用于帮助我们映射
getters
中的数据为计算属性1
2
3
4
5
6
7computed: {
//借助mapGetters生成计算属性:bigSum(对象写法)
...mapGetters({bigSum:'bigSum'}),
//借助mapGetters生成计算属性:bigSum(数组写法)
...mapGetters(['bigSum'])
}, -
mapActions方法:用于帮助我们生成与
actions
对话的方法,即:包含$store.dispatch(xxx)
的函数1
2
3
4
5
6
7methods:{
//靠mapActions生成:incrementOdd、incrementWait(对象形式)
...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
//靠mapActions生成:incrementOdd、incrementWait(数组形式)
...mapActions(['jiaOdd','jiaWait'])
} -
mapMutations方法:用于帮助我们生成与
mutations
对话的方法,即:包含$store.commit(xxx)
的函数1
2
3
4
5
6
7methods:{
//靠mapActions生成:increment、decrement(对象形式)
...mapMutations({increment:'JIA',decrement:'JIAN'}),
//靠mapMutations生成:JIA、JIAN(对象形式)
...mapMutations(['JIA','JIAN']),
}
备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。
扩展学习
模块化+命名空间
-
目的:让代码更好维护,让多种数据分类更加明确。
-
修改
store.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25const countAbout = {
namespaced:true,//开启命名空间
state:{x:1},
mutations: { ... },
actions: { ... },
getters: {
bigSum(state){
return state.sum * 10
}
}
}
const personAbout = {
namespaced:true,//开启命名空间
state:{ ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
countAbout,
personAbout
}
}) -
开启命名空间后,组件中读取state数据:
1
2
3
4//方式一:自己直接读取
this.$store.state.personAbout.list
//方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']), -
开启命名空间后,组件中读取getters数据:
1
2
3
4//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取:
...mapGetters('countAbout',['bigSum']) -
开启命名空间后,组件中调用dispatch
1
2
3
4//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'}) -
开启命名空间后,组件中调用commit
1
2
3
4//方式一:自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations:
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
日总结
今天的学习内容是vuex,感觉它的功能就是像是一个Java中的static,设置全局变量或者全局方法,比之前的组件间通讯都好用,流程梳理明白掌握起来也不难,有复杂计算逻辑的需要放Mutations中,普通的就放actions中就行,所有可以使用计算属性的,都可以用mapstate,模块化是算扩展了,也不算很难,以后为了规范性以及代码可读性,估计还是使用vuex模块化的比较多。
mysql数据导入
学习日期: 3.22
所学内容概述
将小白样题数据导入
source /opt/ds_db01.sql,发现有三个表报错了,解决了挺久的bug,因为sql文件比较大,所以导入或者修改的时候会很卡,而且迟迟不能成功。打开以后,慢慢找创建表语句,再慢慢修改。
BUG点
导入数据的时候出现的bug,导入小白样题的时候,发现只有20表,缺失了三个表,打开sql的源文件,单独创表,发现出现了报错。搜索以后发现是timestamp类型的不能超过两个default,原因是当你给一个timestamp设置为on updatecurrent_timestamp的时候,其他的timestamp字段需要显式设定default值。如果你有两个timestamp字段,但是只把第一个设定为current_timestamp而第二个没有设定默认值,MySQL能成功建表,但是反过来就不行,需要把之前的改成datetime,后面null的都删除,BUG就解决了。
日总结
今天上午先是做了几道蓝桥杯的题目,有道填空题,是直接拿草稿纸推算的。下午的时候看了一下小白的样题,发现之前的sql文件是有点小bug的,无法导入,有两个表是有bug的,一下午解决了bug,然后分析了一下任务书,晚上将平时训练的计划规划了一下。
测试新镜像连接
学习日期: 3.23
所学内容概述
下载镜像导入VM,启动hadoop集群以及hive元数据,一切顺利。
BUG点
今天使用IDEA测试连接的时候报错了,之前出现过这种报错也没找到原因,我认为就是mariadb的问题。重新放入配置文件,修改hosts中的ip映射都不行,就算了,有时候就是会突然坏一下,这几天都没关机什么都没干,直接在spark中跑也是一样的,而且比idea要快很多。
扩展学习
编号468
回文日期
题目很清晰明了,但是需要讨论的特殊条件比较多。就是日期规范性,每个月每个天数不一样,还有日期开头不为0,学份12月以内,不可为0,等等。在我最初的版本都是有一条一条if规范出来的。后来优化算法的时候,发现有些测试点,蓝桥杯是没有设置一些特殊检查点的所以像30天和28 29这种特殊月。所以后面闰月和2月都没用添加判断语句,但是也顺利通过了,耗时有点久。
1 | import java.util.*; |
日总结
今天的主要任务是设置大数据的学习计划,平时练习的计划,然后导入了数据库,然后导入了新的镜像,发现里面是自带数据的,值得注意的是,software的安装位置都改变了,在IDEA中需要修改配置文件。hive hdfs 和core,yarn是不需要的, 晚上做了算法题,做题还是很顺利的,但是有个任务点一直无法通过,优化了算法,节省了不必要的空间。
Spark架构以及集群搭建
学习日期: 3.24
所学内容概述
Spark理论
Spark资源调度策略,之前spark内存爆炸过。看内容是有调优策略的,主要原因是CPU资源占用满了,电脑的i5 3代太垃垮了,之前也使用过spark的调度程序,还是爆了,最后是通过重启hadoop服务成功的。
BUG点
报错信息为截图,但是记住了。java.sql.SQLException: null, message from server: “Host ‘XXX‘ is not allowed to connect异常。是mysql拒绝了我的该网段的链接,是因为mysql中root用户只允许本地连接和 回环地址,之前IDEA无法直接运行的BUG也算是解决了。
解决步骤:
use mysql;
select user,host from user;
update user set host = ‘%’ where user = ‘root’;
flush privileges; --刷新权限
日总结
今天的任务是看小白的spark教程,基础部分,自己基本都学过,就当过了一遍,在ch02开发和部署的实战部分,我就直接把样题数据从mysql导入hive中去了,当实操,用savetable,可以直接自动创建表,往hive中加入了部分假数据,基础部分感觉只有资源调度那边是有点用的,其他都是发展史。
Spark核心编程RDD
学习日期: 3.25
所学内容概述
Spark集合方法
1 | // 构造这两个RDD 充当假数据 |
union(otherDataset)
合并两个rdd,并集。
1 | val rdd3 = rdd1.union(rdd2) |
intersection(otherDataset)
交集。只保留在两个rdd中都有的元素。
1 | val rdd4 = rdd1.intersection(rdd2) |
subtract转换
差集。只保留在第一个rdd中有而在第二个rdd中没有的元素。
1 | val rdd5 = rdd1.subtract(rdd2) |
cartesian(otherDataset)
两个rdd的笛卡尔集。
当在类型为T和U的RDD上调用时,返回一个(T, U)对(所有元素对)的RDD。
1 | val rdd6 = rdd1.cartesian(rdd2) |
BUG点
学习顺利没BUG
扩展学习
学习了action部分对表转换成rdd以后的操作函数还是有很多值得学习
action操作函数
-
reduce(func)
使用函数func(接受两个参数并返回一个参数)聚合数据集的元素。这个函数应该是交换律和结合律,这样才能并行地正确地计算它。
-
collect()
将RDD操作的所有结果返回给驱动程序。这通常对产生足够小的数据集的操作很有用。
-
count()
这会返回数据集中的元素数量或RDD操作的结果输出。
-
first()
返回数据集的第一个元素或RDD操作产生的结果输出。它的工作原理类似于take(1)函数。
-
take(n)
返回RDD的前n个元素。它首先扫描一个分区,然后使用该分区的结果来估计满足该限制所需的其他分区的数量。这个方法应该只在预期得到的数组很小的情况下使用,因为所有的数据都加载到驱动程序的内存中。
-
top(n)
按照指定的隐式排序[T]从这个RDD中取出最大的k个元素,并维护排序。这与takeOrdered相反。这个方法应该只在预期得到的数组很小的情况下使用,因为所有的数据都加载到驱动程序的内存中。
-
takeOrdered(n)
返回RDD的前n个(最小的)元素,并维护排序。这和top是相反的。这个方法应该只在预期得到的数组很小的情况下使用,因为所有的数据都加载到驱动程序的内存中。
-
saveAsTextFile(path)
将数据集的元素作为文本文件(或文本文件集)写入本地文件系统、HDFS或任何其他hadoop支持的文件系统的给定目录中。Spark将对每个元素调用toString,将其转换为文件中的一行文本。
-
countByKey()
仅在类型(K, V)的RDDs上可用。返回(K, Int)对的hashmap和每个键的计数。
-
foreach(func)
在数据集的每个元素上运行函数func。
日总结
复习使用了spark中的RDD编程,集合部分感觉对大数据处理表数据的时候还是有帮助的,然后自己看了以前学期scala时候,中spark的RDD和scala中的方法,结合小白书苑还是有很多对于RDD方法的提升和理解的,使用sql编程,和RDD一起使用并独立完成了RDD的案例。
SparkSQL编程
学习日期: 3.27-3.28
所学内容概述
因为27号sql部分基础所学的内容比较简单没什么可以值得记得,28号学习的sql高级有些需要记笔记的部分,因此将两天的所学内容放到一起。
Spark SQL内置函数
SQL函数大致分为四类:
- 标量函数:每一行返回单个的值。
- 聚合函数:每一组行返回单个的值。
- 窗口函数:每一组行返回多个值。
- 用户自定义函数(UDF):包括自定义的标量函数或聚合函数。
标量和聚合函数在spark的内置方法包中,使用需要导入包
1 | import org.apache.spark.sql.functions._ //(_通配,将functions所有方法都导入) |
常用的内置函数自己基本都使用过,就先不一一列出来了,找个时间全部梳理一下。
Spark SQL聚合函数
这部分基本是和sql中的使用是一样的,参数也一样,就是分组聚合的函数,DSL风格通过groupBy(col)进行分组。
聚合函数 | 描述 |
---|---|
count(col) | 返回每组中成员数量 |
countDistinct(col) | 返回每组中成员唯一数量 |
approx_count_distinct(col) | 返回每组中成员唯一近似数量 |
min(col) | 返回每组中给定列的最小值 |
max(col) | 返回每组中给定列的最大值 |
sum(col) | 返回每组中给定列的值的和 |
sumDistinct(col) | 返回每组中给定列的唯一值的和 |
avg(col) | 返回每组中给定列的值的平均 |
skewness(col) | 返回每组中给定列的值的分布的偏斜度 |
kurtosis(col) | 返回每组中给定列的值的分布的峰度 |
variance(col) | 返回每组中给定列的值的无偏方差 |
stddev(col) | 返回每组中给定列的值的标准差 |
collect_list(col) | 返回每组中给定列的值的集合。返回的集合可能包含重复的值 |
collect_set(col) | 返回每组中给定列的唯一值的集合 |
数据缺失值处理
比如df表如下:
1 | ds1.na.drop().show() //删除缺失值的行 有null的都删除 |
新数据如下,验证去重函数
1 | ds1.dropDuplicates().show() //完全重复的行删除 注意第2和4不一样,一个清朝一个清代 |
BUG点
安装使用Vue3中的vite初始化vue项目结束以后,需要npm i装插件的时候报错了,分析报错信息是npm版本太低了,有些插件装不了,因为vite是比较新的构建项目的一种方式,比较轻量化大小比较小,速度也快了不止一倍,在项目越大中和常规初始化的差距越明显。
扩展学习
蓝桥杯 123
这题主要用到二分以及数据累加,难度一般,明天打算优化优化算法再研究研究。
1 | import java.util.Scanner; |
日总结
27日
今天学习时间只有上午部分,还好这个时间学习的内容不是很难,SparkSQL自己已经是非常熟悉了,对DF的创建以及操作,基础部分没什么难度,案例也没有必要去做了。今天的学习状态也还可以,明天学高级部分看能不能学习到有用的东西
28日
大数据训练部分:今天上午学习的大数据,SparkSQL高级部分所能学到的东西还是听过的,比如自定义sql函数,以及如何处理字段为null的数据,删除或者替换,还有去重相同的数据,都是有方法的。基本这一章的内容就是对sql中高级的函数的讲解以及如何使用,大半部分是对dsl风格的讲解,因为之前学过一段时间了,前几天也着重复习了,所以这部分对自己没什么难度。
Vue路由(重点)
学习日期:3.29
所学内容概述
- 理解: 一个路由(route)就是一组映射关系(key - value),多个路由需要路由器(router)进行管理。
- 前端路由:key是路径,value是组件。
1.基本使用
-
安装vue-router,命令:
npm i vue-router
-
应用插件:
Vue.use(VueRouter)
-
编写router配置项:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//引入VueRouter
import VueRouter from 'vue-router'
//引入Luyou 组件
import About from '../components/About'
import Home from '../components/Home'
//创建router实例对象,去管理一组一组的路由规则
const router = new VueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home
}
]
})
//暴露router
export default router -
实现切换(active-class可配置高亮样式)
1
<router-link active-class="active" to="/about">About</router-link>
-
指定展示位置
1
<router-view></router-view>
2.几个注意点
- 路由组件通常存放在
pages
文件夹,一般组件通常存放在components
文件夹。 - 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
- 每个组件都有自己的
$route
属性,里面存储着自己的路由信息。 - 整个应用只有一个router,可以通过组件的
$router
属性获取到。
3.多级路由(多级路由)
-
配置路由规则,使用children配置项:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20routes:[
{
path:'/about',
component:About,
},
{
path:'/home',
component:Home,
children:[ //通过children配置子级路由
{
path:'news', //此处一定不要写:/news
component:News
},
{
path:'message',//此处一定不要写:/message
component:Message
}
]
}
] -
跳转(要写完整路径):
1
<router-link to="/home/news">News</router-link>
4.路由的query参数
-
传递参数
1
2
3
4
5
6
7
8
9
10
11
12
13<!-- 跳转并携带query参数,to的字符串写法 -->
<router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
<!-- 跳转并携带query参数,to的对象写法 -->
<router-link
:to="{
path:'/home/message/detail',
query:{
id:666,
title:'你好'
}
}"
>跳转</router-link> -
接收参数:
1
2$route.query.id
$route.query.title
5.命名路由
-
作用:可以简化路由的跳转。
-
如何使用
-
给路由命名:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17{
path:'/demo',
component:Demo,
children:[
{
path:'test',
component:Test,
children:[
{
name:'hello' //给路由命名
path:'welcome',
component:Hello,
}
]
}
]
} -
简化跳转:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<!--简化前,需要写完整的路径 -->
<router-link to="/demo/test/welcome">跳转</router-link>
<!--简化后,直接通过名字跳转 -->
<router-link :to="{name:'hello'}">跳转</router-link>
<!--简化写法配合传递参数 -->
<router-link
:to="{
name:'hello',
query:{
id:666,
title:'你好'
}
}"
>跳转</router-link>
-
6.路由的params参数
-
配置路由,声明接收params参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News
},
{
component:Message,
children:[
{
name:'xiangqing',
path:'detail/:id/:title', //使用占位符声明接收params参数
component:Detail
}
]
}
]
} -
传递参数
1
2
3
4
5
6
7
8
9
10
11
12
13<!-- 跳转并携带params参数,to的字符串写法 -->
<router-link :to="/home/message/detail/666/你好">跳转</router-link>
<!-- 跳转并携带params参数,to的对象写法 -->
<router-link
:to="{
name:'xiangqing',
params:{
id:666,
title:'你好'
}
}"
>跳转</router-link>特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
-
接收参数:
1
2$route.params.id
$route.params.title
7.路由的props配置
作用:让路由组件更方便的收到参数
1 | { |
8.<router-link>
的replace属性
- 作用:控制路由跳转时操作浏览器历史记录的模式
- 浏览器的历史记录有两种写入方式:分别为
push
和replace
,push
是追加历史记录,replace
是替换当前记录。路由跳转时候默认为push
- 如何开启
replace
模式:<router-link replace .......>News</router-link>
9.编程式路由导航
-
作用:不借助
<router-link>
实现路由跳转,让路由跳转更加灵活 -
具体编码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19//$router的两个API
this.$router.push({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
this.$router.replace({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go() //可前进也可后退
10.缓存路由组件
-
作用:让不展示的路由组件保持挂载,不被销毁。
-
具体编码:
1
2
3<keep-alive include="News">
<router-view></router-view>
</keep-alive>
11.两个新的生命周期钩子
- 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
- 具体名字:
activated
路由组件被激活时触发。deactivated
路由组件失活时触发。
12.路由守卫
-
作用:对路由进行权限控制
-
分类:全局守卫、独享守卫、组件内守卫
-
全局守卫:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24//全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to,from,next)=>{
console.log('beforeEach',to,from)
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
next() //放行
}else{
alert('暂无权限查看')
// next({name:'guanyu'})
}
}else{
next() //放行
}
})
//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
console.log('afterEach',to,from)
if(to.meta.title){
document.title = to.meta.title //修改网页的title
}else{
document.title = 'vue_test'
}
}) -
独享守卫:
1
2
3
4
5
6
7
8
9
10
11
12
13beforeEnter(to,from,next){
console.log('beforeEnter',to,from)
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'atguigu'){
next()
}else{
alert('暂无权限查看')
// next({name:'guanyu'})
}
}else{
next()
}
} -
组件内守卫:
1
2
3
4
5
6//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}
13.路由器的两种工作模式
- 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
- hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
- hash模式:
- 地址中永远带着#号,不美观 。
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
- 兼容性较好。
- history模式:
- 地址干净,美观 。
- 兼容性和hash模式相比略差。
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
总结
这两天把Vue的重中之重的路由学完了,基本都是掌握了,内容还是挺多的,但是我觉得不是很难,之前给金师附小渲染的时候,主要接触的就是路由,所以掌握就没什么难度,我的理解就是通过路由实现跳转不同的组件,不用刷新页面,然后就是缓存以及守卫,也是比较简单,就几个方法几个参数,
Vue3(重点)
学习日期:3.30-3.31
所学内容概述
创建Vue3.0
vue-cli创建
和vue2一样了那就
1 | ## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上 |
vite创建(推荐)
这种创建的方式运行和打包都会高效很多,端口号和普通的不一样默认是3000
1 | ## 创建工程 |
Composition API(重点)
这是Vue3的重中之重,因为内容比较多,放网站上去了。https://u7u7.top/posts/adf5c49.html下面vue3部分
总结
花了两三天的时间把Vue3基础给学习了,Vue3给我最直观的改变就是使用了composition API把setup作为舞台,将原来的vue2那种数据以及方法在不同生命周期地方,使用不同的方法,都放在了setup中,最后只要return就行了,和java和python中所定义的方法一样,里面有属性以及一些方法。而且代码看起来舒服很多,可读性对我而言强了,上手还是蛮快的,也没什么难点,生命周期部分也非常的知名见意,我觉得vue3的setup这样的写法还是很适合我的。而且原本Vue2中的语法在vue3中都是可以使用的,虽然官方不建议这么做,但是也能写能用,如果和Vue3有冲突的地方,会以Vue3为主,因为自己是使用vite创建的vue3工程,npm run dev跑起来真的比serve快了很多,打包也快了不少。我觉得Vue3的核心应该是响应式proxy,属性值的读写、属性的添加、属性的删除都可以通过proxy完成,所以我认为该点为核心部分,而且基本API都离不开proxy。