函数
Hive的内置函数
数学函数
取整函数:round、floor、ceil、fix
fix朝零方向取整,如fix(-1.3)=-1; fix(1.3)=1;
floor:地板数,所以是取比它小的整数,即朝负无穷方向取整,如floor(-1.3)=-2; floor(1.3)=1; floor(-1.8)=-2; floor(1.8)=1。
ceil:天花板数,也就是取比它大的最小整数,即朝正无穷方向取整,如ceil(-1.3)=-1; ceil(1.3)=2; ceil(-1.8)=-1; ceil(1.8)=2。
round四舍五入到最近的整数,
round(number,digits)
参数
number,要四舍五入的数,digits是要小数点后保留的位数
如果 digits 大于 0,则四舍五入到指定的小数位。
如果 digits 等于 0,则四舍五入到最接近的整数。
如果 digits 小于 0,则在小数点左侧进行四舍五入。
如果round函数只有参数number,等同于digits 等于 0。 返回值 四舍五入后的值
取随机数函数: rand
语法: rand(),rand(int seed)
返回值: double
说明:返回一个0到1范围内的随机数。如果指定种子seed,则会返回固定的随机数
幂运算函数 pow
语法: pow(double a, double p)
返回值: double
说明:返回a的p次幂
举例:
hive> select pow(2,4) ;
16.0
绝对值函数 abs
语法:abs(double a) abs(int a)
返回值 double int
说明:返回值数值a的绝对值
举例:
hive> select abs(-3.9)
3.9
hive> select abs(10.9)
10.9
字符串函数
字符串长度函数:length
语法:length(string A)
返回值:int
说明:返回值字符串A的长度
hive> select length('abcdef');
6
字符串反转函数:reverse
语法:reverse(string A)
返回值:string
说明:返回字符串A的反转结果
hive> select reverse('asdfg')
gfdsa
字符串连接函数:concat、带分隔符 concat_ws
concat(string A, string B…)
hive> select concat(‘abc’,'def’,'gh’);
abcdefgh
说明:返回输入字符串连接后的结果,支持任意个输入字符串
concat_ws(string SEP, string A, string B…)
hive> select concat_ws(',','abc','def','gh');
abc,def,gh
说明:返回输入字符串连接后的结果,SEP表示各个字符串间的分隔符
字符串截取函数:substr,substring
语法: substr(string A, int start),substring(string A, int start)
返回值: string
说明:返回字符串A从start位置到结尾的字符串
举例:
hive> select substr('abcde',3);
cde
hive> select substring('abcde',3);
cde
hive>select substr('abcde',-1);
e
语法: substr(string A, int start, int len),substring(string A, intstart, int len)
返回值: string
说明:返回字符串A从start位置开始,长度为len的字符串
hive> select substr('abcde',3,2);
cd
hive> select substring('abcde',3,2);
cd
hive>select substring('abcde',-2,2);
de
正则表达式替换函数:regexp_replace
语法: regexp_replace(string A, string B, string C)
返回值: string
说明:将字符串A中的符合java正则表达式B的部分替换为C。注意,在有些情况下要使用转义字符,类似oracle中的regexp_replace函数。
举例:
hive> select regexp_replace('foobar', 'oo|ar', '');
fb
分割字符串函数: split
语法: split(string str, string pat)
返回值: array
说明:按照pat字符串分割str,会返回分割后的字符串数组
举例:
hive> select split('abtcdtef','t');
["ab","cd","ef"]
日期函数
获取当前UNIX时间戳函数: unix_timestamp
语法: unix_timestamp()
返回值: bigint
说明:获得当前时区的UNIX时间戳
举例:
hive> select unix_timestamp();
1323309615
UNIX时间戳转日期函数: from_unixtime
语法: from_unixtime(bigint unixtime, [string format])
返回值: string
说明:转化UNIX时间戳(从1970-01-01 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式
举例:
hive> select from_unixtime(1323308943,'yyyyMMdd');
20111208
日期转UNIX时间戳函数:unix_timestamp
语法: unix_timestamp(string date)
返回值: bigint
说明:转换格式为"yyyy-MM-ddHH:mm:ss"的日期到UNIX时间戳。如果转化失败,则返回0。
举例:
hive> select unix_timestamp('2011-12-07 13:01:03');
1323234063
日期时间转日期函数:to_date
语法: to_date(string timestamp)
返回值: string
说明:返回日期时间字段中的日期部分。
举例:
hive> select to_date('2011-12-08 10:03:01');
2011-12-08
日期转年函数: year、转月: month、转日:day、转周:weekofyear
语法: year(string date) month (string date)
day (string date) weekofyear (string date)
返回值: int
说明:返回日期中的年、月、日。
举例:
hive> select year('2011-12-08 10:03:01');
2011
hive> select month('2012-12-08');
12
hive> select day('2012-12-08');
8
hive> select weekofyear('2011-12-08 10:03:01');
49
日期比较函数:datediff
语法:datediff(string enddate,string startdate)
返回值:int
说明:返回结束日期减去开始日期的天数
举例:
hive> select datediff('2012-12-08','2012-05-09');
213
日期增加函数: date_add
语法: date_add(string startdate, int days)
返回值: string
说明:返回开始日期startdate增加days天后的日期。
hive> select date_add('2012-12-08',10);
2012-12-18
日期减少函数: date_sub
语法: date_sub (string startdate, int d ays)
返回值: string
说明:返回开始日期startdate减少days天后的日期。
举例:
hive> select date_sub('2012-12-08',10);
2012-11-28
条件函数
if函数
语法:if(boolean condition,a,b)
说明:当条件condition为ture时,返回a,否则返回b
hive> select if(1=2,100,200) ;
200
hive> select if(1=1,100,200) ;
100
条件判断函数:case
语法:
(1) CASE a WHEN b THEN c [WHEN d THEN e]* [ELSE f] END
说明:如果a等于b,那么返回c;如果a等于d,那么返回e;否则返回f
举例:
select case 100 when 50 then 'tom' when 100 then 'mary'else 'tim' end ;
mary
select case 200 when 50 then 'tom' when 100 then 'mary'else 'tim' end ;
tim
(2) CASE WHEN a THEN b [WHEN c THEN d]* [ELSE e] END
说明:如果a为TRUE,则返回b;如果c为TRUE,则返回d;否则返回e
select case when 1=2 then 'tom' when 2=2 then 'mary' else'tim' end ;
mary
select case when 1=1 then 'tom' when 2=2 then 'mary' else'tim' end ;
tom
转换函数 : cast
cast(表达式 as 数据类型)
cast函数,可以将"20190607"这样类型的时间数据转化成int类型数据。
cast("20190607" as int)
select cast('2017-06-12' as date) filed;
hive的行转列
行转列是指多行数据转换为一个列的字段
hive行转列用到的函数:
concat(str1,str2) 字段或字符串拼接
concat(sep,str1,str2) 以分隔符拼接每个字符串
collect_set(col) 将某字段的值进行去重汇总,产生array类型字段
hive的表生成函数
explode(col):将hive列中复杂的array或者map结构拆分成多行
explode(array):列表中的每个元素生成一行
explode(MAP):map中每个key—value对,生成一行,key为一列,value为一列
开窗函数:row_number(), rank(), dense_rank()
row_nubmer():(顺序排序),这个排序函数的特点是相同数据,先查出的排名在前,没有重复值。像我们这里呢sal相同,先查出来的数据的rank排名优先。 1 2 3 4
rank()函数:(跳跃排序),相同数据(这里为sal列相同)排名相同,比如并列第1,则两行数据(这里为rank列)都标为1,下一位将是第3名.中间的2被直接跳过了。排名存在重复值。1 2 2 4
dense_rank():(连续排序),比如两条并列第1,则两行数据(这里为rank列)都标为1,下一个排名将是第2名。 1 2 2 3
分析函数
分析函数:sum,avg,min,max
求当前行和之前之后的行进行累加 求和、求平均值、求最小值、求最大值的操作
特殊的关键词:
- preceding:往前
- following:往后
- current row:当前行
- unbounded:起点
- unbounded preceding:表示从前面的起点
- unbounded following:表示到后面的终点
格式:
over(partiion by cookiedid order by pv desc rows between rows between unbounded preceding and current row )
lag、lead、first_value、last_value
Lag和Lead分析函数可以在同一次查询中取出同一字段的前N行的数据(Lag)和后N行的数据(Lead)作为独立的列。
lag(param1, param2, param3)取前N行
取前N行
param1:表中列名
param2:前N行
param3:超出行数时默认设置值, 没设置就是null
lead():取后N行
【first_value】
取分组内排序后,截止到当前行,第一个值
SELECT cookieid,
createtime,
url,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
FIRST_VALUE(url) OVER(PARTITION BY cookieid ORDER BY createtime) AS first1
FROM itcast_t4;
【last_value】
取分组内排序后,截止到当前行,最后一个值
SELECT cookieid,
createtime,
url,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
LAST_VALUE(url) OVER(PARTITION BY cookieid ORDER BY createtime) AS last1
FROM itcast_t4;
其他函数
count(1)、count(*)与count(列名)的执行区别
执行效果上:
count(*)包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULL
count(1)包括了忽略所有列,用1代表代码行,在统计结果的时候,不会忽略列值为NULL
count(列名)只包括列名那一列,在统计结果的时候,会忽略列值为空(这里的空不是只空字符串或者0,而是表示null)的计数,即某个字段值为NULL时,不统计。
执行效率上:
列名为主键,count(列名)会比count(1)快
列名不为主键,count(1)会比count(列名)快
如果表多个列并且没有主键,则 count(1) 的执行效率优于 count(*)
如果有主键,则 select count(主键)的执行效率是最优的
如果表只有一个字段,则 select count(*)最优。
date_add() 、date_sub()
date_add()常常用户在mysql的sql中实现对日期类型的操作,比如增加或者减少,但是不改变原来的数据,只是对查询的数据做处理,这里展示使用示例:
date_add(date,interval expr unit)
说明:date表示时间字段或者时间类型,interval是固定标志,expr 表示数量,可以为正负,表示加减,unit表示日期类型 可以是yy,ww,dd等分别表示,年,周,天等,具体示例如下:
// 得到当前时间增加1个小时的结果
select date_add(now(),interval 1 hour)
// 得到当前时间增加1天的结果
select date_add(now(),interval 1 day)
// 得到当前时间减少72个小时的结果
select date_add(now(),interval -72 hour)
【date_sub()】
语法:date_sub(date,interval expr type),函数从日期【减去】指定的时间间隔
date_sub('2019-07-27', interval 30 day)表示往前推30天
CURDATE () 与 NOW () 函数
SELECT NOW(),CURDATE(),CURTIME(),
NOW() 函数返回当前的日期和时间。
CURDATE() 函数返回当前的日期。
CURTIME() 函数返回当前的时间。
datediff()与 timediff()函数
datediff(date1,date2):两个日期相减,得到相减(date1减date2)之后的“天数”
timediff(time1,time2):两个时间相减,得到相减(time1减time2)之后的时间“差值”
日期格式化:date_format() 函数
date_format(date, formate):用于以不同的格式显示日期/时间数据,date 参数是合法的日期,format 规定日期/时间的输出格式。
mysql中的substr()函数使用
常用的方式是:
SBUSTR(str,pos); str是要截取的字符串,从pos开始的位置,一直截取到最后。
还有一种比较常用的是:
SUBSTR(str,pos,len);从pos开始的位置,截取len个字符(空白也算字符)。
mysql中的substr()函数和hibernate的substr()参数都一样,就是含义有所不同。
【mysql中的start是从1开始的,而hibernate中的start是从0开始的。】
substring_index——按关键字截取字符串
substring_index(str,delim,count)
说明:substring_index(被截取字段,关键字,关键字出现的次数)
(注:如果关键字出现的次数是负数 如-2 则是从后倒数,到字符串结束)
示例:
如 原始字符串: aa.bb.cc.dd
SELECT substring_index(substring_index('aa.bb.cc.dd', '.', 1), '.', -1); 得到aa
SELECT substring_index(substring_index('aa.bb.cc.dd', '.', 2), '.', -1); 得到bb
SELECT substring_index(substring_index('aa.bb.cc.dd', '.', 3), '.', -1); 得到cc
SELECT substring_index(substring_index('aa.bb.cc.dd', '.', 4), '.', -1); 得到dd
用两个substring_index是因为substring_index('aa.bb.cc.dd', '.', 2)的结果为aa.bb,所以得再从后截取一次。
replace()替换函数
REPLACE(String,from_str,to_str)
即:将String中所有出现的from_str替换为to_str。
查询替换:
将address字段里的 “九” 替换为 “十” 显示,如下
select *,replace(address,'九','十') AS rep from test_tb
where id in (4,6)
更新替换:
将address字段里的 “东” 替换为 “西” ,如下
update test_tb set address=replace(address,'东','西')
where id = 2
nvl ()
nvl(str1,str2)
含义:如果第一个参数不为空的话,则该表达式返回第一个参数的值,若第一个参数为空时,则返回第二个参数的值。
nvl(str1,str2,str3)
含义:如果str1的值为空则返回str3,如果不为空则返回str2
SQL 语法
SQL语言分为四种语言
1.DDL:数据定义语言 创建数据库、创建表。
2.DML:数据操作语言 对数据的增删改查操作。
3.DCL:数据控制语言 grant revoke
4.DQL:数据查询语言 select ... from where group by having order by limit
DDL之 数据库操作
创建数据库
create database [if not exists] 数据库名称;
使用数据库
use 数据库名称;
删除数据库
drop database [if not exists] 数据库名称;
查看当前使用的数据库
select databases();
DDL之 数据表操作
创建表
create table [if not exists] 表名称 (
字段名 类型(长度) [约束],
字段名 类型(长度) [约束],
....
) ;
案例:
create table if not exists student(
sid varchar(20) p,
sname varchar(20)
);
克隆表
create table 表名称 like 表2;
查看表结构
desc 表名;
删除表
drop table 表名;
修改表结构
添加字段
alter table 表名称 add 字段 数据类型 (长度) [约束];
删除字段
alter table 表名称 drop 字段;
修改字段
alter table 表名称 change 旧字段 新字段 (长度) [约束];
修改表名
rename table 旧表名 to 新表名 ;
案例:
use abc;
create tbale if not exists student(
sid varchar(20) primary key,
sname varchar(20),
sage int,
salary double
);
alter table student add desc varchar(200);
alter table student change `desc` description varchar(200);
alter table student drop description
rename table student to t_student;
DML数据操作语言
数据插入操作 insert into
# 语法格式
INSERT INTO 表名(字段列表1,字段列表2...) VALUES(对应值1,对应值2...);
# 将所有的字段都赋值
INSERT INTO VALUES(对应值1,对应值2...);
第一种,表单没有指定要插入数据的列的名称,只提供要插入的值,即可添加一行新的数据:
INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value3, ...);
第二种,如果要为表中的所有列添加值,则不需要在SQL查询中指定列名称。但是,请确保值的顺序与表中的列顺序相同。INSERT INTO语法如下所示:
INSERT INTO table_name
VALUES (value1, value2, value3, ...);
使用另一个表填充一个表
通过另一个表上的SELECT语句查询出来的字段值,然后将数据填充到本表中,
条件是另一个表所查询的字段与本表要插入数据的字段是一一对应的。
INSERT INTO first_table_name [(column1, column2, ... columnN)]
SELECT column1, column2, ...columnN
FROM second_table_name
[WHERE condition];
注意:
1.字段列表中的类型、字段长度、约束和后续值一一对应。
2.如果不写字段列表,后续的值必须是所有字段的。
3.如果当前的值是数值类型,可以不用单引号,除了数值类型需要使用单引号。
4.如果当前赋值为空,不对当前为空字段赋值或者是赋值为null。
insert into category(cid,name) values('c001',"电器");
insert into category(cid) values('c005');
insert into category values('c009',"蔬菜");
insert into category(cid,name) values('c006',null);
数据更新操作
update 表名 set 字段1=字段1对应值, 字段2=字段2对应值,... where 条件;
案例:
update category set cname='家电'; #将所有行的cname改为'家电'
update category set cname='水果' where cid = 'c001'; #将cid为c001的cname修改为水果
数据删除 delete 或 truncate
# 语法格式
delete from 表名 where 条件;
# truncate
truncate table 表名;
案例:
delete from category where cid = '005'; #删除cid为005的记录
truncate table category; #清空表数据
delete 和 truncate的区别
1.delete 是DML操作,truncate是DDL操作;
2.delete 是按行来删除,truncate是表级删除;
3.delete 是记录binlog日志,可以恢复,效率慢,truncate不记录日志,效率高;
SQL约束
对数据表中的字段对应数据限定
主键约束:primary key
一个表只能有一个主键,当建表时忘记设置主键约束时,设置为主键的列查询速度会非常快,所以一般会用聚集索引。
#格式
create table if not exists t_student(
sid varchar(10) primary key
);
# 添加约束2
create table if not exists t_student(
sid varchar(10),
constraint c_sid_pk primary key(sid)
);
# 添加约束3
alter table t_student add primary key(sid);
添加主键约束:
alter table 表名 add primary key(列名); 可以有多个列名。
修改主键约束:
alter table 表名 modify 列名 列类型 primary key;
删除主键约束:
alter table 表名 drop primary key;
外键约束:foreign key
当建表时需要用到另一个表的主键作为本表的主键时,需要设置外键。设置外键后,若想再删除本表数据时会联级删除或者默认删除其他方式。
添加外键约束:
alter table 表名 add foreign key (列名) references 关联表名称 (列名称);
删除外键约束:
alter table 表名 drop foreign key 外键名称;
查询外键名:
show create table 表名;
非空约束:not null
当插入新数据时对应的列不能为空,非空约束是相对于默认值约束而说的。
# 格式
create table if not exists t_student(
sid varchar(10) primary key,
snmae varchar(20) not null
)
添加非空约束:
alter table 表名 modify 列名 列类型 not null;
修改非空约束:(就是改为空)
alter table 表名 modify 列名 列类型 null;
删除非空约束:(就是设置为默认值)
alter table 表名 modify 列名 列名类型 默认值;
如:alter table myself id int default 'abe';
默认值约束:default
当插入时没有插入值时,会自动插入默认值。默认值约束相对于非空约束而说。
添加默认值约束:
alter table 表名 add 列名 列类型 not null default '默认值';
唯一约束:unique key
本列的内容只能唯一不能重复
# 格式
create table if not exists t_student(
sid varchar(10) primary key,
sname varchar(20) not null,
address varchar(20) unique
)
添加唯一约束:
alter table 表名 add unique(列名称);
修改唯一约束:
alter table 表名 modify 列名 列类型 unique;
删除唯一约束:(删除age列的唯一约束)
alter table 表名 drop index 列名称;
SQL之DQL数据查询语言
DQL是为了将数据表中的数据根据 条件、分组、排序、分页、聚合筛选出来。
语法格式:
select [distinct] 字段列表
from 表名
where 条件
group by 分组字段
having (聚合函数的过滤条件)
order by 排序字段
limit M,N
条件查询
比较运算符:
>,<,<=,>=,【<>、!=】(不等于)
between ... and ... 显示在某一区间的值(含头含尾)
in(...) 显示在in列表中的值,例:in(100,200)
like "张%" ,like "%周%" 模糊查询,like语句中%代表零个或者多个任意字符,_代表一个字符,例如:first_name like '_a%'
is null 判断为空
is not null 判断不为空
分页查询 limit
limit m,n
SELECT 字段1,字段2... FROM 表名 LIMIT M,N
M: 整数,表示从第几条索引开始,计算方式 (当前页-1)*每页显示条数
N: 整数,表示查询多少条数据 offset 偏移量(每页显示多少条数据)
SELECT 字段1,字段2... FROM 表名 LIMIT 0,5
SELECT 字段1,字段2... FROM 表名 LIMIT 5,5
#查询product表的前5条记录
SELECT * FROM product LIMIT 0,5
等价于
SELECT * FROM product LIMIT 5
每一页显示 10条数据,请问第二页显示 10,10
select * from product limit 10,10;
# 请问第十一页显示
select * from product limit 100,10;
# 求查询出来的前3条数据
select * from product limit 3;
limit m offset n
# 跳过 n 行数据,取 m 行数据
-- 查询 10-20 条数据
select * from table_name limit 10 offset 10;
连接查询
内连接:
//特点: 只显示两张表的交集,其他部分不显示
select * from A [inner] join B on 条件; //显示内连接.
select * from A,B where 条件; //隐示内连接.
左连接:
//特点: 左表的全集 + 两张表的交集, 无交集部分用 null left out join
select * from A left join B on 条件;
右连接:
//特点: 右表的全集 + 两张表的交集, 无交集部分用 null right out join
select * from A right join B on 条件;
满连接:
//特点: 左表全集 + 右表全集, 无交集部分用 null full join
select * from A full join B on 条件;
多表的连接查询: //查询(连接)条件的数量 = 数据表的个数 - 1, 即: 3张表联查, 连接条件至少有2个.
select * from A,B,C where A.id=B.id and B.id=C.id;
union和union all的区别
MySQL union用于把来自多个select语句的结果组合到一个结果集合中
区别:当使用union时,MySQL会把结果集中重复的记录删掉,而使用union all,MySQL会把所有的记录返回,且效率高于union
关键字执行顺序
from > join > where > group by > having > select > distinct > order by > limit
Grouping sets
比如,需求分别按照 店铺分组、订单组分组、店铺和订单组分组 统计订单销售额,并获取三者的结果集(插入到宽表)。可以通过union all多个group by来实现:
select store_id,
null,
sum(order_amount)
from yp_dwb.dwb_order_detail
group by store_id
union all
select null,
group_id,
sum(order_amount)
from yp_dwb.dwb_order_detail
group by group_id
union all
select store_id,
group_id,
sum(order_amount)
from yp_dwb.dwb_order_detail
group by store_id, group_id;
此时可以通过grouping sets来实现同样的运算,同时减少重复的SQL语句:
select store_id,
group_id,
sum(order_amount)
from yp_dwb.dwb_order_detail
group by
grouping sets (
store_id,
group_id,
(store_id, group_id)
);
CUBE
CUBE操作会生成所提供column所有可能的grouping sets结果
如:
select store_id,
group_id,
sum(order_amount)
from yp_dwb.dwb_order_detail
group by
cube (store_id, group_id);
等同于
select store_id,
group_id,
sum(order_amount)
from yp_dwb.dwb_order_detail
group by
grouping sets ((store_id,group_id), store_id, group_id, ());
Rollup
select store_id,
group_id,
sum(order_amount)
from yp_dwb.dwb_order_detail
group by
rollup (store_id,group_id);
等同于
select store_id,
group_id,
sum(order_amount)
from yp_dwb.dwb_order_detail
group by
grouping sets ((store_id,group_id), (store_id), ());
即,rollup((a),(b),(c))等价于grouping sets((a,b,c),(a,b),(a),())。
grouping
用来判断是否按照入参字段进行了分组,
如果是,则返回0,否则返回1,
组合参数,相当于二进制累加后,转换得到的十进制结果。
select *
from
(
select store_id,
group_id,
grouping(store_id) gs,
grouping(group_id) gg,
grouping(store_id, group_id) gsg,
sum(order_amount)
from yp_dwb.dwb_order_detail
group by cube(store_id, group_id)
) tmp
where gsg = 3
;
知识点
一个完整的SQL语句的写法:
select 列名1,列名2 from 数据表名 where 分组前的条件筛选 group by 分组字段
having 分组后的条件筛选 order by 排序字段 p [asc/desc] limit 起始索引,数据条数;
什么叫集群
集群指的是有多个节点的,对某一个程序或者某一个软件,重复部署了多次,各个节点都是一摸一样的,组合在一起成为集群
什么叫分布式
分布式指的是将一个程序或者软件,拆分成多个步骤,将每个步骤分别部署,组合在一起成为构建软件称为分布式软件
数据冗余
数据冗余是指同一条数据存储在不同的数据文件中都进行存储,从而产生冗余数据的现象
SQL查询速度慢的原因
1. 没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)
2. I/O吞吐量小,形成了瓶颈效应。
3. 没有创建计算列导致查询不优化。
4. 内存不足
5. 网络速度慢
6. 查询出的数据量过大(可以采用多次查询,其他的方法降低数据量)
7. 锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷)
8. sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。
9. 返回了不必要的行和列
10. 查询语句不好,没有优化
日志等级的严重级别
debug<info<warn<error<fatal
override和overload的区别
override:子类可以重写父类的方法或属性
overload:一个类中有多个同名方法,但参数列表不同,则称为重载
String、StringBuffer 与stringBuilder之间区别?
String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且浪费大量优先的内存空间
StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量
【可变、线程安全、多线程操作字符串】
StringBuilder是可变类,速度更快
【可变、线程不安全、单线程操作字符串】
over(order by salary range between 5 preceding and 5 following):窗口范围为当前行数据幅度减5加5后的范围内的。
数据仓库的特征
【面向主题】:以数据分析需求来对数据进行组织划分若干主题
【集成性】:将不同源数据库中的数据汇总到一起。
【非易失性】:对历史的所有数据的存储需要稳定性,使用非易失的介质来保存,用【HDFS】
【时变性】:数据会增量增加,数据分析的需求可能会发生变化,分析的过程也会发生调整。
with as , 视图view,临时视图temporary view, 缓存表cache table,表 table的区别
1、with T1 as (查询语句1)
查询语句N
好处:比子查询的可读性高。可以复用多次某个逻辑。
2、视图view:保存一段查询语句的【逻辑】,而不是查询语句的【结果集】,【永久】有效,查询这个视图,相当于查询一个【虚拟表逻辑】,如果保存的查询逻辑复杂,这查询视图也【耗时】。
支持重新覆盖 【create or replace view view1 as】
3、临时视图temporary view,只在当前会话【有效】,如果会话结束,则临时视图【销毁】
用 show tables 来查看临时的表或视图,isTemporory = true,表示是临时的。
类似于SparkSQL中的DataFrame.createOrReplaceTempView(临时视图名)
hive【不支持这个语法】
支持重新覆盖【create or replace temporary view temp_view3 as】
4、缓存表cache table :只在当前会话【有效】,将一段查询结果集缓存到【内存】,并赋予一个表名。
立即触发。
hive【不支持这个语法】
5、table:永久有效,保存数据结构和数据本身到磁盘。
create table xxxx as select * from yyyy;
5种语法选型
1、select from
(select from ()t1)t2
2、with t1 as (),
t2 ( select * from t1 ),
3、create view t1 as ...
create view t2 as select * from t1
4、create temporary view t1 as ...
create temporary view t2 as select * from t1
5、cache table t1 as ...
cache table t2 as select * from t1 ...
自定义函数:UDF,UDAF,UDTF
UDTF : 一进多出
UDF函数: 一进一出 ,大部分的函数都是属于 UDF函数
UDAF函数: 多进一出函数 ,大部分的聚合函数都是 UDAF
1. UDF:(用户定义普通函数)表的每一行数据利用若干字段,计算生成一个新字段
select a,b, myudf(a,b) as c from table1
2. UDAF:(用户自定义聚合函数)表分组后的多行数据的内容,计算生成一个新字段
select class_id,
sex,
myudaf(身高) as 平均身高
from table1
group by class_id,sex
3. UDTF:(用户定义表生成函数)进来一行的数据的某个字段是数组类型,生成多行数据,生成的新字段就是数组的每个元素(参考explode函数)
select explode(array) item from table1
关于转义字符 \t \r \n
\t 的意思是 横向跳到下一制表符位置
\r 的意思是 回车
\n 的意思是 换行
decimal()
decimal数据类型用于要求非常高的精确计算中,这些类型允许指定数值的精确度和计算方法作为选择参数。精确度在这里指为这个值保存的有效数字的总个数。而计数方法指的是小数点后数字的个数。
例如:decimal(5,2)规定了存储的值将不会超过五位数字 ,而且小数点后面有两位数字。
举例说明,11615.23653234568这个数存你说的三个格式
decimal:11615
decimal(3):999
decdimal(3,2):9.99
decimal(10,5)11615.23653
超出精度范围的数会被强制进位并只显示数据类型定义的格式
hdfs dfs -put -f
命令行上传文件,若hdfs上已经存在文件,要强制覆盖,用 -f 命令
提高SQL功底的思路。
--为什么造数据,因为有数据支撑,会方便我们根据数据结果去不断调整SQL的写法。
--造数据语法既可以create table再insert into,也可以用下面的create view xx as values语句,更简单。
--其中create view xx as values语句,SparkSQL语法支持,hive不支持。
--先将结果表画出来,包括结果字段名有哪些,数据量也画几条。这是分析他要什么。
--从源表到结果表,一路可能要走多个步骤,其实就是可能需要多个子查询,或者用with as来重构提高可读性。
--要由简单过度到复杂,不要一下子就写一个很复杂的。
--先写简单的select * from table..,每个中间步骤都执行打印结果,看是否符合预期,根据中间结果,进一步调整修饰SQL语句,再执行,直到接近结果表。
--数据量小,工具要快,如果用hive,就设置set hive.exec.mode.local.auto=true;
--如果是SparkSQL,就设置合适的shuffle并行度,set spark.sql.shuffle.partitions=4;
窗口函数中关于起点和终点的选择:
sum(score) over (partition by cid order by score desc
rows between unbounded preceding and current row)
unbounded:无界限
preceding:从分区第一行头开始,则为 unbounded。 N为:相对当前行向前的偏移量
following :与preceding相反,到该分区结束,则为 unbounded。N为:相对当前行向后的偏移量
current row:顾名思义,当前行,偏移量为0
分区表的好处
分区表方便查询时,避免全表扫描
方便数据按照时间或地区分门别类
方便对一个分区的数据进行删除和更新覆盖
查看分区表有哪些分区
show partitions insurance_app.policy_result;
删除一个分区的数据
alter table insurance_app.policy_result drop partition (month='2021-10');
SQL查询——日期函数(months_between、add_months(sysdate,n))
日期函数-关键函数:months_between(sysdate,日期属性)
案例1:查询emp表中,每个月员工都入职到现在一共工作多少月
select empno,ename, months_between(sysdate,hiredate) 月龄from emp;
补充扩展:查询工龄
select trunc(months_between(sysdate,hiredate)/12) 工龄 from emp;
日期函数-关键函数:add_months(sysdate,n)
案例2:在当前时间,6个月以后,时间是多少
select add_months(sysdate,6) from dual;
案例3:查询本月的最后一天
select last_day(Sysdate) from dual;
案例4:查询emp表,查询所有员工,入职当月的最后一天
select empno,ename,last_day(hiredate) from emp;
案例5:查询本年的第1天
select trunc(sysdate,‘YYYY’) from dual;
案例6:查询本月的第1天
select trunc(sysdate,‘month’) from dual ;
案例7:求出下个月的第一天
select trunc(add_months(sysdate, 1), ‘month’) from dual;
案例8:求出上个月的第一天
select trunc(add_months(sysdate, -1, ‘month’) from dual;
MySQL greatest()和least()函数
GREATEST和LEAST函数都使用N个参数,并分别返回最大和最小值。下面说明GREATEST和LEAST函数的语法:
大数据常用端口号
【Hadoop】
50070: HDFS WEB UI 端口
50090: Secondary NameNode 端口
50010: dfs.datanode.address
8020 : 高可用的 HDFS RPC 端口
9000 : 非高可用的 HDFS RPC 远程过程调用端口
8088 : Yarn 的 WEB UI 接口
8031 : Yarn.resouecemanager.resource-tracker.address
8032 : ResourceManager 的 applications manager(ASM) 端口
8485 : JournalNode 的 RPC 端口
8019 : ZKFC端口
19888: jobhistory WEB UI 端口
【Hive】
9083: metastore 服务默认监听端口
10000: Hive 的 JDBC 端口
【Zookeeper】
2181 : 客户端连接 zookeeper 的端口
2888 : zookeeper 集群内通讯使用,Leader 监听此端口
3888 : zookeepe r端口 用于选举 leader
【Hbase】
60010: Hbase 的 master 的WEB UI端口(旧)新的是16010
60030: Hbase 的 regionServer 的 WEB UI 管理端口
【Spark】
7077 : spark 的 master 与 worker 进行通讯的端口 standalone 集群提交
4040 : Application 运行时的 WEB UI 端口
8080 : Master 的 WEB UI 端口
8081 : Worker 的 WEB UI 端口
18080 : 历史日志 WEB UI 端口
【Application】
8080 : master 的 WEB UI 端口 资源调度
8081 : worker 的 WEB UI 端口 资源调度
4040 : Driver 的 WEB UI 端口 任务调度
18080: Spark History Server 的 WEB UI 端口
【Kafka】
9092: Kafka 集群节点之间通信的RPC 端口
【Redis】
6379: Redis 服务端口
【CDH】
7180: Cloudera Manager WebUI 端口
7182: Cloudera Manager Server 与 Agent 通讯端口
【HUE】
8888: Hue WebUI 端口
LInux命令
重定向命令
方式1: >
表示覆盖
方式2: >>
表示追加
一般在什么时候会使用重定向命令?
答案: 后期再启动一个软件的时候, 有时候需要将软件在控制台输出的日志信息, 写入到一个文件中.
cut命令
cut:文件内容查看命令,cut命令可以从一个文本文件或者文本流中提取文本列。
*参数* | *解释* |
---|---|
-b | 按字节选取 忽略多字节字符边界 |
-c | 仅显示行中指定范围的字符 |
-d | 自定义分隔符,默认为制表符 |
-f | 与-d一起使用,指定显示哪个区域。 |
-n | 与“-b”选项连用,不分割多字节字符; |
–complement | 补足被选择的字节、字符或字段; |
–out-delimiter=<字段分隔符> | 指定输出内容是的字段分割符; |
–help | 显示指令的帮助信息; |
–version | 显示指令的版本信息。 |
wc命令
用来进行统计操作的, 例如: 统计行数, 单词数, 字节数.
*参数* | *解释* |
---|---|
-l | 统计行数 |
-c | 统计字节数 |
-w | 统计单词数 |
-m | 统计字符数 |
awk命令
功能: 用于对文件中内容进行查看分析操作的, 与cut命令有点相似, 但是功能要比cut强大的多.
awk命令参数解释:
awk语法:
awk [选项参数] 'script' var=value file(s) //单独执行
或
awk [选项参数] -f scriptfile var=value file(s) //执行awk脚本文件.
选项参数说明:
-F 指定输入文件的分隔符
-v var=value or --asign var=value //赋值一个用户定义变量。
-f scripfile or --file scriptfile //从脚本文件中读取awk命令。
变量分配:
//默认情况下,awk 会将如下变量分配给它在文本行中发现的数据字段:
$0 //代表整个文本行;
$1 //代表文本行中的第 1 个数据字段;
$2 //代表文本行中的第 2 个数据字段;
$n //代表文本行中的第 n 个数据字段。
段之间的连接符OFS
awk '{OFS="#"}{print $1,$2,$3}' test_awk.txt
指定分隔符
-F 来指定分隔符
案例1: 打印出test_awk2.txt的第1段
awk -F ':' '{print $1}' test_awk2.txt
案例2: 打印出test_awk2.txt的所有段
awk -F ':' '{print $0}' test_awk2.txt
正则表达式
正则表达式简介:
1. 就是正确的, 符合特定规则的式子.
2. Linux的正则表达式的格式为: '/这里写具体的正则表达式的规则/' 单双引号均可.
3. 常用的正则表达式的规则如下:
^linux 以linux开头的行
$php 以php结尾的行
. 匹配任意单字符
.+ 匹配任意多个字符, +表示数量词, 即最少1次, 最多无数次.
.* 匹配0个或多个字符(可有可无)
[0-9a-z] 匹配中括号内任意一个字符
(linux)+ 出现多次Linux单词
(web){2} web出现两次以上
\ 屏蔽转义
Shell
shell脚本的三种运行方式
方式1: sh执行.
sh shell脚本的名字. //进入脚本的工作目录, 然后使用对应的sh或者bash来执行脚本.
//细节: 这种执行方式, 脚本文件不需要具有 可执行权.
方式2: 工作目录执行. //细节: 脚本文件需要具有 可执行权.
进入到脚本文件目录后, ./shell脚本名.sh 即可.
方式3: 绝对路径执行. //细节: 脚本文件需要具有 可执行权.
直接写即可, 例如: /export/data/shell/hello.sh
变量的其他赋值方式
1)可以使用read关键字从键盘获取内容赋值给变量
2)可以通过$(linux命令)或者$`linux命令`来执行linux命令,并将命令的执行结果赋值给变量
只读变量
使用readonly命令可以将变量定义为只读变量,只读变量的值不能被改变
删除变量
使用unset命令可以删除变量
unset variable_name
变量被删除后不能再次使用。unset命令不能删除只读变量
特殊变量
*变量* | *解释* |
---|---|
$# | 命令行参数的个数 |
$n | $1表示第一个参数,$2表示第二个参数,以此类推 |
$0 | 当前程序的名称 |
$? | 前一个命令或许或函数的返回码 |
$* | 以“参数1 参数2 。。。”形式保存所有参数 |
$@ | 以“参数1”“参数2”。。。形式保存所有参数 |
$$ | 本程序的(进程ID号)PID |
$! | 上一个命令的PID |
算数运算符
注意:表达式和运算符之间要有空格,完整的表达式要被` `包含
zookeeper
ZooKeeper本质上是一个小文件存储系统, zookeeper将其理解为就是集群的管理工具, 基于zookeeper的特有的机制(节点类型 和 watch机制)来实现对集群化管理操作
zookeeper的选举机制
核心: 如何选举谁当老大的问题
第一种选举主要出现在刚刚搭建好zookeeper集群:
只选那个myid最大的, 一旦集群数量过半即产生老大, 后续的节点id即使再大也只能当从节点
第二种选举方案: 如果集群中各个节点已经有数据了:
在启动过程中, 优先选举那个数据最多的节点当老大, 如果数据都一样, 再参考id谁最大, 一旦集群数量过半即产生老大,后续的节点id即使再大/或者数据在多, 也只能当从节点
Hadoop
Hadoop 1.x 架构存在的问题
1.存在单点故障问题
2.JobTracker的任务比较繁重,既要负责资源的分配,还要负责任务的接受和分配,当任务比较繁重的时候,容易出现宕机的风险
HDFS
HDFS的四个基本组件介绍:
Client:文件上传HDFS时,将文件切分成block块,进行存储。
NameNode:负责管理整个文件系统元数据,
DataNode:负责管理具体的文件数据块存储,
SecondaryNamenode:协助NameNode进行元数据的备份定期合并 fsimage和fsedits,并推送给NameNode。
namenode的基本原理
基本原理:
1) namenode保存的所有的元数据信息, 都是存储在内存中.
2) 整个HDFS可存储的文件数受限于NameNode的内存大小
注意: 实际环境中, namenode需要选择一台性能较高的服务器来当做namenode
3) namenode, 在保存元数据的时候, 会先将元数据保存到磁盘上, 然后在内存中也保存一份.
用于保存元数据的磁盘文件, 主要有二个: fsImage文件 和 edis文件
fsImage文件: 是namenode元数据的镜像文件: 保存一份较为完整的元数据信息
edits文件: 是namenode在运行过程中, 会将元数据更新, 新增 操作全部写入到edits文件中
注意:在达到一定阈值后, snn 会进行对 edits文件和fsImage文件进行合并操作, 以保证在namenode edits文件不至于过大.
4) namenode不真实负责数据的读写操作, 但是需要经过namenode的询问之后 才可以读取datanode数据
datanode的基本原理
1) datanode以数据块(block)来存储文件
2) datanode和namenode保持心跳的机制: 默认每隔3秒, 进行一次心跳, 如果10分钟内, 没有心跳,认为,宕机了
3) datanode每隔6个小时, 会向namenode报告一次完整的块信息
4) 当某个DataNode关闭时,它不会影响数据或群集的可用性。NameNode将安排由其他DataNode管理的块进行副本复制。
Secondary namenode 元数据的辅助管理 (snn)
执行流程:
1.SNN在时刻监控的NN中edits的文件,当这个文件达到一定的阈值(64MB或者1个小时)后,会通知namenode滚动形成新的edits文件
2.SNN会基于http请求,从NN中将edits文件和fsimage文件拷贝到自己的SNN的服务磁盘上
3.SNN将edits文件和fsimage文件统一加载到内存中,进行合并操作,形成一个完整的fsimage文件
4.将这个合并后的fsimage文件发送给NN,替换掉原有的fsimage文件即可
hdfs的安全模式
当hdfs刚刚启动的时候, 此时hdfs会自动进入到安全模式下, 此时, datanode在和namenode进行块的校验过程,对于用户而言, hdfs只能读不能写
默认情况下, hdfs启动到达30s后, 会自动的退出安全模式, 或者说等所有的块校验完成后 自动的退出安全模式
如果发现hdfs中块的副本不够了, 而且还无法新增了, 此时系统自动进入安全模式, 直到手动修复 (将对应块所属文件删除)
安全模式的相关命令:
hdfs dfsadmin -safemode get #查看安全模式状态
hdfs dfsadmin -safemode enter #进入安全模式
hdfs dfsadmin -safemode leave #离开安全模式
心跳机制
datanode要定时(3秒)给namenode发送心跳包,主要目的:
1.告诉namenode,我(datanode)还活着,如果没有定时发送心跳包,namenode会认为datanode进入到“假死”状态,如果超过一定时间间隔(10分钟),就认为它宕机了。
2.datanode定时(6个小时)向namenode发送自己的块信息(元数据)
3.因为namenode的元数据是存储在内存中的,当集群重新启动的时候,datanode需要向namenode发送自己的块信息
负载均衡
namenode要保证各个datanode存储的数据块的个数和整体利用率保持一致
副本机制
数据块的备份数默认是3,当某个数据块的个数不够3个的时候,namenode会自动新增该数据块的备份,当某个数据块的个数超过3个,会自动删除该数据块多余的备份。当数据块的备份不够3个,且无法新增数据块的时候,HDFS集群就会强制进入到:安全模式,只能读,不能写。
机架感知原理
在存储一个文件的某一个副本的时候, 根据机架感知原理以及网络拓扑关系(寻最近机架),将第一个副本放置在某一个机架上, 然后第二个副本放置在同机架的另一个服务器上, 第三个副本放置在另一个机架中
HDFS写数据的流程
1.客户端发送请求,连接namenode,请求写入数据操作
2.namenode接收到请求后,首先要判断客户端是否具有写入权限,没有直接报错,如果有权限,接下来要判断要写入的路径下是否有这个文件,如果有直接报错,如果没有,就通知客户端可以写入
3.客户端接收到可以写入的请求后,开始对文件进行切分,形成多个block切块
4.客户端向请求namenode,第一个block应该存储在哪些datanode中
5.namenode根据机架感知原理 以及 网络拓扑关系 和 副本机制,来寻找对应的datanode的地址列表,然后将这些datanode的地址列表返回给客户端
6.客户端从接收地址列表中,拿出第一个地址与之连接,形成一个pipeline的管道
7.到第一个连接成功后,让第一个连接第二个地址,然后第二个再连接第三个地址,形成一个pipeline的管道
8.客户端开始写入数据:数据以package数据包(64kb)的形式来发送数据,当第一台接收到数据后,然后将请求发送给第二个datanode,然后第二个接收到以后,然后再发送给第三个datanode
9.当第一个接收到数据后,建立好一个应答响应队列,当每个节点接收到数据后,都向应答队列反向报告,最终所有都报告完成后,将队列返回给客户端
10.不断地进行发送,不断的进行应答响应,即可完成数据的发送,当第一个block发送完成后,重新请求namenode第二个block应该存储在哪些datanode中,此时再从流程第5步,往下走即可 循环让所有的block写入成功即可。
HDFS的读取流程
1.客户端向namenode发送请求,连接namenode,请求读取数据操作
2.namenode接收到请求后,首先要判断客户端用户是否具有读取权限,没有则报错,如果有则判断文件是否存在,不存在就报错,如果文件存在且该客户端对于此文件有读取权限,则namenode会根据网络拓扑关系和机架感知原理返回给客户端一个datanode队列,记录的就是该文件的部分或全部的block块的信息,这些地址都是鲜活的
3.客户端根据namenode返回的datanode队列,与它们建立连接,然后并行的去下载这些block块的数据
4.当上述的block块信息下载完毕后,如果只是部分的block块的信息,则客户端会重新向namenode发送请求,从而获取剩下的部分或者剩下的全部的block块信息,然后继续下载,重复此步骤,直至所有的block全部下载完毕
5.按照block块的信息,拼接所有的block块,获取最终完整的文件
使用SNN来恢复元数据
当NameNode发生故障时,我们可以通过将 SecondaryNameNode 中数据 拷贝到 NameNode存储数据的目录的方式来恢复NameNode的数据.
操作步骤:
(1).杀死namenode进程
kill -9 Namenode进程号
(2).删除namenode存储的数据 //这个指令在node1中执行
删除 namenode 的 fsimage 相关的文件:
cd /export/server/hadoop-2.7.5/hadoopDatas/namenodeDatas
rm -rf *
删除 namenode 的 edits 相关的文件:
cd /export/server/hadoop-2.7.5/hadoopDatas/nn/edits
rm -rf *
//删除这些文件之后, 你的namenode都启动不起来.
(3).拷贝SecondaryNameNode中数据到原NameNode存储数据目录.
//以下指令在node2中执行, 因为SecondaryNameNode是部署在这里的.
//从node2中拷贝fsimage文件到 node1中.
cd /export/server/hadoop-2.7.5/hadoopDatas/snn/name //这里存放的是fsimage文件.
scp -r * node1:/export/server/hadoop-2.7.5/hadoopDatas/namenodeDatas/ //把数据拷贝到node1中.
//从node2中拷贝edits文件到 node1中.
cd /export/server/hadoop-2.7.5/hadoopDatas/dfs/snn/edits //这里存放的是edits文件.
cp -r * node1:/export/server/hadoop-2.7.5/hadoopDatas/nn/edits/ //把数据拷贝到node1中.
(4). 重新启动NameNode
hadoop-daemon.sh start namenode
(5). 打开浏览器, 在 node1:50070 中查看内容是否恢复成功即可.
YARN三大组件
【ResourceManager】:负责整个集群的资源管理和分配,是一个全局的资源管理系统
【NodeManeger】:是每个节点上的资源和任务管理器,它是管理这台机器的代理,负责该节点程序的运行,以及该节点资源的管理和监控。
【APPlicationMaster】:用户提交的每个应用程序均包含一个ApplicationMaster
yarn集群的运行机制
1.客户端将任务提交给resourceManager
2.resourceManager接收任务请求
3.在nodeManager上寻找一个比较空闲的节点,通知其启动一个appMaster,将任务信息发送给appMaster,等待appMaster是否启动成功,如果启动失败,认为当前任务直接报错,告知任务无法执行
4.appMaster启动后,开始和主节点保持着心跳机制。appMaster获知任务的相关信息(jar包路径,主类,参数)
5.开始根据任务信息,计算共需要多少个mapTask和多少个reduceTask
6.通过心跳包,将任务计算的结果资源需求发送给主节点,进行资源的申请
7.根据接收到的资源的申请信息,进行资源的分配工作:如果资源非常宽裕,一次性将所需要的所有的资源一并返回如果资源比较紧张,最起码应该返回所有MapTask所需资源
8.appMaster通过心跳包,一直询问,是否已经准备好资源,一旦发现准备好,将资源信息全部获取
9.根据获取资源信息,通知各个nodeManager,启动相关的程序(先启动mapTask)告知nodemanager任务信息(jar包,主类,参数信息)
10.每一个运行的container定时和appmaster汇总任务执行的进度,执行状态,并且还基于nodemanager和resourceManager,报告其资源的使用情况 如果初始的时候,只是返回mapTask运行的资源,当mapTask执行完成后或者执行过程中,appMaster向resourceManager询问reduceTask的资源是否已经准备好了
11.当整个mapTask和reduceTask都运行完成后,通知给appMaster。已经执行完成后,报告给resourceManager已经完成了任务的执行了
12.resourceManager,收回所有分配的资源,然后通知appMaster可以执行自毁程序了
OLTP 和 OLAP
OLTP(数据库): 联机事务处理
面向于事务的, 存储的业务数据(近期数据),数据库设计尽量避免冗余,数据库为了捕获数据.
OLAP(数据仓库): 联机分析处理
面向于主题的 ,存储的过去的数据,在一定程度上需要进行冗余存储,数据仓库是用来分析数据.
实时 OLTP 联机事务处理,OLTP系统强调的是内存效率,实时性比较高。
离线 OLAP 联机分析处理,OLAP系统强调的是数据分析,响应速度要求没那么高
hive的元数据
主要包含:
库的名称, 有那些表, 表中有那些字段, 字段的类型是什么 数据的分隔符号, 数据的路径
这些元数据都存储在哪里?
默认情况下, hive自身带有一个数据库(derby),但是一般不使用, 一般将元数据存储在关系型数据库中(MySQL)
在整个hive服务中, 需求有一个用于管理hive元数据的服务项: metastore服务
主要负责hive中元数据的管理工作, 所以在启动hive的时候, 需要启动metastore服务.
如何启动hive操作
【第一种方式: shell启动 (仅在第一次启动, 后续基本不用了)】
直接进入到bin目录下, 运行 ./hive操作 即可启动
【第二种启动方式: 不需要进入客户端, 直接使用命令在外部执行即可】
bin/hive -e "SQL语句"
bin/hive -f SQL脚本路径
这种方式, 主要运行在以后shell脚本中, 通过编写shell脚本来操作hive
【第三种启动方式: 基于 beeline的连接】
1) 第一步: 先启动好hive的服务: hiveserver2 和 metastore
前台启动: 会占用前端的窗口, 导致无法在使用这个窗口
./hive --service metastore
./hive --service hiveserver2
后台启动: 挂载在后台运行
nohup ./hive --service metastore &
nohup ./hive --service hiveserver2 &
注意:
nohup 表示将执行命令的输出的日志 输出到 nohup.out的日志文件中
& 表示 挂载到后台运行
2) 开启连接操作:
./beeline 回车
输入: !connect jdbc:hive2://node3:10000
用户名输入: root
密码输入: 随便写 无所谓
内部表
内部表 在创建表的时候, 不使用 external关键词, 内部表也被称为叫做管理表, 管理表的路径是在 /user/hive/warehouse当对管理表进行删除的时候, 其所表示的元数据和表中数据一并删除
create table stu02(
sid int,
sname string
) row format delimited fields terminated by '\t' //指定行字段分隔符为: \t
stored as textfile //设置保存方式为 textfile, 因为默认的保存方式就是它, 所以可以省略不写.
location '/test01'; //设置该数据表的存储位置为 /test01, 如果不指定, 则是默认路径: /user/hive/warehouse/...
外部表
创建一个外部表, 需要使用 external 关键词, 一般来说, 外部表的数据, 来源于HDFS中已有的文件数据, 只是通过这个外部表将数据与表产生映射关系,对于hive来说, 不认为对这份有绝对的控制权, 当删除外部表的时候, 数据是不会被删除的
如何让外部表和 HDFS原有文件数据产生关联呢? load命令
load data [local] inpath '/export/servers/datas/student.txt' overwrite | into table student [partition (partcol1=val1,…)];
说明:
[local] : 加上local 从本地加载数据, 不带local 表示从HDFS中加载数据
内部表 和 外部表 有什么区别?
内部表:
创建表的时候, 不需要添加 external关键词
内部表在删除表的时候, 会连带将表的元数据 和 表中数据 全部都删除
外部表:
创建表的时候, 需要添加 external 关键词
外部表在删除表的时候, 只会删除自己的元数据信息, 不会删除表中的数据
那么在构建一个表的时候, 构建内部表呢, 还是外部表呢?
如果数据已经存在HDFS上, 而且这份数据除了你要用, 其他人也要使用, 此时请构建外部表,
当构建外部表以后,对数据进行分析处理操作, 得到一张结果表, 这个结果表建议使用内部表
内部表 和 外部表的互转
如果要将内部表转换为外部表:
alter table 内部表名 set tblproperties('EXTERNAL'='TRUE');
如何将外部表转换为内部表:
alter table 外部表名 set tblproperties('EXTERNAL'='FALSE');
分区表
分文件夹的
作用:对文件数据划分成多个文件夹,然后当查询数据的时候,按照分区字段来查询,减少筛选表中的数据,提升查询的效率
关键语法:
partitioned by (分区字段名称 分区字段类型 ...)
注意:一旦使用分区表, 那么查询数据的时候, 最好携带上分区字段, 提高查询的效率
如何创建一个分区表:
create table score(
s_id string,
c_id string,
s_score int
) partitioned by (month string) row format delimited fields terminated by '\t';
创建带有多个分区的分区表:
create table score2(
s_id string,
c_id string,
s_score int
) partitioned by (year string,month string,day string) row format delimited fields terminated by '\t';
数据导入到这两个表中:
load data local inpath '/root/score.csv' into table score partition (month='201811');
load data local inpath '/root/score.csv' into table score2 partition (yeear='2018',month='11',day='01');
如何查看一个表中有哪些分区:show partition 表名;
添加一个分区:
alter table 表名 add partiiton(分区字段='值')[partition(分区字段='值')partition(分区字段='值')....];
删除分区操作:
alter table 表名 drop partition(分区字段='值');
如何识别分区目录:
第一种方式: 手动加分区即可
alter table score3 addpartition(month='202006') partition(month='202007');
如何向分区表加载数据呢?
静态分区加载:
适用于:一次添加一个分区的操作
格式1:
load data [local] inpath '数据路径' into [overwrite] table partition(分区字段1=值1,分区字段2=值2....)
格式2:
insert into|overwrite table 表名 partition(分区字段1=值1,分区字段2=值2...) + select语句
动态分区加载
适用于一次性添加N个分区
格式:
insert into|overwrite table 表名 partition(分区字段1,分区字段2...) + select语句
使用前提:
1) 必须开启动态分区的支持: set hive.exec.dynamic.partition=true;
2) 必须开启hive的非严格模式: set hive.exec.dynamic.partition.mode=nonstrict;
3) 后续select语句的结果最后的字段必须是分区字段, 保证顺序与分区字段顺序是一致的
动静混合加载
格式:
insert into|overwrite table 表名 partition(分区字段1=值1,分区字段2,分区字段3...) + select语句
使用前提:
1) 必须开启动态分区的支持: set hive.exec.dynamic.partition=true;
2) 必须开启hive的非严格模式: set hive.exec.dynamic.partition.mode=nonstrict;
3) 后续select语句的结果最后的字段必须是分区字段, 保证顺序与分区字段顺序是一致的
分桶表
分文件 底层就是MR的分区操作
将数据按照某个字段,或者某几个字段,进行分桶操作,将数据分发到不同的文件中
第一步:开启分桶操作,默认情况hive是关闭分桶操作
set.hive.enforce.bucketing=true;
第二步:设置reduce的数量:默认的数量为 1
set.mapreduce.job.reduces=3;
分桶逻辑:将字段进行hash取模计算法,进行分桶操作
如何创建一个分桶表:
create table course (
c_id string,
c_name string,
t_id string
) clustered by(c_id) into 3 buckets row format delimited fields terminated by '\t';
说明: 指定的分桶字段, 其实就是在指定MR的 k2的数据
分桶表有什么作用: 将一个文件的数据随机拆分多个文件, 主要是用于数据抽样
hive中数据库中表的相关的操作
创建数据库中表的语法:
CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name
[(col_name data_type [COMMENT col_comment], ...)]
[COMMENT table_comment]
[PARTITIONED BY (col_name data_type [COMMENT col_comment], ...)]
[CLUSTERED BY (col_name, col_name, ...) [SORTED BY (col_name [ASC|DESC], ...)] INTO num_buckets BUCKETS]
[ROW FORMAT row_format]
[STORED AS file_format]
[LOCATION hdfs_path]
说明信息:
[EXTERNAL] : external 外部的 , 如果添加了此关键词, 表示这个表是一个外部表
PARTITIONED BY : partitioned by 如果添加了此关键词, 表示是一个分区表
CLUSTERED BY : clustered by 如果添加此关键词, 表示这是一个分桶表
SORTED BY : 在分桶的过程中, 按照那些字段进行排序操作
INTO num_buckets BUCKETS : 要分多少个桶
ROW FORMAT : 定义字段与字段之间, 或者 字段中数据的分隔符号
STORED AS : 数据存储在HDFS上, 以何种格式来存储(textFile 普通文本)
LOCATION : 表的数据加载的路径
在hive中, 一共有四种类型的表 : 外部表, 内部表 , 分区表, 分桶表
hive中如何导入数据操作
1.直接使用insert into 的方式来插入数据操作 //了解即可. 不使用
insert into 表 (字段1,字段2,.....) values (值1,值2,....)
为什么不用这种方式?
通过这个操作, 底层会转换为MR执行, 每执行一次, 就会产生一个小文件
在进行数据插入的时候, 一般一次性插入N条数据,批量加载过程
2.使用 insert into | overwrite + select语句操作
(比较常用)可以加载所有表模型的数据 底层是运行 MR
注意事项:
后续的select语句查询的结果的结构, 要和 insert into的表的结构要一致.
//包含, 字段的数量, 字段类型, 字段的顺序 保证一致性
3.通过 load 加载数据 :(比较常用)可以加载除分桶表以外的所有表的数据,只能针对普通文本类型 底层运行 hdfs 的命令
load data [local] inpath '路径' into table 表名 [partition(分区字段=值)];
注意:
如果添加local:表示从本地来读取
本地:指的运行hiveserver2的服务器的本地
如果不加local,表示从hdfs中读取数据
底层运行:
如果不添加local:底层执行 hdfs dfs -put 操作
如果添加local:底层执行 hdfs dfs -mv 操作
//一般情况下: 从数据源加载数据到ODS层, 用 load方式, 从ods将数据加载 DW层, 一般使用 insert + select 语句
4.一次性可以给多个表插入数据:(了解)
from score
insert overwrite table score_first partition(month='202006') select s_id,c_id
insert overwrite table score_second partition(month='202006') select c_id,s_score;
等同于:
insert overwrite table score_first partition(month='202006') select s_id,c_id from score;
insert overwrite table score_first partition(month='202006') select c_id S__score from score;
5.import 导入 hive表数据:
import table teacher2 from '/export/teacher'; 注意 此路径为 HDFS的路径
hive如何导出数据操作
1.insert导出:(常用方式)
insert overwrite [local] directory '路径' select 语句;
注意:默认的分隔符为\001
insert overwrite [local] directory '路径' [row format dilimited fields terminated by '\t' [collection items terminated by '#'] ] select 语句;
案例操作:
insert overwrite local directory '/root/student' select * from student;
insert overwrite local directory '/root/student2' row format delimited fields terminated by '\t' select * from student;
2.hive shell 命令导出 (编写shell脚本的时候,在脚本导出hive中数据使用)
bin/hive -e "select * from myhhive.student;" > /root/student3
3.export导出到HDFS上:在客户端内部使用
export table student to '/student4';
hive的查询的语法
select [all | distinct] select_expr,select_expr,....
from table_reference
[where where_condition]
[group by col_list]
[having where_condition]
[order by col_list]
[cluster by col_list | [distinct by col_list][sort by col_list]]
[limit number]
order by :全局排序的
sort by :排序操作,局部排序,如果reduce只有一个,和order by 是相同的功能,做全局排序
distribute by :按照那些字段来当做k2 进行发送,理解为 分区操作
cluster by:当分区的字段和排序的字段是同一个,并且进行升序排序,此时可以直接使用cluster by简写
分组操作: group by
当使用分组操作后, 在select之后 和 from之前, 如果想直接使用某一个字段, 这个字段必须是group by存在的字段, 否则直接报错 但是可以在聚合函数中, 使用其他字段
CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name //EXTERNAL: 写了就是外部表, 不写就是内部表.
[(col_name data_type [COMMENT col_comment], ...)]
[COMMENT table_comment] //表的描述信息
[PARTITIONED BY (col_name data_type [COMMENT col_comment], ...)] //分区, 带了就是: 分区表, 后边是分区字段, 分区不同: 文件夹
[CLUSTERED BY (col_name, col_name, ...) //分桶, 带了就是: 分桶表, 后边是分桶字段, 区分不同: 文件
[SORTED BY (col_name [ASC|DESC], ...)] INTO num_buckets BUCKETS] //排序
[ROW FORMAT row_format] //指定行格式分隔符.
[STORED AS file_format] //指定文件的存储类型, 一般写: textfile
[LOCATION hdfs_path] //指定该数据表的位置, 默认在: /user/hive/warehouse
hive的调优部分讲解
hive的数据压缩,其实本质上指的就是MR中数据进行压缩操作
map端在输出的中间结果进行压缩:减少reduce从map端拉取的数据体积,提升拉取的效率
reduce端,在输出最终结果文件进行压缩:减少对磁盘的占用空间
————详情在hadoop笔记——day14_hive
hive的数据存储格式
默认的存储格式为textfile(普通文件)
HDFS(hive)支持的文件存储格式:textfile 、sequencefile 、orc、parquet
行式存储方案: textfile sequencefile
列示存储方案: orc parquet
行式存储和列式存储的优缺点
行式存储:
优点: 执行select * 查询的效率比较快 , 可读性更好
弊端: 占用的磁盘空间相当于列式存储更大一些, 执行select 字段 效率比较低
列式存储:
优点: 执行select 字段 效率比较高, 占用磁盘空间比较小
弊端: 执行select * 效率比较低, 可读性比较差
数仓一共被分为三层:
ODS层:源数据层,主要和数据源打交道
对于ODS层,表数据的格式一般采用textfile,从文件到表的导入操作,一般使用load data 操作,而load data只能适用于 普通文本类型
DW层:数据仓库层,主要使用进行数据分析工作
对于DW层,表数据的格式,一般采用的:ORC + snappy的压缩
但是ORC内部,自带了一种压缩格式GZIP压缩格式,在使用ORC的时候,需要将这种格式的内置默认压缩算法更换为snappy压缩算法
APP(DA ADS)层:数据应用层,数据展示层,主要是用于存储分析的结果数据,后期如果要对接BI,可以将结果数据,从APP层将数据导出到 交互式的数据库(mySQL)中,从而提升查询数据的效率,对于APP层,表数据的格式一般采用 textfile
压缩方案的选择:
zlib(GZ|GZIP):
优点: 压缩率比较高
弊端: 解压缩的性能比较一般
适用于: 数据量比较大, 写入较多, 读取较少的场景
SNAPPY:
优点: 解压缩性能比较高 以及具有较好压缩率
弊端: 没有zlib压缩率高, 以及如何使用原生hadoop版本, 并不支持snappy压缩方案
适用于: 数据量比较大, 读写操作都比较高的场景 , 并且磁盘空间相对比较充足情况下
RDBMS的特点
RDBMS:关系型数据库管理系统
工具:MySQL,Oracle,SQL server....
应用:业务性数据存储系统:事务性、稳定性
特点:体现数据之间的关系,支持完整事务,保证业务完整性和稳定性,小数据量的性能也比较好
开发:SQL
业务架构中存在的问题:
当并发量很大,所有高并发全部直接请求MySQL,容易导致MySQL崩溃
NoSQL的特点
NoSQL:Not Only SQL:非关系型数据库
工具:Redis,Hbase,MongoDB....
应用:一般用于高并发和高性能场景下的数据缓存或者数据库存储(永久性)
特点:读写速度非常快,并发量非常高,相对而言不如RDBMS稳定,对事物性的支持不太友好
开发:每种NoSQL都有自己的命令语法
解决上面RDBMS的问题:实现读写分离
读请求:读请求不读取MySQL,读取Redis
写请求:写请求直接写入MySQL
Redis
定义:基于内存的分布式NoSQL数据库
设计:
- 所有数据存储在内存中
- 问题1:内存小、高可用
- 问题2:易丢失
- 将内存中的数据同步到磁盘中
- 每次写入数据,直接写入Redis的内存,Redis会自动内存数据同步到磁盘文件中
- 下次Redis重启,Redis将磁盘中所有数据恢复到内存
- 解决方案:persist
- 数据库【永久性存储】、缓存【临时存储】和消息中间件【MQ:一般不用】
应用场景:
- 缓存:高并发和高性能的大数据量的存储【临时性存储】
- 数据库:高并发的小数据量的存储【永久性存储】
- 大数据架构平台中:Redis用于做实时计算结果存储
- 消息中间件:MQ,传递两个系统的数据,一般不用
Redis的数据结构及数据类型
数据结构
整个Reids中所有数据以KV结构形式存在
K:作为唯一标识符,唯一标识一条数据,K固定为String类型
V:真正存储的数据,V可以有多种类型
String、Hash、List、Set、Zset、BitMap、HypeLogLog
理解Redis:类似于Java中的一个Map集合,可以存储多个KV,根据K获取V,Map<String,Object>
Redis持久化
数据存储如何保证数据安全?
磁盘存储:数据直接存储在硬盘上
特点:容量大,安全性相对内存较高,读写性能相对内存慢
场景:进程故障,断电导致文件的损坏,硬盘机械损坏
解决
软件:冗余副本,HDFS
数据:每个块存储多份
元数据文件fsimage
硬件:RAID
RAID0:两块硬盘分别1TB,在操作系统中1块硬盘,2TB
RAID1:两块硬盘分别1TB,在操作系统中1块硬盘,1TB
内存存储:数据直接放在内存中
特点:读写速度比较快,容量小,安全性比较低
场景:进程故障,断电
解决:
记录内存操作日志:将内存中的变化记录在磁盘文件中
将内存数据拍摄快照:将内存的快照存储在磁盘上
副本机制:在另外一台机器上构建相同数据的内存副本
RDB方案(Redis默认的持久化方案)
思想:
按照一定的时间内,如果Redis内存中的数据产生了一定次数的更新【新增、修改、删除】,就将整个Redis内存中的所有数据拍摄一个全量快照文件存储在硬盘上
新的快照会覆盖老的快照文件,快照是全量快照,包含了内存中所有的内容,基本与内存一致
如果Redis故障重启,从硬盘的快照文件进行恢复
触发
手动触发:当执行某些命令时,会自动拍摄快照【一般不用】
save:手动触发拍摄RDB快照的,将内存的所有数据拍摄最新的快照
前端运行
阻塞所有的客户端请求,等待快照拍摄完成后,再继续处理客户端请求
特点:快照与内存是一致的,数据不会丢失,用户的请求会被阻塞
bgsave:手动触发拍摄RDB快照的,将内存的所有数据拍摄最新的快照
后台运行
主进程会fork一个子进程负责拍摄快照,客户端可以正常请求,不会被阻塞
特点:用户请求继续执行,用户的新增的更新数据不在快照中
shutdown:执行关闭服务端命令
flushall:清空,没有意义
自动触发:按照一定的时间内发生的更新的次数,拍摄快照
配置文件中有对应的配置,决定什么时候做快照
#Redis可以设置多组rdb条件,默认设置了三组,这三组共同交叉作用,满足任何一个都会拍摄快照
save 900 1
save 300 10
save 60 10000
为什么默认设置3组?
原因:如果只有一组策略,面向不同的写的场景,会导致数据丢失
针对不同读写速度,设置不同策略,进行交叉保存快照,满足各种情况下数据的保存策略
优点:
RDB方式实现的是全量快照,快照文件中的数据与内存中的数据是一致的
快照是二进制文件,生成快照加载快照都比较快,体积更小
Fork进程实现,性能更好
总结:更快、更小、性能更好
缺点:
存在一定概率导致部分数据丢失
应用:希望有一个高性能的读写,不影响业务,允许一部分的数据存在一定概率的丢失的缓存场景,大规模的数据备份和恢复
AOF设计
思想:
按照一定的规则,将内存数据的操作日志追加写入一个文件中
当Redis发生故障,重启,从文件中进行读取所有的操作日志,恢复内存中的数据
重新对Redis进行执行,用于恢复内存中的数据
实现:追加的规则
appendfsync always
每更新一条数据就同步将这个更新操作追加到文件中
优点:数据会相对安全,几乎不会出现数据丢失的情况
缺点:频繁的进行数据的追加,增大磁盘的IO,导致性能较差
appendfsync everysec
每秒将一秒内Redis内存中数据的操作异步追加写入文件
优点:在安全性和性能之间做了权衡,性能要比always高
缺点:有数据丢失风险 ,但最多丢失1秒
appendfsync no
交给操作系统来做,不由Redis控制
肯定不用的
优点:安全性和性能做了折中方案,提供了灵活的机制,如果性能要求不高,安全性可以达到最高
缺点:
这个文件是普通文本文件,相比于二进制文件来说,每次追加和加载比较慢
数据的变化以追加的方式写入AOF文件
问题:文件会不断变大,文件中会包含不必要的操作【过期的数据】
解决:模拟类似于RDB做全量的方式,定期生成一次全量的AOF文件
应用:数据持久化安全方案
理论上绝对性保证数据的安全
持久化方案:两种方案怎么选?
AOF + RDB
AOF:保证运行时数据安全
RDB:用于做辅助构建数据备份
问题:如果AOF和RDB一起使用,Redis启动时,加载哪种方式的数据到内存中?
AOF
Redis过期策略与内存淘汰机制
过期策略
设计思想:避免内存满,指定Key的存活时间,到达存活时间以后自动删除
命令:expire/setex
【定时过期】:指定Key的存活时间,一直监听这个存活时间,一旦达到存活时间,自动删除
需要CPU一直做监听,如果Key比较多,CPU的消耗比较严重
【惰性过期】:指定Key的存活时间,当使用这个Key的时候,判断是否过期,如果过期就删除
如果某个Key设置了过期时间,但是一直没有使用,不会被发现过期了,就会导致资源浪费
【定期过期】:每隔一段时间就检查数据是否过期,如果过期就进行删除
中和的策略机制
Redis中使用了 惰性过期和定期过期 两种策略共同作用
淘汰机制
设计思想:内存满了,怎么淘汰
Redis的内存淘汰策略是指在Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据, Redis 源码中的默认配置
缓存:所有KV中,通过LRU淘汰一个
数据库:所有设置了过期时间的KV中,通过LRU淘汰一个
--详细资料在\实时生态圈笔记Redis(二)
主从复制集群设计
架构问题
Redis的服务只有单台机器
问题1:单点故障问题,如果Redis服务故障,整个Redis服务将不可用
问题2:单台机器的内存比较小,数据存储的容量不足,会导致redis无法满足需求
设计
分布式主从架构
Master:主节点
负责对外提供数据的读写
Slave:从节点
负责对外提供读的请求,不能接收写请求
负责与主节点同步数据
特点:主从节点上的数据都是一致的,连接任何一个节点实现读,写操作只能连接主节点
主从复制同步与配置
同步策略
全量同步
新的Slave节点添加到集群中或者Slave与Master断开很长时间,超出了数据积压缓冲大小
Master会通过bgsave构建一份完整的内存快照,发送给Slave,Slave加载快照的数据
Master也会维护一个缓存队列,将快照中没有的数据,放在缓存队列中,Slave会同步缓存中的数据
全量同步比较消耗性能
增量同步
Master会将当前Slave没有同步的数据放在积压缓存区中
Slave请求同步数据时,Master如果判断同步的数据在积压缓冲区中进行返回同步
哨兵集群的设计
哨兵设计
思想:基于主从复制模式之上封装了哨兵模式,如果Master出现故障,哨兵让Slave选举成为新的Master
实现:哨兵进程实现
必须能发现Master的故障
必须监听Master
必须负责重新选举新的Master
监听Slave,保证所有Slave状态,能够成为Master
哨兵集群:多个哨兵节点组成,所有哨兵彼此互相监听
主观性故障
客观性故障:超过一定个数的哨兵认为Master故障,才真正故障,实现选举切换
架构
Redis主从架构
哨兵进程
每个哨兵负责监听所有Redis节点和其他哨兵
为什么要监听所有Redis的节点:发现所有节点是否会出现故障
如果Master出现故障,会进行投票选择一个Slave来成为新的Master
为什么要监听别的哨兵:如果哨兵故障,不能让这个哨兵参与投票选举等
哨兵功能
集群监控:监控节点状态
消息通知:汇报节点状态
故障转移:实现Master重新选举
配置中心:实现配置同步
流程
step1:如果Master突然故障,有一个哨兵会发现这个问题,这个哨兵会立即通告给所有哨兵
主观性故障【sdown】
step2:当有一定的个数的哨兵都通告Master故障了,整体认为Master故障了
客观性故障【odown】
step3:所有哨兵根据每台Slave通信的健康状况以及Slave权重选举一个新的Master
step4:将其他所有的Slave的配置文件中的Master切换为当前最新的Master
分片集群的设计
分片集群模式
思想:
将多个Redis小集群【理解为多个哨兵集群】从逻辑上合并为一个大集群,每个小集群分摊一部分槽位,对每一条Redis的数据进行槽位计算,这条数据属于哪个槽位,就存储对应槽位的小集群中
分片的规则:==根据Key进行槽位运算:CRC16【K】 & 16383 = 0 ~ 16383
常见的分布式架构
公平节点架构:ZK、Redis
普通节点架构:HDFS、YARN
都依赖于ZK
Hbase
官方定义:Hbase是一个基于Hadoop的分布式的 可扩展的 大数据存储的 基于内存列存储NoSQL数据库
功能:提供分布式的实时随机的大数据持久性存储
实时随机读写:性能非常高
大数据量的持久性存储
应用:
大数据量、高并发、高性能的结构化数据存储(读写)
有固定的行和列的数据或者半结构化(每一行的列不一样)
电商:订单
交通:实时监控、实时车辆轨迹
金融:交易信息
为什么Hbase读写速度比较快?
Hbase的数据肯定要存储在内存中
为什么Hbase可以支持大数据量?
Hbase支持分布式架构
Hbase的数据要想实现大数据存储,必须基于磁盘
核心思想:
设计:分布式内存 + 分布式磁盘(HDFS)
应用:实时场景
特点:数据刚写入以后,立即就会被读取,读取完成以后,数据就基本没有需求
1.什么样的数据放在内存中?什么样的数据放在磁盘中?
设计:所有写入Hbase的数据,都写入内存在,如果内存达到阈值,内存中的所有数据会Flush到磁盘中,将内存空间腾出,存储最新的数据
2.为什么内存满了以后,不将后面的数据写入磁盘,而是将内存的数据写入磁盘,后面的数据再写入内存?
最新的数据被读取的概率最大
最新的数据放入内存,被读取的速度就最快
内存中永远的都是最新的数据
3.依旧存在一定的概率,需要从磁盘中读取大量的数据,怎么保证读磁盘也很快?
存储磁盘:文件
索引查询
二进制
有序
HBase通过很多种机制来保证即使读取文件依旧可以有很高的效率
Hbase与HDFS、Redis有什么区别?
特点 | HDFS | Redis | Hbase |
---|---|---|---|
计算场景 | 离线 | 实时 | 实时 |
定义 | 分布式文件系统 | NoSQL的KV数据库 | 结构化数据NOSQL数据库【KV】 |
存储 | 分布式磁盘 | 分布式内存 | 分布式内存 + 分布式磁盘 |
应用场景 | 离线文件永久性存储 | 实时大数据量缓存或者小数据量永久性存储 | 实时大数据量的永久性存储 |
Hbase中的数据库概念NameSpace
概念:NameSpace命名空间,等同于数据库中Database的概念
Hbase中任何一张表都必须属于某个NameSpace
使用
MySQL中如果要想使用某个数据库中的某张表:itcast数据库中的heima这张表
方式一:绝对路径
select * from itcast.heima
方式二:相对路径
use itcast;
select * from heima;
Hbase中没有切换NameSpace的命令,访问所有表只能使用Namespace:TableName方式来访问表
Hbase中没有相对路径
itcast:heima
特例:Hbase中自带了一个Namespace叫default,除非这张表在default Namespace下面,可以不加ns访问
Hbase中的表概念Table
概念:表的概念,等同于数据中的表的概念
使用:Hbase中的表是分布式的:表的数据分布式存储在不同的机器上
Hbase是分布式存储【读写】,读写是操作表
所有表在访问时,都必须加上NS的名称,除非表在default默认ns下,可以不加ns名称来进行访问
有一个ns叫做itcast,这个ns中有一张表叫做heima
itcast:heima
Hbase中自带了一个ns叫做default,这个ns中有一张表叫t1
default:t1 或者 t1
Hbase中的存储概念
数据行设计Rowkey
Rowkey:行健,这个概念是整个Hbase的核心,类似于MySQL主键的概念
MySQL主键:可以没有,唯一标记一行、作为主键索引
Hbase行健:
所有Hbase的表不用定义,所有Hbase的表自带行健这一列【行健这一列的值由用户自己设计】
//唯一标识一行
//作为Hbase表中的唯一索引:
Hbase不能创建索引
问题:查询数据走索引查询和全表扫描,只有按照rowkey查询才走索引查询
原因:Hbase整个数据存储都是按照Rowkey实现数据存储的
Rowkey的设计决定了查询效率
假设1:rowkey:sid
索引:sid
假设2:rowkey:name_sid
索引:name、name + sid
列族设计ColumnFamily
cf:列族,对除了Rowkey以外的列进行分组,
注意:任何一张Hbase的表,都至少要有一个列族,除了Rowkey以外的任何一列,都必须属于某个列族,Rowkey不属于任何一个列族
分组:将拥有相似IO属性的列放入同一个列族【要读一起读,要写一起写】
设计原因:划分列族,读取数据时可以加快读取的性能
如果没有列族,没有划分班级教室:找一个人,告诉你这个人就在这栋楼
如果有了列族,划分了教室:找一个人,告诉你这个人在这栋楼某个房间
数据列设计Qualifier
Qualifier/Column:列,与MySQL中的列是一样
注意:Hbase除了rowkey以外的任何一列都必须属于某个列族,引用列的时候,必须加上列族的名称
如果有一个列族:basic
如果basic列族中有两列:name,age
basic:name
basic:age
Hbase是列存储,Hbase中每一行拥有的列是可以不一样的
每个Rowkey可以拥有不同的列
多版本设计VERSIONS
功能:某一行的任何一列存储时,只能存储一个值,Hbase可以允许某一行的某一列存储多个版本的值的
默认每一列都只能存储1个版本
级别:列族级别,指定列族中的每一列最多存储几个版本的值,来记录值的变化的
区分:每一列的每个值都会自带一个时间戳,用于区分不同的版本
默认情况下查询,根据时间戳返回最新版本的值
分布式设计
HDFS设计
文件夹
文件:划分Block:根据每128M划分一个块,每个Block存储在不同的机器上
Hbase的表如何实现分布式存储的?
Namespace
Table:Regin:分区,Hbase中任何一张表都可以有多个分区,数据存储在表的分区中,每个分区存储在不同的机器上
非常类似于HDFS中Block的概念
划分规则:范围分区:决定了这条数据会写入Hbase表的哪一个分区
设计对比
分布式概念 | HDFS | Redis | Hbase |
---|---|---|---|
对象 | 目录 + 文件 | DB0 | Namespace + Table |
分布式 | Block | 分片集群 | Region |
划分规则 | 按照大小划分:128M | 槽位划分 | 按照范围划分 |
整体概念对比
概念 | MySQL | Hbase |
---|---|---|
数据库 | DataBase | NameSpace |
数据表 | Table | Table【分布式的】 |
数据分区 | - | Region |
数据行 | 数据【主键+其他列】 | Rowkey+数据【其他列】 |
列族 | - | ColumnFamily |
数据列 | 普通列与对应的值 | 列【timestamp】与对应的值【支持多版本】 |
Hbase中的按列存储
本质:KV结构存储
底层:将每一列的数据变成一个KV对来进行存储
结构:
K:Rowkey + CF + column + TS
V:value
功能:
Hbase的最小操作单元是列,不是行,可以实现对每一行的每一列进行读写
设计:
MySQL:按行存储,最小的操作单元是行
Hbase:按列存储,最小的操作单元是列
MySQL中读取数据
查询【id,name,age,addr,phone……100列,每一列10M】:select id from table ;
先找到所有符合条件的行,将整行的数据所有列全部读取:1000M数据
再过滤id这一列:10M
Hbase中读取数据
查询【id,name,age,addr,phone……100列,每一列10M】:select id from table ;
直接对每一行读取这一列的数据:10M
HBASE集群架构
架构
Hbase集群:分布式架构集群,主从架构
HMaster:主节点:管理节点
负责所有从节点的管理:监控所有从节点的状态
负责元数据的管理
HRegionServer:从节点:存储节点
负责管理每张表的分区数据:Region
对外提供Region的读写请求
用于构建分布式内存:多台RegionServer利用自己的JVM堆内存构建分布式存储
角色
Hbase:通过RegionServer构建分布式内存
HDFS:构建分布式磁盘,当分布式内存的数据存储达到阈值,将内存中的数据写入HDFS
Zookeeper
辅助选举:多个Master的Active选举
存储元数据:Hbase的管理元数据
存储设计:存储架构
Client:负责连接服务端
提交用户操作给服务端执行
将服务端执行的结果返回给用户
Zookeeper:存储Hbase管理元数据
管理元数据:Hbase中有哪些Master、RegionServe正在处理的任务
表的元数据:一张表有几个分区,每个分区存储在哪台机器...
注意:表的元数据不在Zk中:【Hbase:meta】
所有Hbase的客户端想要访问Hbase,必须获取Zk的地址
Hbase:分布式内存
HMaster:管理节点
管理从节点:保证数据安全
发现RS故障:监听所有RS在ZK中创建的临时节点
恢复RS数据到别的RS上
内存:WAL:write ahead log:预写日志【Hlog】
写入内存之间先将这个操作追加记录在日志中
磁盘:HDFS,副本机制
管理元数据【将元数据的信息记录在ZK中】
RegionServer的java堆内存
HDFS:分布式磁盘
当RegionServer的内存达到一定阈值,会将内存中的数据写入HDFS
文件:二进制文件HFILE
存储设计:Table、Region、RS的关系
Table:(逻辑概念)是一个逻辑对象,物理上不存在,供用户实现逻辑操作,存储在元数据的一个概念
类似于HDFS中文件
数据写入表以后的物理存储:Region分区
一张表会有多个分区Region,每个分区存储在不同的机器上
默认每张表只有一个Region分区
Region:(分区)Hbase中实现分布式存储和负载均衡的单元
类似于HDFS中Block,用于实现Hbase中分布式
就是分区的概念,每张表都可以划分为多个Region,实现分布式存储
默认一张表只有一个分区
每个Region由一台RegionServer所管理,Region存储在RegionServer
RegionServer:(物理概念)是一个物理对象,Hbase中的一个进程,管理一台机器的存储
类似于HDFS中Datanode
一个RegionServer可以管理多个Region
一个Region只能被一个RegionServer所管理
存储设计:Region的划分规则
回顾
HDFS划分规则
划分分区的规则:按照大小划分,文件按照128M划分一个Block
Redis划分规则
划分规则:给每个小集群分配一定范围的槽位,所有小集群所有槽位加在一起:0 ~16383
分区规则:CRC16【key】& 16383,槽位分区
Hbase分区划分规则:范围划分【根据Rowkwy范围】
任何一个Region都会对应一个范围,不论有多少分区,所有分区的范围加在一起:-00 ~ +00
如果只有一个Region,范围 -00 ~ +00
范围划分:从整个 -00 ~ +00 区间上进行范围划分
每个分区都会有一个范围:根据RowKey属于哪个范围就写入哪个分区
[startKey,stopKey)
前闭后开区间
默认:一张表创建时,只有一个Region
范围:-00 ~ +00
自定义:创建表时,指定有多少个分区,每个分区的范围
什么是ETL
ETL: 抽取 转化 加载
狭义的概念:
指的从ODS层将数据抽取出来, 然后根据要分析的主题, 对数据进行转换(清洗,类型转换)操作, 将转换后的数据加载到DW层过程, 称为ETL操作
宽泛的概念:
从数据源到ODS层, 以及从ODS层到DW层, 最后从DW层到DA层, 这些都是存在抽取和加载过程的, 所以可以认为这些都是ETL范畴之内, 描述整个数仓的全过程
维度和指标
维度: 指的看待问题的角度, 一般来说, 角度越多, 也就是代表维度越多
例如: 分析班级同学, 此时可以通过年龄 穿着 性别 身高 ... 进行分析, 而这些都是角度或者维度
维度的分类:
定性维度: 指的字符串维度, 以及包括需要计算每个 各个情况(没有具体范围)
这种维度在SQL上最大的表示通过 group by 来处理
定量维度: 指的数值类型的维度 以及包括需要计算某一个固定范围下情况
这种维度在SQL上最大的表示通过 where来处理
维度的分层和分级: 在根据某一个维度进行计算的时候, 维度可以再次进行细化维度的操作, 我们将这种情况称为分层或分级操作
例如: 时间维度
细化分层: 年 月 天 小时 周 季度
例如: 地区维度
细化分层: 省份 市 县
维度的上卷 和 下钻操作
必须有一个标准值, 比如以 天为例 , 上卷指的统计每周 每月 每年 下钻指的统计每个小时
总结: 有一些维度可以进行细化分层, 从统计分析的角度来看, 无非多了需要计算维度而已
指标: 衡量事务的标准, 或者度量
常见的指标: sum max min avg count topN 比率
指标的分类:
绝对数值: 指的是需要计算出具体的结果的指标
相对数值: 指的需要计算比率范围的情况
数仓建模
数仓建模主要适用于指导怎么在数据仓库中进行构建表, 以及如何进行数据仓库的分层构建, 以及包括表中应该有那些字段, 这都是由一套理论来指导的
常见的数仓建模规范主要有两种:一种为三范式建模规范, 还有一种维度建模规范。
三范式建模:要求在构建的时候, 尽可以避免数据冗余出现, 同时每个表需要有一个主键, 三范式建模方案一般应用RDBMS中。
维度建模:更多侧重于数据分析 , 所以在建模过程中,只要是利于分析的建模 都是合格的,在此基础, 允许数据可以出现一定的冗余情况,在维度建模的思想中,主要定义两种类型的表:事实表,维度表
建模的作用
访问性能:能够快速查询所需的数据,减少数据IO
数据成本:减少不必要的数据冗余,实现计算结果数据复用,降低大数据系统中的存储成本和计算成本
使用效率:改善用户体验,提高使用数据的效率
数据质量:改善数据口径的不一致性,减少数据计算错误的可能性,提供高质量的,一致的数据访问平台
事实表的三种分类
事实表的分类:
1) 事务事实表: 表示最原始的事实表,其实就是事务事实表, 一条数据就是一个事实
2) 周期快照事实表: 周期快照事实表以具有规律性的、可预见的时间间隔来记录事实,时间间隔如每天、每月、每年等等。
3) 累积快照事实表: 累积快照事实表代表的是完全覆盖一个事务或产品的生命周期的时间跨度,它通常具有多个日期字段,用来记录整个生命周期中的关键时间点
星型模型和雪花模型 是什么 以及区别
星型模型:
当所有的维度表都是和事实表直接相连是,整个图形看上去就是一个星星,称为星型模型。星型模型是一个非正规化的架构,因为多维数据集的每一个维度都和事实表直接相连,不存在渐变维度,所以有一定的数据冗余,因为有数据冗余,很多情况下不需要和外表关联进行查询和数据分析,因此效率相对较高。
优点:因为不需要和外表关联,所以查询和数据分析时的效率较高。
缺点:有数据冗余。
雪花模型:
当有多个维度表没有和事实表相连,而是通过其他维度表,间接的连接在事实表上,其图形就像是一个雪花,因此称为雪花模型,雪花模型的优点:减少了数据冗余,在进行数据统计或分析的时候,需要和其他的表进行关联。
优点:连接较小的维度表,减少了数据的冗余。
缺点:关联的维度表较多,查询性能会相对较低。
区别及优缺点
区别:维度表是直接关联事实表还是其他维度表,星型模型更适合用于做只能指标分析,而雪花模型更适用于做维度分析。
雪花模型的优点:通过最大限度的减少数据量以及连接较小的维度表来实现改善查询的功能,雪花结构减少了数据的冗余。
雪花模型的缺点:在雪花模型需要事实表和维度表之间的连接较多,因此查询性能会相对较低
sqoop的基本介绍
sqoop是一款apache开源产品, 主要是用于数据的导入导出的工具, 负责将数据在RDBMS(关系型数据库) 与hadoop生态圈之间进行数据导入导出操作
导入: 从 RDBMS到 hadoop生态圈
导出: 从hadoop生态圈 到 RDBMS
基于sqoop将数据导入到HIVE中: 支持两种方案 : 原生API, HCataLog:
从mySQL中将数据导入到HDFS(全量导入)
命令1:
sqoop import \
--connect jdbc:mysql://hadoop01:3306/test \
--username root \
--password 123456 \
--table emp
通过测试发现: 只需要指定输入端, 即可将数据导入到HDFS中, 默认情况下, 将数据导入到HDFS对应操作用户家目录下, 建立了一个与导入表同名的目录, 同时发现表中, 有几条数据, sqoop默认就会启动几个map来读取数据, 默认数据中字段与字段的分割符号为逗号
注意: 当执行sqoop的时候, 如果不指定mapTask数量, 默认最多和yarn管理的CPU核心数是相等的
如何减少map的数量呢? 只需要在命令后面添加一个 -m的参数即可, 在参数中指定需要运行多少个map
sqoop import \
--connect jdbc:mysql://hadoop01:3306/test \
--username root \
--password 123456 \
--table emp \
-m 1
如果想使用二个map呢? 建议添加 --split-by 表示按照那个字段进行分割表数据操作
sqoop import \
--connect jdbc:mysql://hadoop01:3306/test \
--username root \
--password 123456 \
--table emp \
--split-by id \
-m 2
如果修改其输出的目的地: --target-dir指定HDFS的目的地路径 --delete-target-dir 如果目的地存在, 先删除
sqoop import \
--connect jdbc:mysql://hadoop01:3306/test \
--username root \
--password 123456 \
--table emp \
--delete-target-dir \
--target-dir /emp \
-m 1
如何设置输出中字段与字段之间分隔号: 比如 以空格 --fields-terminated-by 指定字段与字段之间分割符号
sqoop import \
--connect jdbc:mysql://hadoop01:3306/test \
--username root \
--password 123456 \
--table emp \
--delete-target-dir \
--target-dir /emp \
--fields-terminated-by ' ' \
--split-by id \
-m 2
sqoop详细操作 ->【教育项目day02笔记】
Spark
面试题
为什么spark比hive快?
1.spark的中间数据都是基于RDD的,而RDD是基于内存的
2、hive会将SQL翻译成MapReduce,hive的job有连续的MapTask和ReduceTask,上一个ReduceTask的结果写到磁盘,下一个MapTask会读取磁盘的结果,处理后再将中间结果写到磁盘。频繁的磁盘读写,并伴随序列化和反序列化,导致慢。
3、Spark都是基于内存的,基于DAG有向无环图,将job划分为多个stage,stage中有连续的的算子,经过pipeline合并后简化成一次性并行计算。所有的stage内核stage间的中间结果直接存储在内存中,所以很快
4、MapReduce的最小运行单元task是进程,而Spark的最小运行单元task是线程。进程的启动销毁比线程慢。
Java
JDK、JRE、JVM区别
【JDK】:java开发工具,包含java开发所需的工具和JRE
【JRE】:java运行环境,包含java运行环境所需的核心类库和JVM
【JVM】:java虚拟机,保证java程序跨平台。
常量与变量
变量:在程序执行过程中,其值可以发生改变的量
格式: 数据类型 变量名 = 值;
int a =10;
常量:在程序的执行过程中, 其值不能发生改变的量.
格式: final 数据类型 变量名 = 值;
final String cardId = "1012200801010039";
final boolean flag = true;
分类 :
自定义常量:final修饰的,面向对象详解
字面值常量:
数据类型
数据类型的分类:1.基础数据类型2.引用数据类型
【基础数据类型】: 整数型:byte、short、int、long
浮点型:float、double
字符型:char
布尔型:boolean
【引用数据类型】:String、数组([])、接口(interface)、类(class)
byte类型的取值范围是: ***\*-128 ~ 127\****,char类型的取值范围是: ***\*0 ~ 65535\****
默认的整形是: int类型, 默认的浮点型(即: 小数类型)是: double类型.
定义long类型的数据时, 数据后边要加字母L(大小写均可), 建议加L
定义float类型的数据时, 数据后边要加字母F(大小写均可), 建议加F
定义double类型的数据时, 数据后边要加字母D(大小写均可), 建议加D
ASCii码表
a:97
A:65
0:48
标识符
标识符就是用来给变量, 常量,类, 接口, 方法,包等起名字的规则.
【命名规则】:
1. 标识符只能包含52个英文字母(区分大小写)、0 ~ 9数字 、$(美元符号)和_(下划线)。
2. 标识符不能以数字开头。
3. 标识符不能和Java中的关键字重名。
4. 最好做到见名知意.
【命名规范】:
1.对类定义 大驼峰命名法
2.对于变量 小驼峰命名法
3.对于常量 全部大写 每个单词之间使用_
4.对于包 全部小写每个单词之间使用 . 一般是公司的域名的反写 举例 cn.itcast.项目名.包名 cn.itheima.
数据类型转换
将两种不同数据类型的数据进行累加操作,因为数据类型不同,存储方式不同,需要将两种数据类型进行统一,数据类型转换。
数据类型转换的方式:(byte -> short -> int -> long -> float -> double)
1.自动(隐式)类型转换:指的是小类型转大类型,会自动提升为大类型,运算结果是大类型.
2.强制(显式)类型转换:指的是手动将大类型转换成小类型,运算结果是小类型.
public class VariableDemo08 {
public static void main(String[] args) {
//使用赋值运算符进行算数计算,会将大的类型自动转换成小的类型,不需要强制类型转换
short a = 1;
a += 2;
a = (short)(a+2);
System.out.println(a);
}
}
判断结构
if语句
单支格式:
if(判断语句){
执行语句;
}
双分支语句格式
if(关系表达式) { //if的意思: 如果
//语句体1;
} else { //否则...
//语句体2;
}
多分支格式
if(关系表达式1) {
//语句体1;
} else if(关系表达式2){
//语句体2;
} else if(关系表达式3){ //这里可以根据需求, 有多个else if语句
//语句体3;
} else {
//语句体n;
}
switch语句
switch 语句是判断语句,主要是判断固定值的
语句格式:
switch (表达式){
case 值1:
执行语句1;
break;
case 值2:
执行语句2;
break;
...
default:
执行语句n;
break;
}
循环结构
循环结构指的是,使一部分代码按照**次数**或一定的条件反复执行的一种代码结构。
循环结构的分类
1. for 循环
2. while 循环
3. do... while 循环
组成循环结构,哪些部分
1. 初始条件
2. 判断条件
3. 控制条件
4. 循环体
for循环
主要是针对固定循环次数的
格式:
for(初始化条件1; 判断条件2; 控制条件3){
循环体4;
}
while
使用非常广泛,常用于 不固定的循环次数的循环
格式:
初始化条件;
while(判断条件){
循环体;
控制条件;
}
do…while
用的不多,对于不固定的循环次数
格式
初始化条件;
do{
循环体;
控制条件;
} while(判断条件);
循环结构区别
【do...while循环和其他两个循环之间的区别】
do.while循环是先执行一次, 后判断
而其他两个循环都是先执行判断条件, 然后决定是否执行循环体.
【for循环和其他两个循环之间的区别】
for循环执行结束后, 初始化条件就不能继续使用了.
而其他两个循环do..while和while执行结束后, 初始化条件还可以继续使用.
死循环
格式:
for(;;){
循环体;
}
while(true){
循环体;
}
循环跳转
在循环的过程中,跳过当次的循环或者直接跳出当前循环分为两类:
continue:来结束本次循环,进行下一次循环的,循环还会继续执行
break:是用来终止循环的,循环不再继续执行
循环嵌套
循环结构中还包裹着循环结构,外边的循环结构叫外循环,里边的循环结构叫内循环。
数组
变量定义: 数据类型 变量名 = new 数据类型();
数组:存储相同的数据类型的值的容器
数据格式:
//动态定义数组
数据类型[] 数组名称 = new 数据类型[长度]; //推荐使用
数据类型 数组名称[] = new 数据类型[长度];
//静态定义数组
数据类型[] 数组名称 = new 数据类型[]{值1,值2,...};
数据类型[] 数组名称 = {值1,值2,...}; //推荐使用
数组的特点
1.数组的存储是顺序的,获取数据的下标(索引),默认索引位置是0
2.数组类型默认值
int类型默认值是0
String/char类型默认值null
boolean类型默认值是flase
double类型默认值是0.0d
数组基本用法
1. 如何获取数组的值 格式: 数组名称[索引]
如果数组名是 arr 获取第12个位置的数据 arr[11]
2. 如何对数组的进行赋值: 数组名称[索引]=值
int[] arr = new int[3] // 0,0,0
第二个值赋值为15
arr[1]=15;
3. 如何获取数组的长度呢: 数组名称.length
int[] arr = new int[3]; // 0,0,0
arr.length // 3
JVM内存划分
【栈】:存储局部变量以及所有代码执行的局部变量: 指的是定义在方法中,或者方法声明上的变量。
特点: 先进后出
【堆区】:
【方法区】:(代码区、静态区、常量池、本地方法区先进后出)
数组的常见操作
遍历数组
public class ArrayDemo07 {
public static void main(String[] args) {
//1.定义int类型的数组arr, 存储元素11, 22, 33, 44, 55.
int[] arr = {11, 22, 33, 44, 55};
//2.通过for循环, 遍历数组.
//2.1 增强 for 循环 //快捷键: foreach || iter || fori
for (int i : arr) {
System.out.println(i);
}
//2.2 使用 for 固定大小遍历 //快捷键 itar
for (int i = 0; i < arr.length; i++) {
int i1 = arr[i];
System.out.println(i1);
}
}
}
获取最大最小值
public class ArrayDemo08 {
public static void main(String[] args) {
//求出当前颜值最高的数字
int[] arr = {5, 15, 2000, 10000, 100, 4000};
//设置变量用于接收当前最大值的变量
int tmp = arr[0];
//遍历数组,比较最大值
/*for (int i = 0; i < arr.length; i++) {
int i1 = arr[i];
if (i1 > tmp) {
tmp = i1;
}
}*/
//先排序,取第一个或者最后一个
//冒泡排序,快速排序,堆排序,归并排序
Arrays.sort(arr);
for (int i : arr) {
System.out.println(i);
}
System.out.println("当前颜值最高的值: " + arr[arr.length - 1]);
}
}
反转数组元素
public class ArrayDemo09 {
public static void main(String[] args) {
int[] arr = {11, 33, 22, 55, 44, 66};
for (int i = 0; i <= (arr.length - 1) / 2; i++) {
int tmp = 0;
tmp = arr[i];
arr[i] = arr[arr.length - 1 - i];
arr[arr.length - 1 - i] = tmp;
}
for (int i : arr) {
System.out.println(i);
}
}
}
对数组排序
Arrays,sort(arr);
填充数组的内容
Arrays.fill(arr,2,3,200);
for(int i = 0; i < arr.length; i++){
int i1 = arr[i]; {
System.out.println(i1);
}
将数组转换成其他的类型,比如List,字符串等
# 转换成 字符串
Arrays.toString(arr);
# 转换成 列表
Arrays.asList(arr);
方法
方法简介:
概述:
方法指的是把一些具有独立功能的代码块整合为一个整体,使其成为一个具有特殊功能的代码集。
把Scanner,if,for,输出语句,数组 -> 求数组元素最大值。
格式:
修饰符 返回值的数据类型 方法名(数据类型 形参1,数据类型 形参2){
//具体的业务代码,就是你写的:变量,常量,if,for,switch,输出语句,运算法....
return 具体的返回值;
}
格式解释:
修饰符: 固定格式,目前先写public static
返回值的数据类型: 表示方法执行完毕后,需要返回一个什么类型的结果,如果方法无任何返回值,则返回值的数据类型要写成 void
方法名: 遵循小驼峰命名法,最好做到 见名知意。
参数列表: 表示调用方法的时候,需要给方法传入什么类型的值,例如:int a,int b
具体的业务代码: 就是你写的:变量,常量,if,for,switch,输出语句,运算法....
return 具体的返回值: 就是方法执行结束后,返回的 具体的 结果。
注意事项:
1.方法与方法之间是平级关系,不能嵌套定义。
2.方法只有被调用才会执行。
3.方法在调用之前,必须先定义,这个动作称为:定义方法。
4.定义方法的时候,写到参数列表中的内容叫:形式参数(简称:形参),形容调用方法,需要传入什么类型的值。
5.调用方法的时候,传入的值叫:实际餐数(简称:实参)既:实际参与运算的数据。
6.方法的功能越单一越好。
使用方法的核心6步:
定义方法时的三个明确:
1.明确方法名,做到见名知意
2.明确参数列表,即:调用方法,需要传入什么类型的值
3.明确返回值的数据类型,即:方法执行结束后,返回一个具体的什么类型的结果。
调用方法时的三个步骤:
1.写 方法名();
2.传参,方法要什么类型的参数,我们就传入什么类型的值。
3.接收返回值,方法返回什么类型的结果,我们就用对应类型的变量来接收。
无参无返回值的方法
public class Demo02{
public static void main(String[] args){
//2.调用方法printHello()
printHello();
}
//1.定义方法 printHello()
public static void printHello(){
System.out.println("Hello World!");
}
}
有参无返回值的方法
public class Demo03{
public static void main(String[] args){
isEvenNumber(876);
}
public staic void isEvenNumber(int num){
if(num % 2 == 0 )
System.out.println("偶数");
else
System.out.println("奇数");
System.out.println("----------------------------");
System.out.println(num % 2 ==0 ? true : false);
}
}
Python
输出
整数:%d,%06d
字符串:%s
小数:%.2f
student_no = 1
age = 18
name = "小明"
height = 1.78
# print("格式化字符串" % (变量1, 变量2, ...))
# print("格式化字符串" % 变量)
print("我是学号为%06d的%s,年龄为%d,身高为%.2f米" % (student_no, name, age, height))
输入 input
输入后得到内容的类型是`字符串`
用户输入的任何内容python都认为是一个 字符串
语法:
字符串类型变量 = input("提示信息:")
示例代码:
password = input("请输入密码:")
print('您刚刚输入的密码是:%s' % password)
类型转换
int(x) # 把x转换成整形
float(x) # 把x转换成浮点型
str(x) # 把x转成字符串