详解大中小数据常用数据库的SQL语句、函数以及常见优化

本篇文章主要介绍目前我在目前项目中使用过的一些数据库的用法,像中小型数据常用的关系型数据库Sql Server,大数据常用的数据库Hive,Hbase。


SQL 你必须知道的

sql执行顺序:
from ,where,group by,having,select,over,order by,limit


SQL网站

菜鸟教程:https://www.runoob.com/sql/func-date-sub.html
w3school教程:https://www.jb51.net/w3school/sql/index.asp.html
json转sql:http://sql.yupi.icu/


关系型数据库

这里以Sql Server为例,Mysql类似。如果没有可以参考来找相应的函数。

B列值为0则设置为1,值为1则不变。 C列等于A/C
select A,(case when B = 0 then 1 else ... end) as B ,A/C as C from table
case  when .. then ... when .. then ... else .. end
如果为空就.... ISNULL
日期加减,相差天数 dateadd,datediff
创建表之前删除表:
DROP TABLE table
CREATE TABLE table(...)
强制转换数据类型:
cast(date as datetype)
创建表:
复制表(含数据): create table table_name2 as select * from table_name1

复制表(不含数据): create table table_name2 as select * from table_name1 where 1=2

只复制表数据:insert into table_name2 select * from table_name1

指定字段插入:
insert into table_name(......) select .......from.....

sql server创建表并插入数据:
select * into newtable from oldtable where...

SQL 函数

常用数学函数包括如下:

avg函数:计算查询中某一特定字段资料的算术平均值
count函数:计算符合查询条件的记录数。
min, max函数:传回指定字段之中符合查询条件的第一条、最末条记录的资料。
first, last函数:传回指定字段之中符合查询条件的最小值、最大值。
stdev函数:计算指定字段之中符合查询条件的标准差
sum函数:计算指定字段之中符合查询条件的资料总和
var,函数:计算指定字段之中符合查询条件的变异数估计值。
计算变异系数:SELECT (case Avg(1) when 0 then 0 else Stdev(1) end)/(case Avg(1) when 0 then 1 else Avg(1) end)
多列转多行可以使用union。如果是hive也可以使用explode函数,(列转行)
eg:
(select id,name,'数学' as type,math as sore from student)
union 
(select id,name,'英语' as type,english as sore from student)
union 
(select id,name,'语文' as type,chinese as sore from student)
日期函数:
关于获取时间戳绝具体的属性可以=》具体可以=》https://www.cnblogs.com/lxc89/p/5962978.html
data_name,year,month,day,week,quarter,fweek
2021-11-01,2021,11,1,星期一,4,45周
得到当前时间:
now()getdate()
获取年,,,,小时:
year(),month(),dayofweek(),day(),hour()
也可以使用格式化的方法:
data_format(data,"%d")
或者使用datename(year/month/day/dw/week/hour/minute/second,getdate())...
函数 参数/功能
GetDate( ) 返回系统目前的日期与时间
DateDiff (interval,date1,date2) 以interval 指定的方式,返回date2 与date1两个日期之间的差值 date2-date1
DateAdd (interval,number,date) 以interval指定的方式,加上number之后的日期
DatePart (interval,date) 返回日期date中,interval指定部分所对应的整数值
DateName (interval,date) 返回日期date中,interval指定部分所对应的字符串名称
参数 interval的设定值如下:
值 缩 写(Sql Server) 说明
Year Yy 年 1753 ~ 9999
Quarter Qq 季 1 ~ 4
Month Mm 月1 ~ 12
Day of year Dy 一年的日数,一年中的第几日 1-366
Day Dd 日,1-31
Weekday Dw 一周的日数,一周中的第几日 1-7
Week Wk 周,一年中的第几周 0 ~ 51
Hour Hh 时0 ~ 23
Minute Mi 分钟0 ~ 59
Second Ss 秒 0 ~ 59
Millisecond Ms 毫秒 0 ~ 999

SQL 错误梳理

一.不能为表‘XXX‘ 中的标识列插入显式值:
1.先运行“SET IDENTITY_INSERT 表名 ON”
2.再运行你的插入语句
3.最后执行“SET IDENTITY_INSERT 表名 OFF”

二.误删除了查询语句,sql查询某段时间查询的历史记录:

SELECT     TOP 1000 QS.creation_time, SUBSTRING(ST.text, (QS.statement_start_offset / 2) + 1,

                      ((CASE QS.statement_end_offset WHEN - 1 THEN DATALENGTH(st.text) ELSE QS.statement_end_offset END - QS.statement_start_offset) / 2) + 1)

                      AS statement_text, ST.text, QS.total_worker_time, QS.last_worker_time, QS.max_worker_time, QS.min_worker_time

FROM         sys.dm_exec_query_stats QS CROSS APPLY sys.dm_exec_sql_text(QS.sql_handle) ST

WHERE     QS.creation_time BETWEEN '2022-05-12 15:30:00' AND '2022-05-12 15:42:00' AND ST.text LIKE '%%'

ORDER BY QS.creation_time DESC

大数据常用数据库

Hive

Hive SQL

一.hive查询表结构:
desc …

二.hive创建索引:
Hive索引的使用过程比较繁琐:
每次查询时候都要先用一个job扫描索引表,如果索引列的值非常稀疏,那么索引表本身也会非常大;
索引表不会自动rebuild,如果表有数据新增或删除,那么必须手动rebuild索引表数据;
索引表:
https://www.cnblogs.com/zlslch/p/6105294.html
https://blog.csdn.net/i000zheng/article/details/80435610
hive索引:
https://www.cnblogs.com/pengpenghuhu/p/14465830.html
使用布隆过滤器添加索引:

CREATE TABLE lxw1234_orc2 (.......)
stored AS ORC
TBLPROPERTIES
(
'orc.compress'='SNAPPY',
'orc.create.index'='true',
-- pcid字段开启BloomFilter索引
"orc.bloom.filter.columns"="pcid"
)
AS
SELECT CAST (siteid AS INT) AS id,
pcid
FROM lxw1234_text
DISTRIBUTE BY id sort BY id;

三.hive分桶,分区:
分区:Hive在查询数据的时候,一般会扫描整个表的数据,会消耗很多不必要的时间。有些时候,我们只需要关心一部分数据,比如WHERE子句的查询条件,那这时候这种全表扫描的方式是很影响性能的。从而引入了分区的概念。分区就是对数据进行分类,这样在查询的时候,就可以只是针对分区查询,从而不必全表扫描。
一个目录对应一个分区
分桶:并非所有的数据集都可形成合理的分区,特别之前所提到过的要确定合适的划分大小的疑虑。对于每一个表或者分区,可以进一步细分成桶,桶是对数据进行更细粒度的划分。Hive默认采用对某一列的每个数据进行hash(哈希),使用hashcode对 桶的个数求余,确定该条记录放入哪个桶中。
分桶实际上和 MapReduce中的分区是一样的。分桶数和reduce数对应。

hive动态分区插入数据:
https://wenku.baidu.com/view/257247e15cbfc77da26925c52cc58bd631869392.html

四.清空表数据:
truncate table tablename;//用于清空管理表
delete from tablename where …;
外部表,即使删除表,数据仍然存在与hdfs中,所以只能删除hdfs上的数据后重建才能完全删除数据。

五.变量设置:
https://wenku.baidu.com/view/9f7712e9a2c7aa00b52acfc789eb172ded63997e.html

六.Hive SQL编译的执行顺序

(1) 将SQL转化为抽象语法树AST Tree

(2) 遍历AST Tree,抽象出查询的基本组成单元QueryBlock

(3) 遍历QueryBlock,翻译为执行操作树OperatorTree

(4) 遍历OperatorTree,翻译为MapReduce任务

(5) 逻辑层优化器进行OperatorTree变换,合并不必要的ReduceSinkOperator,减少shuffle数据量

(6) 物理层优化器进行MapReduce任务的变换,生成执行计划

Hive 函数大全

一.over窗口函数

over(partition by…order by …)
1.
https://www.csdn.net/tags/MtTaMg1sMDU5Mzk1LWJsb2cO0O0O.html
2.
https://blog.csdn.net/qq_45765882/article/details/119999529?utm_medium=distribute.pc_aggpage_search_result.none-task-blog2~ aggregatepage~ first_rank_ecpm_v1~rank_v31_ecpm-3-119999529-null-null.pc_agg_new_rank&utm_term=hive%E5%BC%80%E7%AA%97%E8%AE%A1%E7%AE%97%E5%B7%AE%E5%80%BC&spm=1000.2123.3001.4430
3.
https://wangpei.blog.csdn.net/article/details/81437574?spm=1001.2101.3001.6650.7&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-7-81437574-blog-119999529.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-7-81437574-blog-119999529.pc_relevant_default&utm_relevant_index=14

二.非空判断函数:
coalesce函数,也可以使用if,但是如果我们想要使用非空的值,if可能会涉及到重复计算。一次可以判断多个,直到数据非空为止
NVL函数,如果第一个值为空就取第二个
https://blog.csdn.net/godlovedaniel/article/details/122301768?spm=1001.2014.3001.5502

三.内置函数:
https://blog.csdn.net/m0_58371965/article/details/122168883

四.列转行,行转列函数:
这个函数是指将多行的这个字段数据合并到一列中,或者将1列中一个数据转为多行这个字段数据
(行转列,实际是一行中包括数据部分和他对应的项目名称,我们进行转换的时候将项目名称单独使用多个列指代,数据就是原来的数据部分)
https://blog.csdn.net/weixin_42679482/article/details/120422007
如果要实现多个列字段变为多行数据就需要使用union函数(实际就是将每个每一列的结果与其他项单独提取出来,然后再组合到一起UNION)

select datediff(TestTime,'1970-01-01'),ID,Type,1 as taskid,
ods_testresults_inc.Emptyup_minWorkH as taskresult from ods_testresults_inc)
union
(select datediff(TestTime,'1970-01-01'),ID,Type,2 as taskid,
ods_testresults_inc.Emptyup_maxWorkH as taskresult from ods_testresults_inc

或者使用lateral view explode():
https://blog.csdn.net/whoami19871226/article/details/117783426?spm=1001.2101.3001.6650.6&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-6-117783426-blog-120422007.pc_relevant_antiscanv2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-6-117783426-blog-120422007.pc_relevant_antiscanv2&utm_relevant_index=9

五.日期函数
hive日期:
datediff,dateadd,datesub…
https://blog.csdn.net/qq_44173974/article/details/113829168

https://zhuanlan.zhihu.com/p/465783758

https://wenku.baidu.com/view/550d9e364731b90d6c85ec3a87c24028915f856e.html

https://www.cnblogs.com/w-j-q/p/14774876.html


Hive 调优

hive常用调优:
https://baijiahao.baidu.com/s?id=1714643208535989978&wfr=spider&for=pc

开启FetchTask

一个简单的查询语句,是指一个没有函数、排序等功能的语句,当开启一个Fetch Task功能,就执行一个简单的查询语句不会生成MapRreduce作业,而是直接使用FetchTask,从hdfs文件系统中进行查询输出数据,从而提高效率。

设置的方式:

Hive.fetch.task.conversion 默认为minimal
 
修改配置文件hive-site.xml
<property>
  <name>hive.fetch.task.conversion</name>
  <value>more</value>
  <description>
    Some select queries can be converted to single FETCH task 
    minimizing latency.Currently the query should be single 
    sourced not having any subquery and should not have
    any aggregations or distincts (which incurrs RS), 
    lateral views and joins.
    1. minimal : SELECT STAR, FILTER on partition columns, LIMIT only
    2. more    : SELECT, FILTER, LIMIT only (+TABLESAMPLE, virtual columns)
  </description>
</property> 
 
 
或者当前session修改
hive> set hive.fetch.task.conversion=more;
执行SELECT id, money FROM m limit 10; 不走mr

常用的hive优化流程:
https://baijiahao.baidu.com/s?id=1714643208535989978&wfr=spider&for=pc

https://blog.csdn.net/m0_58371965/article/details/122253984?utm_term=hive%E4%BC%98%E5%8C%96%E6%9F%A5%E8%AF%A2%E9%80%9F%E5%BA%A6&utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2allsobaiduweb~default-0-122253984-null-null&spm=3001.4430

补充
hive定时脚本

https://www.cnblogs.com/cstark/p/15075905.html

数仓:sqoop-数据库导入hive,hive导出到数据库

https://blog.csdn.net/godlovedaniel/article/details/108884300

使用Hive做多维分析

可以通过多维分析数据构建数据立方体
hive高阶多维聚合函数应用:
https://blog.csdn.net/godlovedaniel/article/details/124589490?spm=1001.2014.3001.5501

Hbase

Hbase SQL

建表
直接建表:

create ‘表名’,‘列族名'

如果之后想加入新的列族:

alter ‘表名’,‘列族名’

补充:

alter ‘表名’,{NAME=‘列族名’,属性1=>属性值 1,属性2=>属性值2}{NAME=>‘列族名2’ ,属性 1=>属性值1.....}

如果是不存在的列族,就是添加,如果是已存在的列族,就是修改属性.(eg:version,bloofilter,ttl…)
其中Alter 允许的列族级别属性 BLOOMFILTER, COMPRESSION ,TTL, BLOCKSIZE, BLOCKCACH …

如果建表时想加入一些设置:

create ‘表名’,{NAME=>‘列族名’,VERSIONS=>‘’,BLOCKCACHE=>‘’, BLOOMFILTER=>‘’,COMPRESSION=>‘’,TTL=>‘’},{NAME=>’列族名’, 属性=>属性值........}

bloomfilter是设置布隆过滤器(一般是none,可以设置为row,rowcol),compression是设置数据压缩方式,TTL是设置数据过期时间
查看表的属性:

describe '表名‘

删除表:

Disable '表名‘
drop ‘表名‘

删除列族:

alter‘表名’,delete=>’列族名’

判断表是否存在:

exists '表名‘

为某列族的某列数据拼接后缀数据:

Append ‘表名’,‘行键’,‘列名’,‘拼接值’

删除某个列族的某个字段数据:

delete ‘表名’,‘行键’,‘列族:列’

插入数据put使用
put只能每次插入一个行键对应的一个列族的一个列属性的值,不能直接插入多条数据,区别mysql的一条语句查询。
eg:

put  'student','001','grade:Database',92
put  'student','001','grade:Math',98
put 'student','003','doom:phone',1110989009

hbase中查看所有存在的表,使用list进行查询
hbase所有属性:columns,startrow,endrow,timerange,versions,fliter,limit
get
get是直接使用的行键进行查询是索引查找,速度最快
无论是get还是scan都使用[ ]来查询多个列族字段的数据或者查询时间戳范围数据,如columns=>[‘info:name’,‘info:sno’,‘grade:Database’],如果只是一个列族的单字段数据可以不使用[ ]
eg:
versions=>3表示取过去3个版本的数据,但是前提是需要先设置当前字段有三个版本

get 'student','001',{COLUMNS=>['info:name','info:sno'],TIMERANGE=>[1639034014464,1639034055161],VERSIONS=>3}

scan
scan是基于范围查找,另外Scan命令若不指定显示的版本数,则显示拥有最大(最 新)时间戳的数据版本。
直接查看表中的前10行数据:

scan 'student'{LIMIT=>10}

更广用法:
eg:

scan 'student',{COLUMNS=>['info:name' ,'info:dept' ], STARTROW=>'002',STOPROW=>'005',TIMERANGE=>[1639034014464,1639034055161],VERSIONS=>3}

如果想要显示所有版本数据就必须加上versions

Hbase 布隆过滤器

查找形式多样,支持多种形式的查找
RowFilter
行键过滤器
查询student表中rowkey中包含123的数据。

scan 'student', {FILTER=> "RowFilter(=,'substring:123')"}

ValueFilter
值过滤器
查询student表中值包含123的数据,因为hbase在存储的时候是以key-value格式的列式存储,在查询遍历的时候每行每列都会遍历,所以才有这个过滤器.
除了可以查询等于,也可以查询大于、大于等于、小于、小于等于、不等于,比较的方式是字典排序。
除非在数据存储的时候存储的类型为数字类型。
substring:包含的意思、binary:精确查询、regexstring:正则匹配、null:空值比较、long:数字比较

scan 'student',{FILTER=> "ValueFilter(=,'substring:123')"}

SingleColumnValueFilter
单列值过滤器
查询student表中列簇为info.列名为server的所有数据。
注意:在使用单列值过滤器必须保证每行数据都有要查询的列,因为查询结果也会返回没有要查询的列的整行记录。

scan 'student',{FILTER=>"SingleColumnValueFilter('info','server',=,'binary:chds-2:60020')"}

PrefixFilter
行键前缀过滤器
查询student表中行键以123开头的所有数据

scan 'student',{FILTER=>"PrefixFilter('123')"}

ColumnPrefixFilter
单列名前缀过滤器
查询student表中以server开头的列的所有数据

scan 'student',{FILTER=>"ColumnPrefixFilter('server')"}

MultipleColumnPrefixFilter
多列名前缀过滤器
查询student表中以server和regioninfo开头的列的所有数据

scan 'student',{FILTER=>"MultipleColumnPrefixFilter('servedr','regioninfo')"}

PageFilter
分页过滤器
分页过滤器要和startRow搭配使用,和LIMIT功能类似。
过滤器搭配使用的时候分页过滤器要放在最后。
但是如果是页面开发使用java api操作,分页过滤器有一个缺点就是必须一页一页翻页,因为要传递startRow的值,和ES的深度分页类似

scan 'student',{FILTER=>"PageFilter(2)",STARTROW=>''}
scan 'student',{LIMIT=>2,STARTROW=>''}

InclusiveStopFilter
包含结尾过滤器
endRow在使用过程中查询结果不包括endRow,可以搭配使用这个过滤器。

scan 'student',{STARTROW=>'',ENDROW=>'',FILTER=>"InclusiveStopFilter('')"}

FamilyFilter
列簇过滤器

scan 'student',{FILTER=>"FamilyFilter(=,'binary:info')"}

QualifierFilter
列名过滤器

scan 'student',{FILTER=>"QualifierFilter(=,'binary:server')"}

SingleColumnValueExcludeFilter
单列值不包含过滤器,和单列值过滤器类似

scan 'student',{FILTER=>"SingleColumnValueExcludeFilter('info','server',=,'binary:13270')
Hbase 优化

https://baijiahao.baidu.com/s?id=1732318312901581970&wfr=spider&for=pc
一、HBase 读优化

  1. HBase客户端优化
    和大多数系统一样,客户端作为业务读写的入口,姿势使用不正确通常会导致本业务读延迟较高实际上存在一些使用姿势的推荐用法,这里一般需要关注四个问题:
    1. scan缓存是否设置合理?
      优化原理:在解释这个问题之前,首先需要解释什么是scan缓存,通常来讲一次scan会返回大量数据,因此客户端发起一次scan请求,实际并不会一次就将所有数据加载到本地,而是分成多次RPC请求进行加载,这样设计一方面是因为大量数据请求可能会导致网络带宽严重消耗进而影响其他业务,另一方面也有可能因为数据量太大导致本地客户端发生OOM。在这样的设计体系下用户会首先加载一部分数据到本地,然后遍历处理,再加载下一部分数据到本地处理,如此往复,直至所有数据都加载完成。数据加载到本地就存放在scan缓存中,默认100条数据大小。
      通常情况下,默认的scan缓存设置就可以正常工作的。但是在一些大scan(一次scan可能需要查询几万甚至几十万行数据)来说,每次请求100条数据意味着一次scan需要几百甚至几千次RPC请求,这种交互的代价无疑是很大的。因此可以考虑将scan缓存设置增大,比如设为500或者1000就可能更加合适。笔者之前做过一次试验,在一次scan扫描10w+条数据量的条件下,将scan缓存从100增加到1000,可以有效降低scan请求的总体延迟,延迟基本降低了25%左右。
      优化建议:大scan场景下将scan缓存从100增大到500或者1000,用以减少RPC次数
    2. get请求是否可以使用批量请求?
      优化原理:HBase分别提供了单条get以及批量get的API接口,使用批量get接口可以减少客户端到RegionServer之间的RPC连接数,提高读取性能。另外需要注意的是,批量get请求要么成功返回所有请求数据,要么抛出异常。
      优化建议:使用批量get进行读取请求
    3. 请求是否可以显示指定列族或者列?
      优化原理:HBase是典型的列族数据库,意味着同一列族的数据存储在一起,不同列族的数据分开存储在不同的目录下。如果一个表有多个列族,只是根据Rowkey而不指定列族进行检索的话不同列族的数据需要独立进行检索,性能必然会比指定列族的查询差很多,很多情况下甚至会有2倍~3倍的性能损失。
      优化建议:可以指定列族或者列进行精确查找的尽量指定查找
    4. 离线批量读取请求是否设置禁止缓存?
      优化原理:通常离线批量读取数据会进行一次性全表扫描,一方面数据量很大,另一方面请求只会执行一次。这种场景下如果使用scan默认设置,就会将数据从HDFS加载出来之后放到缓存。可想而知,大量数据进入缓存必将其他实时业务热点数据挤出,其他业务不得不从HDFS加载,进而会造成明显的读延迟毛刺
      优化建议:离线批量读取请求设置禁用缓存,scan.setBlockCache(false)
  2. HBase服务器端优化
    一般服务端端问题一旦导致业务读请求延迟较大的话,通常是集群级别的,即整个集群的业务都会反映读延迟较大。可以从4个方面入手:
    1. 读请求是否均衡?
      优化原理:极端情况下假如所有的读请求都落在一台RegionServer的某几个Region上,这一方面不能发挥整个集群的并发处理能力,另一方面势必造成此台RegionServer资源严重消耗(比如IO耗尽、handler耗尽等),落在该台RegionServer上的其他业务会因此受到很大的波及。可见,读请求不均衡不仅会造成本身业务性能很差,还会严重影响其他业务。当然,写请求不均衡也会造成类似的问题,可见负载不均衡是HBase的大忌。
      观察确认:观察所有RegionServer的读请求QPS曲线,确认是否存在读请求不均衡现象
      优化建议:RowKey必须进行散列化处理(比如MD5散列),同时建表必须进行预分区处理
    2. BlockCache是否设置合理?
      优化原理:BlockCache作为读缓存,对于读性能来说至关重要。默认情况下BlockCache和Memstore的配置相对比较均衡(各占40%),可以根据集群业务进行修正,比如读多写少业务可以将BlockCache占比调大。另一方面,BlockCache的策略选择也很重要,不同策略对读性能来说影响并不是很大,但是对GC的影响却相当显著,尤其BucketCache的offheap模式下GC表现很优越。另外,HBase 2.0对offheap的改造(HBASE-11425)将会使HBase的读性能得到2~4倍的提升,同时GC表现会更好!
      观察确认:观察所有RegionServer的缓存未命中率、配置文件相关配置项一级GC日志,确认BlockCache是否可以优化
      优化建议:JVM内存配置量 < 20G,BlockCache策略选择LRUBlockCache;否则选择BucketCache策略的offheap模式;期待HBase 2.0的到来!
    3. HFile文件是否太多?
      优化原理:HBase读取数据通常首先会到Memstore和BlockCache中检索(读取最近写入数据&热点数据),如果查找不到就会到文件中检索。HBase的类LSM结构会导致每个store包含多数HFile文件,文件越多,检索所需的IO次数必然越多,读取延迟也就越高。文件数量通常取决于Compaction的执行策略,一般和两个配置参数有关:
      hbase.hstore.compactionThreshold
      hbase.hstore.compaction.max.size
      前者表示一个store中的文件数超过多少就应该进行合并,后者表示参数合并的文件大小最大是多少,超过此大小的文件不能参与合并。这两个参数不能设置太’松’(前者不能设置太大,后者不能设置太小),导致Compaction合并文件的实际效果不明显,进而很多文件得不到合并。这样就会导致HFile文件数变多。
      观察确认:观察RegionServer级别以及Region级别的storefile数,确认HFile文件是否过多
      优化建议:hbase.hstore.compactionThreshold设置不能太大,默认是3个;设置需要根据Region大小确定,通常可以简单的认为 hbase.hstore.compaction.max.size = RegionSize / hbase.hstore.compactionThreshold
    4. Compaction是否消耗系统资源过多?
      优化原理:Compaction是将小文件合并为大文件,提高后续业务随机读性能,但是也会带来IO放大以及带宽消耗问题(数据远程读取以及三副本写入都会消耗系统带宽)。正常配置情况下Minor Compaction并不会带来很大的系统资源消耗,除非因为配置不合理导致Minor Compaction太过频繁,或者Region设置太大情况下发生Major Compaction。
      观察确认:观察系统IO资源以及带宽资源使用情况,再观察Compaction队列长度,确认是否由于Compaction导致系统资源消耗过多
      优化建议:
      Minor Compaction设置:hbase.hstore.compactionThreshold设置不能太小,又不能设置太大,因此建议设置为5~6;hbase.hstore.compaction.max.size = RegionSize / hbase.hstore.compactionThreshold
      Major Compaction设置:大Region读延迟敏感业务( 100G以上)通常不建议开启自动Major Compaction,手动低峰期触发。小Region或者延迟不敏感业务可以开启Major Compaction,但建议限制流量;
      期待更多的优秀Compaction策略,类似于stripe-compaction尽早提供稳定服务
  3. HBase列族设计优化
    HBase列族设计对读性能影响也至关重要,其特点是只影响单个业务,并不会对整个集群产生太大影响。列族设计主要从以下方面检查:
    1. Bloomfilter是否设置?是否设置合理?
      优化原理:Bloomfilter主要用来过滤不存在待检索RowKey或者Row-Col的HFile文件,避免无用的IO操作。它会告诉你在这个HFile文件中是否可能存在待检索的KV,如果不存在,就可以不用消耗IO打开文件进行seek。很显然,通过设置Bloomfilter可以提升随机读写的性能。
      Bloomfilter取值有两个,row以及rowcol,需要根据业务来确定具体使用哪种。如果业务大多数随机查询仅仅使用row作为查询条件,Bloomfilter一定要设置为row,否则如果大多数随机查询使用row+cf作为查询条件,Bloomfilter需要设置为rowcol。如果不确定业务查询类型,设置为row。
      优化建议:任何业务都应该设置Bloomfilter,通常设置为row就可以,除非确认业务随机查询类型为row+cf,可以设置为rowcol
  4. HDFS相关优化
    HDFS作为HBase最终数据存储系统,通常会使用三副本策略存储HBase数据文件以及日志文件。从HDFS的角度望上层看,HBase即是它的客户端,HBase通过调用它的客户端进行数据读写操作,因此HDFS的相关优化也会影响HBase的读写性能。这里主要关注如下三个方面:
    1. Short-Circuit Local Read功能是否开启?
      优化原理:当前HDFS读取数据都需要经过DataNode,客户端会向DataNode发送读取数据的请求,DataNode接受到请求之后从硬盘中将文件读出来,再通过TPC发送给客户端。Short Circuit策略允许客户端绕过DataNode直接读取本地数据。(具体原理参考此处)
      优化建议:开启Short Circuit Local Read功能,具体配置戳这里
    2. Hedged Read功能是否开启?
      优化原理:HBase数据在HDFS中一般都会存储三份,而且优先会通过Short-Circuit Local Read功能尝试本地读。但是在某些特殊情况下,有可能会出现因为磁盘问题或者网络问题引起的短时间本地读取失败,为了应对这类问题,社区开发者提出了补偿重试机制 – Hedged Read。该机制基本工作原理为:客户端发起一个本地读,一旦一段时间之后还没有返回,客户端将会向其他DataNode发送相同数据的请求。哪一个请求先返回,另一个就会被丢弃。
      优化建议:开启Hedged Read功能,具体配置参考这里
    3. 数据本地率是否太低?
      数据本地率:HDFS数据通常存储三份,假如当前RegionA处于Node1上,数据a写入的时候三副本为(Node1,Node2,Node3),数据b写入三副本是(Node1,Node4,Node5),数据c写入三副本(Node1,Node3,Node5),可以看出来所有数据写入本地Node1肯定会写一份,数据都在本地可以读到,因此数据本地率是100%。现在假设RegionA被迁移到了Node2上,只有数据a在该节点上,其他数据(b和c)读取只能远程跨节点读,本地率就为33%(假设a,b和c的数据大小相同)。
      优化原理:数据本地率太低很显然会产生大量的跨网络IO请求,必然会导致读请求延迟较高,因此提高数据本地率可以有效优化随机读性能。数据本地率低的原因一般是因为Region迁移(自动balance开启、RegionServer宕机迁移、手动迁移等),因此一方面可以通过避免Region无故迁移来保持数据本地率,另一方面如果数据本地率很低,也可以通过执行major_compact提升数据本地率到100%。
      优化建议:避免Region无故迁移,比如关闭自动balance、RS宕机及时拉起并迁回飘走的Region等;在业务低峰期执行major_compact提升数据本地率
  5. HBase读性能优化归纳
    在本文开始的时候提到读延迟较大无非三种常见的表象,单个业务慢、集群随机读慢以及某个业务随机读之后其他业务受到影响导致随机读延迟很大。了解完常见的可能导致读延迟较大的一些问题之后,我们将这些问题进行如下归类,读者可以在看到现象之后在对应的问题列表中进行具体定位:
    在这里插入图片描述

二、HBase 写优化
和读相比,HBase写数据流程倒是显得很简单:数据先顺序写入HLog,再写入对应的缓存Memstore,当Memstore中数据大小达到一定阈值(128M)之后,系统会异步将Memstore中数据flush到HDFS形成小文件。
HBase数据写入通常会遇到两类问题,一类是写性能较差,另一类是数据根本写不进去。这两类问题的切入点也不尽相同:

1.写性能优化切入点

1)是否需要写WAL?WAL是否需要同步写入?
优化原理:数据写入流程可以理解为一次顺序写WAL+一次写缓存,通常情况下写缓存延迟很低,因此提升写性能就只能从WAL入手。WAL机制一方面是为了确保数据即使写入缓存丢失也可以恢复,另一方面是为了集群之间异步复制。默认WAL机制开启且使用同步机制写入WAL。首先考虑业务是否需要写WAL,通常情况下大多数业务都会开启WAL机制(默认),但是对于部分业务可能并不特别关心异常情况下部分数据的丢失,而更关心数据写入吞吐量,比如某些推荐业务,这类业务即使丢失一部分用户行为数据可能对推荐结果并不构成很大影响,但是对于写入吞吐量要求很高,不能造成数据队列阻塞。这种场景下可以考虑关闭WAL写入,写入吞吐量可以提升2x~3x。退而求其次,有些业务不能接受不写WAL,但可以接受WAL异步写入,也是可以考虑优化的,通常也会带来1x~2x的性能提升。
优化推荐:根据业务关注点在WAL机制与写入吞吐量之间做出选择
其他注意点:对于使用Increment操作的业务,WAL可以设置关闭,也可以设置异步写入,方法同Put类似。相信大多数Increment操作业务对WAL可能都不是那么敏感~

2) Put是否可以同步批量提交?
优化原理:HBase分别提供了单条put以及批量put的API接口,使用批量put接口可以减少客户端到RegionServer之间的RPC连接数,提高写入性能。另外需要注意的是,批量put请求要么全部成功返回,要么抛出异常。
优化建议:使用批量put进行写入请求

3) Put是否可以异步批量提交?
优化原理:业务如果可以接受异常情况下少量数据丢失的话,还可以使用异步批量提交的方式提交请求。提交分为两阶段执行:用户提交写请求之后,数据会写入客户端缓存,并返回用户写入成功;当客户端缓存达到阈值(默认2M)之后批量提交给RegionServer。需要注意的是,在某些情况下客户端异常的情况下缓存数据有可能丢失。
优化建议:在业务可以接受的情况下开启异步批量提交
使用方式:setAutoFlush(false)

4) Region是否太少?
优化原理:当前集群中表的Region个数如果小于RegionServer个数,即Num(Region of Table) < Num(RegionServer),可以考虑切分Region并尽可能分布到不同RegionServer来提高系统请求并发度,如果Num(Region of Table) > Num(RegionServer),再增加Region个数效果并不明显。
优化建议:在Num(Region of Table) < Num(RegionServer)的场景下切分部分请求负载高的Region并迁移到其他RegionServer;

5) 写入请求是否不均衡?
优化原理:另一个需要考虑的问题是写入请求是否均衡,如果不均衡,一方面会导致系统并发度较低,另一方面也有可能造成部分节点负载很高,进而影响其他业务。分布式系统中特别害怕一个节点负载很高的情况,一个节点负载很高可能会拖慢整个集群,这是因为很多业务会使用Mutli批量提交读写请求,一旦其中一部分请求落到该节点无法得到及时响应,就会导致整个批量请求超时。因此不怕节点宕掉,就怕节点奄奄一息!
优化建议:检查RowKey设计以及预分区策略,保证写入请求均衡。

6) 写入KeyValue数据是否太大?
KeyValue大小对写入性能的影响巨大,一旦遇到写入性能比较差的情况,需要考虑是否由于写入KeyValue数据太大导致。

KeyValue较大可能导致的严重问题,eg:因为大字段业务写入导致其他业务吞吐量急剧下降。因为大字段业务scan导致RegionServer宕机。

  1. 写异常问题检查点
    上述几点主要针对写性能优化进行了介绍,除此之外,在一些情况下还会出现写异常,一旦发生需要考虑下面两种情况(GC引起的不做介绍):
    Memstore设置是否会触发Region级别或者RegionServer级别flush操作?
    问题解析:以RegionServer级别flush进行解析,HBase设定一旦整个RegionServer上所有Memstore占用内存大小总和大于配置文件中upperlimit时,系统就会执行RegionServer级别flush,flush算法会首先按照Region大小进行排序,再按照该顺序依次进行flush,直至总Memstore大小低至lowerlimit。这种flush通常会block较长时间,在日志中会发现“Memstore is above high water mark and block 7452 ms”,表示这次flush将会阻塞7s左右。
    问题检查点:
    Region规模与Memstore总大小设置是否合理?如果RegionServer上Region较多,而Memstore总大小设置的很小(JVM设置较小或者upper.limit设置较小),就会触发RegionServer级别flush。
    列族是否设置过多,通常情况下表列族建议设置在1~3个之间,最好一个。如果设置过多,会导致一个Region中包含很多Memstore,导致更容易触到高水位upperlimit
    Store中HFile数量是否大于配置参数blockingStoreFile?
    问题解析:对于数据写入很快的集群,还需要特别关注一个参数:hbase.hstore.blockingStoreFiles,此参数表示如果当前hstore中文件数大于该值,系统将会强制执行compaction操作进行文件合并,合并的过程会阻塞整个hstore的写入。通常情况下该场景发生在数据写入很快的情况下,在日志中可以发现”Waited 3722ms on a compaction to clean up ‘too many store files“
    问题检查点:
    参数设置是否合理?hbase.hstore.compactionThreshold表示启动compaction的最低阈值,该值不能太大,否则会积累太多文件,一般建议设置为5~8左右。hbase.hstore.blockingStoreFiles默认设置为7,可以适当调大一些。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
数据库中的split函数常用于将一个字符串按照指定的分隔符拆分成多个子字符串,并将这些子字符串以数组的形式返回。它的语法通常如下: ``` split(string, delimiter) ``` 其中,string表示要被拆分的字符串,delimiter表示分隔符。该函数的返回值是一个数组,包含被分隔后的所有子字符串。 以下是一些常见数据库中split函数的用法: 1. MySQL中的split函数: MySQL中没有原生的split函数,但可以使用SUBSTRING_INDEX函数来实现类似的功能。例如: ``` SELECT SUBSTRING_INDEX('a,b,c', ',', 1) AS col1, SUBSTRING_INDEX(SUBSTRING_INDEX('a,b,c', ',', 2), ',', -1) AS col2, SUBSTRING_INDEX('a,b,c', ',', -1) AS col3; ``` 该语句会将字符串'a,b,c'按照','分隔成三个子字符串'a'、'b'和'c',并将它们分别存储在名为col1、col2和col3的列中。 2. Oracle中的split函数: Oracle中也没有原生的split函数,但可以使用REGEXP_SUBSTR函数来实现类似的功能。例如: ``` SELECT REGEXP_SUBSTR('a,b,c', '[^,]+', 1, 1) AS col1, REGEXP_SUBSTR('a,b,c', '[^,]+', 1, 2) AS col2, REGEXP_SUBSTR('a,b,c', '[^,]+', 1, 3) AS col3; ``` 该语句会将字符串'a,b,c'按照','分隔成三个子字符串'a'、'b'和'c',并将它们分别存储在名为col1、col2和col3的列中。 3. SQL Server中的split函数SQL Server中有一个内置的STRING_SPLIT函数,可以直接用于对字符串进行分隔。例如: ``` SELECT value FROM STRING_SPLIT('a,b,c', ','); ``` 该语句会将字符串'a,b,c'按照','分隔成三个子字符串'a'、'b'和'c',并将它们存储在名为value的列中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

看不见的罗辑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值