hadoop 生态圈:
HDFS 分布式文件储存系统
MapReduce 分布式离线计算引擎
Yarn 资源调用
Zookeeper 分布式协调服务
Hive 数据仓库/数据分析
Flume 数据采集
Spoop 数据迁移
HBase NoSql:实现百万数据级的毫秒级操作
Spark
kafka 消息队列
Scala 函数式编程语言
Spark RDD
SparkSql
SparkStreaming
SparkCore
缓存机制
Elasticsearch 大数据弹性分布式搜索引擎
Git GitHUB 分布式项目管理工具/代码托管平台
Docker
俗称的 3V :
第一个是Volume(海量),数据容量越来越大;第二个是Velocity(速度),数
据量增长越来越快,需要处理的速度和响应越来越快;第三个是Variety(多样性),指各种各样类型的数据出现,过去的数据更多的是结构化的,现在越来越多的数据是半结构,甚至是完全没有结构的数据,如文本、邮件甚至于语音、视频等。“3V”是对大数据最基本特征的归纳,得到业界的共识。
虽然后续不断有人增加对V的理解,如Value(价值),强调大数据中的总体价值大,但是价值密度低;也有Veracity(真实和准确)
,强调真实而准确的数据才能让对数据的管控和治理真正有意义,也有Vitality(动态性)
强调数据体系的动态性等,这些都有一定的道理,但都不及最初的“3V”具有代表性。
hdfs fsck / --fsck(文件系统一致性检查)
集群的基本信息。整个集群的空间多大 所有目录多少 所有文件 所有块(平均的块大小) 所有链接 备份的块 个数 有没有块坏掉 有没有块备份丢失 集群里面有多少个节点
hdfs dfs -df -h
查看集群的硬盘使用情况
hdfs dfs -du /
目录下的硬盘空间使用情况
hdfs dfs -ls -h /file
显示具体的文件大小(差不多就这个意思,转换成可读懂的计量单位)
MapReduce是什么? --MapReduce(分布式计算模型)
用于大规模数据处理
map阶段负责对输入文件进行切分处理,然后汇总再分组给reduce进行处理,以达到高效的分布式计算效率
1)MapReduce是什么?
a、MapReduce是一种分布式计算模型模型,用于大规模数据集(大于1TB)的并行运算。
b、相对于Hadoop框架来说,其最核心设计就是:HDFS和MapReduce。HDFS提供了海量数据的存储,MapReduce提供了对数据的计算。
c、MapReduce把任务分为 map(映射)阶段和reduce(化简)。
每个MapReduce作业由两个阶段组成
Map
Reduce
MaoReduce自动计算
MapReduce计算是并行和自动分布的;
开发人员只需要专注于实现映射和reduce功能;
M/R是用Java编写的,但也支持Python流。
如何知道和显示当前所在数据库?
select current_database();
--current(现在的)
数据库描述:
describe database ...(嗯哼);
--(也可以简写为desc) desc formatted ...(嗯哼); (更详细的信息)还可以显示location)--describe(描述)
如何知道你的表是怎么创建的(显示建表语句):
show create table ...(嗯哼)
--显示你的建表语句
Hive Tables --(表)
External Tables: --(外部表)
数据保存在由LOCATION关键字指定的HDFS路径中。Hive不完全管理数据, 因为删除表(元数据)不会删除数据
Internal Tables: --(内部表)
数据保存在默认路径中,例如/user/hive/warehouse/employee。数据完全由Hive管理,因为删除表(元数据)也会删除数据
Temporary Tables: -- (临时表)
temporary table只对当前session有效,session退出之后,table自动删除
当名称相同时,temporary table优先于相同名称
--(只有drop或rename之后,,才能使用原始表)
不支持分区和创建索引
如何确定你的表是临时表:
show create table ...(嗯哼);
--(查看)
Hive建表高阶语句:
CTAS:
Create Table As Select(之能创建内部表)
--CTAS(使用查询创建表)
create table 要创建的表 as select * from 目标表(复制数据);
CANNOT create a partition, external, or bucket table --cannot,partion,external talbe,bucket table(无法,分区,外部表,分桶)
CTE:
with Common Table Expression
--CTE(公共表达式)
CREATE TABLE cte_employee AS
WITH
r1 AS (SELECT name FROM employee WHERE sex_age.sex= 'Male'),
--查询sex 为 Male 的 name
r2 AS (SELECT name FROM r2 WHERE name = 'Michael'),
--查询sex 为 Male , name 为 Michael
r3 AS (SELECT name FROM employee WHERE sex_age.sex= 'Female')
--查询sex 为 Male 的 name
SELECT * FROM r2 UNION ALL SELECT * FROM r3;
--把r2和r3的查询结果写到emp_cte中 --union all(联合 )
like:
CREATE TABLE 要创建的表 LIKE 目标表(复制表结构);
--(快速的创建一个表,复制目标表的的表结构,没有数据)
purge:
drop talbe ...(嗯哼) purge;
--内部表:完全删除,无法找回 外部表:只删除表结构,数据还在
truncate:
truncate talbe ...(嗯哼);
--用来删除表中的数据(外部表除外)(delete 删除单行)
重命名表:
ALTER TABLE table_name rename to new_table_name;
--(数据所在的位置和分区都没有改变。)
修改表属性:
alter table table_name set TBLPROPERTIES ('EXTERNAL'='TRUE');
--内部表转外部表alter table
alter table table_name set TBLPROPERTIES ('EXTERNAL'='FALSE');
--外部表转内部表
增加/更新列:
ALTER TABLE table_name ADD|REPLACE COLUMNS (col_name data_type);
--add 在最后一行添加列 replace 删除所有列,重新添加
对Hive表的三连问:
1.什么是内部,外部表?
未被external修饰的是内部表(Internal table),被external修饰的为外部表(external table)
2.它们的区别是什么?:
1)内部表数据由Hive自身管理(完全控制),外部表数据由HDFS管理;
2)内部表数据存储的位置是hive.metastore.warehouse.dir(默认:/user/hive/warehouse),外部表数据的存储位置由自己制定;
3)删除外部表仅仅会删除元数据,HDFS上的文件并不会被删除;删除内部表会直接删除元数据(metadata)及存储数据
4)对内部表的修改会将修改直接同步给元数据,而对外部表的表结构和分区进行修改,则需要修复(MSCK REPAIR TABLE table_name; )
3.它们适合应用在什么场景?
外部表:
对数据产生了保护作用,适合用于储存原数据(重要),需要定期将外部数据映射到表中。也适合用于共享资源,(如果要对用户共享数据,可以直接把表给他, 缺点是需要把自己数据库的访问权限给他,让他访问自己的权限。。。 ,那这样我可以把要共享的数据整理到一个新的database,新的External(外部表)里,这样就不会让他访问我的数据库,然后就可以给他hdfs的路径,)
内部表:在做统计分析时候用到的中间表,结果表可以使用内部表。(对数据清洗和内部转换)
Hive Partition: --partiton (分区)
在Hive Select查询中一般会扫描整个表内容,会消耗很多时间做没必要的工作。有时候只需要扫描表中关心的一部分数据,因此建表时引入了partition概念
一个表可以拥有一个或者多个分区,每个分区以文件夹的形式单独存在表文件夹的目录下。
分区是以字段的形式在表结构中存在的。
分区建表分为2种,一种是单分区,也就是说在表文件夹目录下只有一级文件夹目录。另外一种是多分区,表文件夹下出现多个文件夹嵌套模式。
--(以文件夹的形式储存在HDFS)
创建分区表:
create table table_name (...) partiton by (partiton_name data_type);
显示表中的分区:
show partitons table_name;
添加分区:
Alter TABLE table_name ADD PARTITION (dt='20130101') ;
--(一次添加一个分区)
修改分区:
Alter TABLE table_name PARTITION (dt='2008-08-08') SET LOCATION "new location";
--修改location (每次都要手动Alter)
Alter TABLE table_name PARTITION (dt='2008-08-08') RENAME TO PARTITION (dt='20080808');
--重命名
删除分区:
Alter TABLE table_name DROP PARTITION (dt='2008-08-08');
Dynamic Partition: --dynamic partition(动态分区)
当数据量很大而我们不知道分区值是多少时,动态分区是非常有用的。
dynamic partition的两个参数:
set hive.exec.dynamic.partition=true;
--开启动态分区(默认是false)(动态分区是一个很刺激的操作,因为一不小心就很可能弄出成千上万个分区,后果嘛...(嗯哼),所有在默认情况下,动态分区是没有打开的)
set hive.exec.dynamic.partition.mode=nonstrict;
--开启允许所有分区都是动态的,否则必须要有静态分区才能使用。
Hive Partition 三连问:
1.hive partiotion的作用:
在Hive Select查询中一般会扫描整个表内容,会消耗很多时间做没必要的工作。有时候只需要扫描表中关心的一部分数据,因此建表时引入了partition概念
2.hive有几种partiton:
两种:static partiton 和 dynamice partition
3.不同分区的适用场景和最佳实践:
静态分区:根据数据到来的频率,新数据的到来
动态分区:进行数据转换、重新组合,根据已有数据进行动态分区
Hive Buckets: --buckets(分桶)
hive.optimize.bucketmapjoin= true
--(让hive强制分桶,自动按照分桶表的bucket 进行分桶)
好处:
1、方便抽样
2、提高join查询效率
buckets的column是数据文件的column,不是凭空生成的
buckets的个数是2的N次方
buckets是由column组织成多个文件
--(文件的个数由buckets决定)
创建 buckets table:
CREATE TABLE bucketed_user (id INT) name STRING)
CLUSTERED BY (id) INTO 4 BUCKETS;
往表中插入数据:
INSERT OVERWRITE TABLE bucketed_users SELECT * FROM users;
--(向分桶表中插入数据的时候必然要执行一次MapReduce)
如何将数据插入分桶表
1.从hdfs或本地磁盘中load数据,导入中间表
2.通过从中间表查询的方式的完成数据导入
Hive Views: --views(视图)
视图是建立在已有表的基础上,如果基表被修改或删除,那视图作废
--(视图赖以建立的这些表称为基表)
视图可以简化复杂的查询,提供了一定的逻辑独立性
视图不储存数据,
视图从数据库的基本表中选取出来的数据组成的逻辑窗口
--(可以被定义为多个表的连接,也可以被定义为只有部分列可见,也可为部分行可见)
建立视图支持cte,order by,limit,join,etc;
查找视图用, show tables/show views (仅在hive v2.2.0之后的版本)
显示View定义用 show create table view_name;
hive views的一些常用语句:
建立视图:create view view_name as select target_table_name;
删除视图:drop view_name;
更改视图属性:alter view view_name set tblproperties(...);
更改视图定义:alter view view_name as select target_table_name;
Hive Laternl Views: --laternl views(侧视图)
例如:
select id,explode(arry1) from table; —错误
会报错FAILED: SemanticException 1:40 Only a single expression in the SELECT clause is supported with UDTF's.
select explode(array1) from table; —正确
但是实际中经常要拆某个字段,然后一起与别的字段一起出.例如上面的id和拆分的array元素是对应的.我们应该如何进行连接呢?我们知道直接select
id,explode()是不行的.这个时候就需要lateral view出厂了.
lateral view为侧视图,意义是为了配合UDTF来使用,把某一行数据拆分成多行数据.不加lateral view的UDTF只能提取单个字段拆分,并不能塞会原来数据表中.加上lateral view就可以将拆分的单个字段数据与原始表数据关联上
也可以多次使用lateral view explode
select id,num1,num2 from table
lateral view explode(array1) subview1 as num1
lateral view explode(array2) subview2 as num2
where ...;
行转列:
select name,loc from e_text lateral view explode(work_place) a as loc;
Hive Select: --(数据映射)
distinct: 去重(去掉重复的数据)
--distinct(单独)
CTE:
with
r1 as (select name from e_text),
r2 as (select * from r1)
select name from r2 where name='Will';
Hive Join : --join(加入)
join:
select * from a a join b b on a.e_id = b.id;
--(内连接:把两表相同的条件显示出来)
select * from a a left join b b on a.e_id = b.id;
--(左连接:把表a所有数据显示出来,把表b中与表a相同的数据显示出来,不同则为null)
select * from a a right join b b on a,e_id = b.id;
--(右连接:与左连接相反,把表b所有数据显示出来,并把表a与表a相同的数据显示出来,不同则为null)
select * from a a full join b b on a.e_id = b.id;
--(外连接/全连接:两表有相同的数据,则显示,有不同的数据,则为null)
select * from a a left semi join b b on a.e_id = b.id ;
--(左半连接:只显示表a中,与表b相同的数据)
select * from a a cross join b b ;
--(笛卡尔积/交叉关联:和表b的每条数据做连接)
Hive Load:
load data local inpath '/' overwrite into table table_name;
--(local:本地参数,用于读取本地目录,如果不加,默认为hdfs目录
overwrite:替换文件参数,用于删除原来的数据文件,(替换))
Hive Insert:
要将数据插入表/分区,Hive使用insert语句
insert支持覆盖和插入语法
Hive支持从同一个表中插入多个数据
关键字在insert into中是可选的
所有数据插入必须具有相同数量的指定列,或在未指定时具有相同数量的所有列
insert into table_name select * from table_name;
--(按照查询表的方式添加数据,如果col比较多,可以自行添加col);
from table_name
insert overwrite table table_name
select *
insert overwrite table talbe_name
select *;
--(按照查询表的方式给两个表添加数据,以被插入数据的表为基准,必须把所有列全部插入数据,不然报错)
--(多重insert因为是一条语句,所以执行速度更快,性能更好)
from table_name
insert overwrite table partition(col1,col2)
select * ;
--(同上,查询表添加分区数据)
insert into table_name(col) select '...' from table_name limit 1;
--(添加查询col的数据到table_name,但表内如果有其他col,值为null)
insert into table table_name(col_name) values(v1),(v2);
--(简单的insert的语句,col随意)
overwrite不支持指定col插入,
into可以
from table_name
insert overwrite directory 'path'
select *
insert overwrite local directory 'path'
select *
insert overwrite table table_name
select *;
--local(本地),directory(目录)
--(把查询出来的数据导出到hdfs path,加了local就本地 path,最后插入到table_name)
--(高性能,支持多种数据类型)
insert overwrite directory 'path'
row format delimited fields terminated ','
select * from table_name;
--(数据导出的经典方式)
hdfs dfs -getmerge
--(用上面的方式导出的数据,就可以用这行hdfs code整合到本地目录)
Hive Data Ex/Import
export and import用与数据迁移
--(hive暂时没有单行可以ex/import database的code)
export table table_name to 'path';
export table table_name to partition (col1,col2) 'path';
--(导出表和分区的数据,导出的目标目录必须是空的,执行速度so fast)
--(导出之后path下会出现一个_metadata的元数据文件和一个data文件夹,data下是表数据)
import table table_name 'path';
import table table_partition_name 'path';
--(导出后,我们可以手动将导出的文件复制到其他HDFS。然后,使用import语句导入它们)
Hive Sorting data --sort data(数据排序)
order by 使用一个reducer执行全局数据排序
--order by(以...排序)
order by 支持使用with CASE WHEN或表达式
order by 默认是正序排序 desc:倒序排序
缺点:
由于是全局排序,所以所有的数据会通过一个Reducer 进行处理,当数据结果较大的时候,
只有一个Reducer 进行处理十分影响性能
当开启MR 严格模式的时候order by 必须要设置 limit子句 ,否则会报错
set hive.mapred.mode=strict;
--(开启严格模式)
由于Hive 中的ORDER BY 对于大数据集 存在性能问题,
延伸出了部分排序,以及将按相同KEY 控制到同一划分集合的需求
使用sort by 你可以指定执行的reduce 个数 (set mapred.reduce.tasks=
--(如果number数 = 1 那么 sort by 等于 order by)
sort by: --sort by(排序方式)
sort by 是一个部分排序方案, 其只会在每个reducer 中对数据进行排序,
也就是执行一个局部排序过程。
排序通常不单独使用
sort/distribute/cluster by的列必须出现在select列表中(*)
distribute by:
distribyte by 控制map 中的输出在 reducer 中是如何进行划分的。
使用distribute 可以保真相同KEY的记录被划分到一个Reduce 中。
distribute通常用在sort by 之前
如果对某一列既想采用sort by也想采用 distribute,那么可以使用cluster by 进行排序
--(排序只能是升序排序(默认排序规则),不能指定排序规则为asc 或者desc。)
cluster by = sort by + distribute by 在同一列
cluster by 不能 asc desc;
为了充分利用所有的约简方法进行全局排序,我们可以先使用cluster,然后再使用order by
Hive Aggregation --aggregation(聚合运算)
group by:
在select子句中使用group by不能写与聚合无关的col
例:
select id,max(date) from offers group by score;
报错:
表达式id不在group by中
having和where的区别:
where用于筛选条件查询
having用于聚合函数的条件查询
--(where子句要快于聚合语句)
where子句作用于表和视图,having子句作用于组。 where在分组和聚合计算之前选取输入行
而 HAVING 在分组和聚合之后选取分组的行
select count(distinct id),sum(distinct id) from a group by e_id;
window function:
排序函数:
RANK 返回数据项在分区中的排名。排名值序列可能会有间隔
DENSE_RANK 返回数据项在分区中的排名。排名值序列是连续的,不会有间隔
PERCENT_RANK 计算当前行的百分比排名
ROW_NUMBER 确定分区中当前行的序号
CUME_DIST 计算分区中当前行的相对排名
NTILE() 将每个分区的行尽可能均匀地划分为指定数量的分组
聚合类:
COUNT: 计数,可以和DISTINCT一起用,从v2.1.0开始没有ORDER BY和window_cause。完全支持v2.2.0。
SUM: 聚合
AVG: 均值
MAX / MIN: 最大 / 小值
分析类:
LAG() LAG()窗口函数返回分区中当前行之前行(可以指定第几行)的值。 如果没有行,则返回null。
LEAD() LEAD()窗口函数返回分区中当前行后面行(可以指定第几行)的值。 如果没有行,则返回null。
FIRST_VALUE FIRST_VALUE窗口函数返回相对于窗口中第一行的指定列的值。
LAST_VALUE LAST_VALUE窗口函数返回相对于窗口中最后一行的指定列的值。
Hive Transaction:
Atomicity: 不可再分割的工作单位,事务中的所有操作要么都发,要 么都不发。
Consistency: 事务开始之前和事务结束以后,数据库的完整性约束没有
被破坏。这是说数据库事务不能破坏关系数据的完整性以及业务逻辑上的 一致性。
Isolation: 多个事务并发访问,事务之间是隔离的,
Durability: 意味着在事务完成以后,该事务锁对数据库所作的更改便持 久的保存在数据库之中,并不会被回滚。
从v0.14开始支持行级事务
支持插入、删除、更新(从v2.2.0开始合并)
不支持开始、提交和回滚
不支持buckets colums and partition colums的更新
--(因为这两个xx决定了物理分配,意义很大,所以不支持update也合情合理)
文件格式只支持ORC
表必须是bucketed clustered表
需要压缩工作,这需要时间、资源和空间
锁可以是共享锁和排他锁
--(S:随便访问,X:只要被一个访问,其他就访问不到了,和图书馆借书是一个道理)
不允许从一个非ACID连接写入/读取ACID表
在命令行中,通过为当前session设置:
set hive.support.concurrency = true;
set hive.enforce.bucketing = true;
set hive.exec.dynamic.partition.mode = nonstrict;
set hive.txn.manager = org.apache.hadoop.hive.ql.lockmgr.DbTxnManager;
set hive.compactor.initiator.on = true;
set hive.compactor.worker.threads = 1;
永久保存在所有会话的hive-site.xml中:
Ambari
Hive Merge:
merge只能在支持ACID的表上执行
merge最多写三条操作语句,每条最多一次 insert/delect/update
当不匹配时最后一个必须是WHEN子句。
如果同时存在UPDATE和DELETE子句,则语句中的第一个子句必须包含[和
set hive.execution.engine=tez
--(设置引擎为tez)
set hive.auto.convert.join=false
--(关闭自动转化MapJoin,默认为true)
set hive.ignore.mapjoin.hint=false
--(关闭忽略mapjoin的hints(不忽略,hints有效),默认为true(忽略hints)。)
Hbase:
什么是hbase:
HBase是一个领先的非sql数据库,它在HDFS上存储数据
--(HBase使用HDFS作为存储并利用其可靠性。)
HBase是面向列的数据库
HBase是一个分布式哈希映射。
数据访问速度快,响应时间约2-20毫秒
支持每个节点20k到100k+ ops/s的随机读写。
扩展到20,000多个节点。
时间序列数据:高容量,高速写入
信息交换-消息传递:高容量,高速读写
内容服务—Web应用程序后端:高容量,高数读取
百万级数据,毫秒级
创建表:
create 'table_name' , {NAME => 'addr'} , {NAME => 'order'}
exists 'table_name';
判断这张表是否存在
添加数据/修改数据:
put 'table_name','row_key','colum_famliy:colum_name','values'
put 'table_name','row_key','colum_famliy:colum_name','values'
--(只要满足前面的rowkey...就可以直接替换想要修改的values)
查询:
get 'table_name','row_key','colum_famliy : colum_name'
修改版本:
alter 'table_name' , {NAME => addr , VERSIONS => 5}
--(当插入的时候,后面的值会覆盖前面的值。如果想保存历史表的数据,那就需要修改表的version_numb)
scan查询:
scan 'table_name'
--(查询table)
scan 'table_name' {STARTROW => j}
--(查询以'j'为开头的rowkey)
count 'table_name'
--(查询table 中colum_famliy的个数)
删除:
deleteall 'table_name','row_key'
--(删除表中指定的rowkey)
delete 'table_name' ,'row_key','colum_famliy:colum_name','values'
--(根据情况自定义删除)
alter 'table_name' , 'delete => colum_famliy'
--(删除指定的colum_famliy)
truncate 'table_name'
--(删除表中的所有数据,只留下表结构)
disable 'table_name'
--(删除表之前,要先用disable把表的权限取消,然后才能删除表,disable之后,表就无法访问了)
drop 'table_name'
--(删除表,同上)
Hbase Phoenix:
语句:
!tables
--(显示所有表)
create table table_name (id int primary key,name var(225));
--(创建表)
!colums table_name
--(显示表中列)
upsert into table_name values();
--(更新插入数据)
select * from table_name;
--(查询表中数据)
delete form table_name where id = 5;
--(删除表中id为5的数据行)
select a.id,b,name,b,id from table_A a left join table_B b on a.id = b.id;
--(左连接以table_A为主)
在ptoenix中创建的表在hbase里面也存在
Sqoop:
Sqoop是一种用于在Hadoop和关系数据库或大型机之间传输数据的工具
--(用作数据迁移)
从数据库导入数据到HDFS
将数据从HDFS导出到数据库
Sqoop使用MapReduce导入和导出数据,提供并行操作和容错功能
sqoop import
--connect jdbc:mysql://localhost/hr
--query “select * from user where host != ‘127.0.0.1 and \$CONDITIONS”
--(所有查询都应该以\$CONDITIONS结束,sqoop内部使用\$CONDITIONS将记录范围分发给所有map)
--driver com.mysql.jdbc.Driver
--table user
--columns “host,name,age” (指定要迁移的col)
--where “order_date > ‘2015-10-10’” (指定要迁移的条件)
--username root
--password 12345
--split-by host(根据主机分割工作单元,尽可能平均的分割)
--delete-target-dir (如果目标路径存在,那数据迁移就会失败,加入了delete之后,如果指定路径存在就会删了它)
--target-dir /data/user
--m 3
--as-avrodatafile(目前最流行的数据文件格式)、
Scala:
变量的定义:
var:可替换value的数据类型
val:不可替换
如果函数有返回值,写在最后一行
在变量的声明中_(下划线)代替默认值
--(只能用var)
如:
scala> var name : String = _
name: String = null
scala> var id : Int = _
id: Int = 0
scala> var bl : Boolean = _
bl: Boolean = false
数据类型:
Any :
AnyVal : 7种数据类型 ,Boolean ,Unit
AnyRef
条件表达式:
if ..else :
val i = 8
val r = if (i > 8) i
--(没有else的话,默认为Unit = ())
val r1 : Any if Any else Any
for :
for (i <- 表达式/集合/数组; if(条件表达式))
for(i <- 0 to 3; j <- 0 to 3; if i!=j)
yield:产生一个新的集合
to :
例:
0 to 3
--(返回一个0到3的范围区间,左右都包含闭界值,也就是(1,2,3))
until:
例:
0 until 3
--(返回一个0到2的范围区间,左闭右开区间,包含左边界的值,不包括右边界的值,适合用作index(下标索引))
方法的定义,关键字def:
def name(参数名称: 参数类型 )= 方法的返回值 = 方法体
--(如果方法体内的code比较多,那就用{})
函数的定义:
函数的参数最多可以有22个
方式一:
(参数列表) => 函数体
val add = (x: Int, y: Int) => x * y
方式二:
(参数类型列表) => 返回值类型 =(参数名字)=> 函数体
val add:(Int,Int) => Int =(x,y) => x + y
val addf: String => Unit = age => println(age)
def add(f:(Int ,Int) => Int,a: Int, b: Int) = {
f(a,b)
}
传名调用/传值调用:
val f=(a:Int, b: Int) => a * b
add(f,2 + 4,3){
f(10,6 )
}
可变参数:
在参数类型后面加上一个通配符*
可变参数一般放在参数列表的末尾,不然后面的参数永远得不到赋值
如果不确定参数类型,可以用Any*(任意类型)
参数的默认值:
定义参数时可以给定一个默认值
调用时如果不传递参数,就使用默认值 println(add()) //24
如果传递了参数值,就使用传递的参数值 println(add(11,12)) //23
替换第一个值,只写一个值,默认为第一个参数的替换值 println(add(8)) //22
替换第二个值,可以指定参数名字 println(add(b = 11)) //21
高阶函数:
将其他函数作为参数,或结果为函数的函数,统称高阶函数
部分参数应用函数:
如果函数传递所有预期的参数,则表示已完全应用它。 如果只传递几个参数并不是全部参 数,那么将返回部分应用的函数。这样就可以方便地绑定一些参数,其余的参数可稍后填写补上
还可以用占位符 _:参数类型
柯里化:
柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新 的函数返回一个以原有第二个参数为参数的函数
偏函数:
def func : PartialFunction[String,Int] = {
case "one" => 1
case "two" => 2
case _ => 0
}
Array:
内容都可变
长度可变数组(ArrayBuffer)和长度不可变数组Array
集合:
可变集合:长度可变,内容可变
不可变集合:长度不可变,内容也不可变
import scala.collection.mutable._
import scala.collection.immutable._
--(不导包的情况下默认是immutable(不可变))
list:
head:list.head:取第一个元素
tail:list.tail:取第一个往下的所有元素
Nil: val lt = Nil //定义一个list值为空
90 :: lt //定义一个新的list,原list不变
count:把符合筛选条件的参数的count返回 例:list.count(_>1)
--(_>1也就是x =>x >1)
filter:把符合筛选条件的参数作为一个集合返回 例:list.filter(_>1)
--(_>1也就是x =>x >1)
sorted:把目标list中的parameter降序排序
--list.sorted
sortBy:按照目标list进行排序,传递x默认是降序,x => -x是升序
sortWith: 把目标list中的parameter按照表达式进行排序 val wds = List(("a",4),("b",1))
--wds.sortWith((x,y) => x._2 > y._2)
groupBy:按照key进行分组
grouped: 把目标list中的parameter按照size,分组 val list = List(3,5,1)
--scala> wds.grouped(2).toList
res1: List[List[Int]] = List(List(3, 5), List(1))
fold(叠加):有两个parm,分别与list中的parm相加,在把相加的结果求和
--scala> list.fold(8)(_+_)
res5: Int = 17
scala> list
res6: List[Int] = List(3, 5, 1)
foldLeft:加法同上,减法的话,((0-3)-5)-1
foldRigth:加法同上,减法的话,((3-0)-5)-1
reduce:把list中所有的parm都聚合到一起
size:返回list的长度
--scala> list.size
res22: Int = 10
union:把list和目标list中的parm合并到一起,(重复的数据)
--list.union(list2)
intersect(交集):把两个list重复的parm返回
--list.intersect(list2)
indices:返回list的index范围
--scala> list.indices
res2: scala.collection.immutable.Range = Range 0 until 10
diff(差集):按照第一个list,返回第二个list的不同的parm
--list.diff(list2)
zip(拉链):把两个list的index相同的组合到一起
--list.zip(list2)
如:list:1.34.6
list2:3,52,6
返回的zip结果:(1,3),(34,52),(6,6)
mkString:把list进行格式化输出,把list转换成字符串,parm之间用|隔开
--list.mkString("|")
nonEmpty:判断list是否为空,有值为true,空值为false
--scala> list.nonEmpty
res4: Boolean = true
slice: list2.slice(1,3)//取前两个parm
--list.slice(0,list.length)
sum:聚合求和,只有数值类型才能求和,String是会报错的(还是没忍住去骚气的试了一下QAQ)
--list.sum
distinct:像sql语句里一样,去重(去掉重复的数据)
--list.apply(1,1,2,2,3,3,4,4,5,5,).distinct
res1: List[Int] = List(1, 2, 3, 4, 5)
par:
转化为并行化集合
--list.par
问题:怎么把zip的结果相加
val r1 = list.zip(list2)
r1.map(x => x._1 + x._2)
求list中tail中每个元素乘10的结果
list.slice(1,list.length).map(_ * 10)
list.tail.map( _ * 10)
Set: 不可重复,无序,没有顺序
HashSet:
无序,不可重复
hset.add(insert parm)
--add parm
remove: 删除hashset中指定的parm
--hser.remove(parm)
hset.-=(指定一个parm)
--删除parm
hset ++ Set(parm)
--返回一个增加parm的hset
hset ++= Ser(parm)
--把parm赋值给hset(因为有=,所以赋值)
Map:
创建:val mapName = Map[String,Int]("a" -> 1)
--默认是不可改变的
创建一个可变的:val mapName = collection.mutable.HashMap[String,Int]()
--定义一个空的可变的map
添加可变map的几种方式(QAQ):
map.put(k,v)
--add k,v(String,Int)
map += "name" -> 100
--另一种骚气的添加kv的方式
map += (("xx",98))
删除可变map的几种方式(QAQ)
map.remove(k)
--指定key删除parm
map.-=(k)
--(String)
map.get(k).get
--得到key的values,第一次get会返回一个some对象,再次get得到values
map.getOrElse(k,initvalue)
--如果key存在,返回对应的values,如果没有返回定义的initvalue
元组(tuple):
最多22个parm
创建:
val tuple = (1,true,"xx",Unit)
--可以骚气的存放任意类型的数据
tuple._3
--tuple取值没有get方法,可以通过_index取值,要取第几个,index就是几
tuple.productIterator.foreach
--因为tuple的数据类型是Any,所以要遍历tuple就要用Iterator(迭代器),Iterator是Any,还可以toList
swap(对偶元组):
tuple.swap
--只有对偶元组(只有两个parm)才能用swap进行位置调换
面向对象:
在scala中object是一个单例对象,不能new
object中的方法,成员变量都是static的
调用的话可以,对象名.要调用的方法名/变量名
在scala中类用class修饰
类默认有一个空构造器
定义在class name 后面的构造器(constructor)为主构造器,也可以自定义构造器为辅助构造器
--在主构造器的parm默认会被定义为成员变量,所以类中不能有相同的parm name
--def this(parm)
如果主构造器中的parm没有被val,var修饰,那parm就不能被访问,相当于没有对外提供get,set方法
如果主构造器的parm使用了val,var相当于对外提供了get方法
在辅助构造器中必须先调用主构造器(每个都要调用主con的parm)
--额...主con下有(var name: String, val age: Int)多parm
那就必须调用this(name,age)
con的访问权限:
主con变为private:
在class后,parm之前加入private修饰符后,主con变为私有的,外部类不能访问
辅助con也可以是private的
--class classname private ()
class的成员变量访问权限:
主con的parm也可以被private修饰,修饰之后set,get方法都不可访问
--class classname (private parm)
class的访问权限:
class也可以被private修饰,可以被外部类new出来,但执行就执行不了 当前的包下都可以访问
加入了[this]关键字都,只在当前所在包下可用,子包和父包都不可用
[]中还可以指定包名,表示这个类在指定包和指定包下的子包都可用
--private class classname()
伴生对象:
在 Scala 中, 当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象。必须 在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类。类和它的伴 生对象可以互相访问其私有成员。
bs可以无视类的private,可以访问get,set
并行化集合:
par:
转化为并行化集合
--list.par
特质(trait):
在scala中特质可以定义有实现的方法,也可以定义没有实现的方法
如果trait中方法没有实现,重写的时候可以不加override
如果实现了,就必须加override
trait可以多重继承
抽象(abstract):
无论是抽象还是特质,必须先写extends,然后在能写with
特质可以用他俩修饰
抽象只能用with
final:
用final修饰:
类:不能被继承
方法:不能被重写
变量:不能被修改,赋值
样例类:
类名的定义必须要驼峰命名,第一个小写,第二个大写
--例:tableName
支持模式匹配,默认实现了Serializable接口
--case calss calssname (parm)
样例对象:
不能封装数据
--case object objectname(parm)
问题:
如果两个要继承的trait都有一个相同的method(方法)
编译器会骚气的默认从右往左编译
模式匹配:
def name (parm) x match {}
如果匹配到了相同的case,取第一个case的值 就不继续向下匹配
case "1" => println("1")
--匹配字符串
case x = Int => println(s"Int $x")
--匹配数据类型
case Array(1,_*) => println()
--1为开头,后面随便类型, 随便数值
--匹配Array数组
case sum::sum::Nil => println(包含两个sum的list)
case parm::parm::parm::Nil => println(只有三个元素的list)
case parm::parm if parm.length > 0 => print(拥有head,tail的list)
--匹配list 集合
case (parm,parm,parm) => parintln(三个任意类型,任意数值的tuple)
-- 匹配tuple元组
case objectname(parm) => println(System.currentTimeMillis())
--匹配对象
--只有case object才能模式匹配
scala中的变态符号:
<-:用于遍历集合对象,也可以叫做生成器(generator)
例如:val b = (1 to 100 by 2)
for ( a <- b )
println(a)
把b的值直接扔给a,这里的a是凭空生成的对象,是val不是var
不需要指定a的类型,Scala会自动推导b的类型然后得到a的类型
:: :向队列的头部添加数据,创建一个新的列表
--list ::List(parameter)(这样添加的是list)
--parameter :: List (这样是在list头部添加元素)
++ :连接两个list,在尾部添加list,创建一个新的list: list ++ List(parameter)
+: :和::很相似,都是在头部追加元素,list.+:(parameter)
--(冒号在后面,parameter就在后面)
:+ :在尾部追加元素 list :+(parameter)
--(冒号在前面,parameter就在前面)
::: :拼接两个list 创建一个新的list list ::: List(parameter)
--(该方法只能用于连接两个List类型的集合)
+:方法用于在头部追加元素,和::很类似,但是::可以用于pattern match ,而+:则不行。
WorkCount:测试数据("hello Tom hello jerry hello Marry","hello Tom hello jerry hello Marry")
scala> arr.flatMap(_.split(" "))
res26: Array[String] = Array(hello, Tom, hello, jerry, hello, Marry, hello, Tom, hello, jerry, hello, Marry)
--(把目标Array以空格切分为单词,扁平化放进一个Array,放进map)
scala> arr.flatMap(_.split(" ")).groupBy(x => x)
res25: scala.collection.immutable.Map[String,Array[String]] = Map(Marry -> Array(Marry, Marry), Tom -> Array(Tom, Tom), jerry -> Array(jerry, jerry), hello -> Array(hello, hello, hello, hello, hello, hello))
--(groupBy将相同的values划分一个key)
scala> arr.flatMap(_.split(" ")).groupBy(x => x).mapValues(_.length)
res28: scala.collection.immutable.Map[String,Int] = Map(Marry -> 2, Tom -> 2, jerry -> 2, hello -> 6)
--(最后获取values的length(长度)完成workcount)
如果要排序的话,就先toList转换为list,然后sortBy进行排序
scala> arr.flatMap(_.split(" ")).groupBy(x => x).mapValues(_.length).toList.sortBy(x => - x._2)
res32: List[(String, Int)] = List((hello,6), (Marry,2), (Tom,2), (jerry,2))
--(升序 从大到小 把大的排在前面)
scala> arr.flatMap(_.split(" ")).groupBy(x => x).mapValues(_.length).toList.sortBy(_._2)
res32: List[(String, Int)] = List((hello,6), (Marry,2), (Tom,2), (jerry,2))
--(降序 从小到大 把小的排在前面)
spark:
问题:Worker怎么知道Master在哪里嗯?
读取spark-env.sh文件得知Master在哪里的
通过web页面访问spark管理页面(master所在机器的地址+8080端口)
配置了高可用的spark集群,修改了一个配置文件:
配置了Worker运行时的资源(内存、cores)
启动集群
1.启动ZK集群
2.启动spark集群,但是只会启动一个Master,另外一个Master手动启动
修改spark-env.sh:
export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER -Dspark.deploy.zookeeper.url=Master:2181,Worker1:2181,Worker2:2181 -Dspark.deploy.zookeeper.dir=/spark" #通过Zookeeper管理集群状态
export SPARK_WORKER_CORES
--内核数量
export SPARK_WORKER_MEMORY=5G
--分配给worker的内存大小
提交第一个spark应用到集群中运行
bin/spark-submit --master spark://node-5:7077 --class org.apache.spark.examples.SparkPi examples/jars/spark-examples_2.11-2.2.0.jar 100
--executor-memory 每个executor使用的内存大小
--total-executor-cores 整个app使用的核数
提交一个spark程序到spark集群,会产生哪些进程?
SparkSubmint(Driver)提交任务
Executor 执行真正的计算任务的
提交任务可以指定多个master地址,目的是为了提交任务高可用
bin/spark-submit --master spark://node-4:7077,node-5:7077 --class org.apache.spark.examples.SparkPi --executor-memory 2048mb --total-executor-cores 12 examples/jars/spark-examples_2.11-2.2.0.jar 1000
--主节点的master挂了之后,会使用节点上的备用master,这就是高可用
用spark Shell完成WordCount计算
启动HDFS(上传数据到hdfs),sc是spark core(RDD)的执行入口
sc.textFile("hdfs://node-4:9000/wc").flatMap(_.split(" ")).map((_, 1)).reduceByKey(_+_).sortBy(_._2, false).collect
----------------------------
Yarn和Spark的StandAlone调度模式对比
ResouceManager Master 管理子节点、资源调度、接收任务请求
NodeManger Worker 管理当前节点,并管理子进程
YarnChild Executor 运行真正的计算逻辑的(Task)
Client SparkSubmit (Client + ApplicaitonMaster)提交app,管理该任务的Executor
ApplicaitonMaster 并将Task提交到(Executor)
----------------------------
spark-shell可以指定一些选项(shell运行的机器,内存,核数)提高效率,充分发挥计算资源
--master spark://node-4:7077,node-5:7077
--executor-memory 5g
--total-executor-cores 12
RDD:
1.RDD是一个基本的抽象,操作RDD就像操作一个本地集合一样简单,降低的编程的复杂度
RDD的算子分为两类,一类是Transformation(lazy),一类是Action(触发任务执行)
RDD不存储真正要计算的数据,而是存储RDD的转换关系
创建RDD的方式:
1.通过外部的存储系统创建RDD(比如HDFS里面的目录)
2.将Driver的Scala集合通过并行化的方式编程RDD
--应用场景:(学习,实验,测试)
例:
scala> val arr = Array(1,2,3,4,5,6,7,8,9,10)
arr: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> val r1 = sc.parallelize(arr)
r1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[6] at parallelize at
因为scala的集合存在空间有限,不适合大数据开发的需求
3.调用一个已经存在的RDD的Transformation,会生成一个新的RDD
RDD的Transformation的特点
1.lazy
2.生成新的RDD
Rdd的map方法,真正是在Executor中执行时,一条一条的将数据拿出来处理
RDD的算子:
mapPartitionWithIndex:
一次拿出来一个分区,有多条数据,分区中并没有数据,要读取数据,就要生成Task读取多条数据,并可以将分区的编号取出来
功能:
取分区中对应的数据时,还可以将分区的编号取出来,这样就可以知道数据是属于哪个分区(对应的Task)
一个分区对应一个Task:
分区中没有数据,真正处理、读取数据的是Task,也可以认为就是对应分区中的数据
定义一ge把分区中的数据取出,并带有分区ID的func:
val func = (index: int, it: Iterator[Int]) => {
it.map(x => s"[partId:"+ $index + ",parm:"+ $x +"]")
}
scala> rdd1.mapPartitionsWithIndex(func).collect
res3: Array[String] = Array([partId:0,parm:1], [partId:0,parm:2], [partId:0,parm:3], [partId:0,parm:4], [partId:1,parm:5], [partId:1,parm:6], [partId:1,parm:7], [partId:1,parm:8], [partId:1,parm:9])
同上,是String类型的
val func2 = (index: Int, it: Iterator[String]) => {
it.map(x => "[partId:"+ index + ",parm:"+ x +"]")
}
aggregate;
第一个parm是全局运算,后面是局部运算,表达式
--按照分区
scala> rdd1.aggregate(2)(_+_,_+_)
res1: Int = 51、
执行完成后,会立即返回结果
affregateByKey:
先在每一个分区里面按照key进行分组,把相同的key分到一起
然后局部聚合
生成新的RDD
aggregateByKey和reduceByKey一样,就是多了个初始值
countByKey:
统计key出现的次数也就是相同key的数量,和values无关
filterByRange:
筛选出符合添加的值,包含边界
flatMapValues:
对values进行flatMap
foldByKey:
有初始值,对rdd进行表达式聚合
foreach和foreachPartition的区别:
foeach是一条一条数据输出,
foreach是一个分区一个分区进行输出
它们的适用场景:
比如有大量的数据要写入mysql,如果用foreach,就会插入一条数据,创建一条JDBC连接,就很傻屌,
用foreachPartition就几条就够了,很高效
CombineByKey:
关于spark的workcount:
reduceByKey和grooupBykey的区别和适用场景:
当数据量大的时候,,reduceByKey更适合,为什么?
它可以先局部聚合,在全局聚合,
而groupBykey呢?它傻乎乎就会先分组,然后在聚合:
如果不先聚合就会产生大量的shuffle,在网络方面就会消耗更多的资源,代价
只有list中有kv形式的数据才能使用:
reduceByKey,,affregateByKey,,ConbineByKey
sc.parallelize()
--sc是spark context的简写,
--parallelize:并行化处理数据,第一个parm可以写数据,第二个parm是分区数量
combineByKey:
第一个parm:
将分好组的key的第一个values取出来进行操作
第二个parm:
在每一个分区中将value中的其他元素加入进来
//定义一个partitioner,指定分区数量
val hp : Partitioner = new HashPartitioner(2)
val rddName = TargetRdd(目标rdd).combineBykey(x => ListBuffer(x),(m: ListBuffer[String], n:String) => n += n, (a:ListBuffer[String],b: ListBuffer[String]) => a ++= b,hp,true,null)
cache:
它不会生成一个新的RDD,不会action ,它只会标记这个RDD被cache
cache是一个方法,作用是把RDD的结果集存放到Executor中也就是放在内存里面(将数据缓存到内存里面),再次操作RDD会有毫秒级反应,
缺点:测试数据是大小为360M左右的日志文件,有100万行,存放到内存中的占用大小为3倍左右,1100M左右
适用场景:当然不适合只使用一次的Rdd,适合需要反复使用的RDD
什么时候适合使用cache(适用场景):
1.要求计算速度快
2.集群的资源够大
3.重点:cache的数据会多次的触发Action
4.先把需要的数据过滤出来,然后缓存到内存,进行操作:
例:
var lines = sc.textFile("hdfs://master:9000/access")
--定义hdfs的文件路径
val filtered = lines.filter(_.contains("bigdata"))
--contains(包含)过滤出lines中包含bigdata中的数据
val cached = filtered.cache()
--然后把过滤后的数据缓存到内存
cached.count
--因为lazy所以用count action
unpersist:
用于释放掉cache到内存的资源,
RDDName.unpersist(true)
这里有一个参数,如果为true,那就等资源释放完成,在执行以下代码
如果为false,那就边释放资源,边执行以下代码
checkpoint:
迭代计算:要求保证数据安全
对速度要求不高(cache)
将中间结果保存到hdfs中
sc.setCheckpointDir(hdfs:path/folder)
lines.filter(_.contains("javaee"))
filtered.checkpoint
--此时执行了吗,当然没有,只是标记了filtered要做checkpoint(检查点)
如果在下面count一下,然后才会执行
filtered.count
--count之后filter计算的数据就被保存到hdfs指定的path下了
下次再count或者其他操作的时候,就可以直接用hdfs计算好的数据
根据访问日志的ip地址计算出访问者的归属地,并按照省份,计算出访问次数,然后将计算好的结果写入到MySql
1.整理数据,切分出ip字段,然后将ip地址转换成十进制
2.加载规则,整理规则,取出有用的字段,然后将数据缓存到内存中(Executor中的内存中)
3.将访问log与ip规则进行匹配(二分法查找)
4.取出对应的省份名称,将其和一组合在一起
5.按省份名称,进行聚合
6.将聚合后的数据写入到MySql中
见(TextIp.scala)
Transformation:
aggregateByKey
reduceByKey
filter
flatMap
map
mapPatition
mapPartitionWithIndex
Action:
collect
aggregate
saveAsTextFile
foreach
foreachPartition
spark执行流程:
1.构建DAG(调用RDD上的方法)
--DAG(有向无环图):实际上就是描述了RDD的转换过程,也就是以后对数据如何进行操作
2.DAGScheduler依据shuffle将DAG切分成stage,将stage中生成的Task以Ta skSet的形式给TaskScheduler
3.TaskScheduler调度Task,(根据资源情况分配Task调度到相应的Executor)
4.Executor接收Task,然后放到线程池中执行
DAG:
有向无环图(有方向,无闭环,也就是不循环)
DAG描述多个RDD的转换过程,任务执行时,可以按照DAG的描述,执行真正的计算
DAG是有边界的:
开始(通过SparkContext创建RDD),
结束(触发Action,调用run Job一个完整的DAG就形成了,一旦触发Action成了一个完整的DAG)
一个Spark的Application中有多少个DAG?
一到多个,触发一个Action就有一个DAG(取决于触发了多少Action)
一个DAG中可能产生多中不同类型和功能的Task,会有不同的阶段
一个RDD只是描述了数据计算过程中的一个环节,而DAG就由一到多个RDD组成,描述了数据计算过程的所有环节、过程
为什么要切分stage?
一个复杂的业务逻辑(多台机器上具有相同属性数据聚合到一台机器上:shuffle),如果有shuffle,那么久意味着前面阶段产生的结果后,才能执行下一个阶段,下一个阶段的计算要依赖的计算要依赖上一个阶段的数据,在同一个Stage中,多有算子,可以合并在一起,我们称它为pipeline(流水线:严格按照流程、顺序执行)
shuffle的定义:
洗牌:
父RDD一个分区中的数据如果给了子RDD的多个分区(只有存在这种可能 ),就是shuffle
shuffle会有网络传输数据,但是有网络传输,并不意味着就是shuffle
宽依赖:
父RDD一个分区中的数据如果给了子RDD的多个分区,那么就是宽依赖(即使存在可能)
Spark Sql:
SparkSql是Spark上的高级模块,SparkSql是一个SQL解析引擎,将Sql解析成特殊的RDD(DataFrame),然后在Spark集群中运行,
SparkSql是用来处理结构化数据的(先将非机构化数据转换成结构化数据)
SparkSql支持两种编程API:
1.Sql方式
2.DataFrame的方式(DSL)
SparkSql兼容hive(元数据库、Sql语法、UDF、序列化、反序列化机制)
SparkSql支持统一的数据源,课程读取多种类型的数据
SparkSql提供了标准的连接(JDBC、ODBC),以后可以对接一下BI工具