大数据理论与实践9 分布式数据仓库Inceptor

简介

定位:
分布式通用SQL引擎:支持Hyperbase、Search、ArgoDB、StellarDB和Slipstream等核心数据库和计算引擎。逻辑数仓。
分布式关系型数仓:基于Hive和Spark,主要用于离线分析(对时间不敏感)。主要是关系型的数据。

原理

系统架构

在这里插入图片描述
数据类型是不一样的,要放在不同的数据库中。关系型数据放在HDFS中(Hive底层),半结构化数据放在Hyperbase(思维表)中,文本类型需要综合搜索防在Search(切词和全文索引),一体化的统一架构防在ArgoDB,图防在StellarDB中。
逻辑数仓:数据其实是分离的,但是在逻辑上是一个。

InceptorServer

connector:为BI、ETL工具提供标准JDBC、ODBC接口。
SQL Compiler包括:SQL语法解析器、规则和代价优化器、代码生成器。
PL/SQL Compiler包括:存储过程解析器、控制流优化器、并行优化器。
Transaction Manager:分布式增删改、事务并发控制器。
Spark:星环发行版
计算资源:Executor是资源调度的基本单元。

Metastore

存储各种类型的元数据,如Inceptor表、Hyperbase表、Search表、Slipstream流表等,数据存储在TxSQL(MySql引擎)上。

Client

  • CLI(命令行界面):Beeline
  • JDBC/ODBC
  • Web UI
  • Waterdrop:Inceptor SQL IDE工具,支持主流的关系数据库和数据仓库,如Oracle、DB2、MySQL、Hive、Teradata、SAP等。

数据模型

在这里插入图片描述
表下面分区,分区下面分桶。因为在大数据中,索引是不合适的,因为索引进行更新开销是非常大的,对数据进行分区。分区下面分桶,用于join连接。

database 数据库

数据库是一个包含若干表的命名空间和磁盘目录,类似于RDBMS中的数据库。系统会为每个数据库创建一个目录,目录名=数据库名.db,通常无法删除包含表的数据库,应该先删表,再删数据库。

table 表

和关系型数据库一样,存储同一类关系的数据记录。
元数据存储在Metastore中,表数据存储在HDFS、Hyperbase(用于半结构化)和Search(用于全文数据)中。

按照所有权分类,表可以分为内表外表

  • 内表(Managed Table,又称托管表)
    系统具有内表的完全控制权,负责管理它的生命周期,元数据存储在Metastore中,想删就删。表数据默认存储在数据库(database)目录下的对应子目录(文件夹)中(目录名=表名),或者人工创建的外部目录中。删除内表时,会同时删除表数据,以及Metastore中的元数据。
  • 外表(External Table)
    系统不具有外表的完全控制权,更加常用。元数据存储在Metastore中,表数据通常存储在指定的外部存储目录中。删除外表时,不会删除表数据,但会删除Metastore中的元数据。

按存储格式分类,表可以分为Text表ORC表

  • Text表
    系统默认的表类型,无压缩,行存储,仅支持批量Insert,性能较低,主要用于导入原始数据时建立过渡表(贴源层,靠近数据源,仅进行简单清洗)。
  • ORC表
    优化的列式存储,轻量级索引,压缩比高,仅支持批量Insert;是Inceptor计算的主要表类型,适用于数仓离线分析,通常由Text表生成(清洗转换后,通常为ORC表)。

按存储格式分类,分为ORC事务表Hyperbase表以及ES表

  • ORC事务表:由ORC表衍生而来,继承了ORC表的所有特性,支持完整CURD以及分布式事务处理。多版本文件存储,定期做压缩,10~20%的性能损失。
  • Hyperbase表:存储在Hyperbase上,支持多种索引,以及Insert、Update、Delete;适用于高并发点查(简单条件,精确查询),支持半结构化数据、小对象(10M以内的图片和音视频文件)存储。
  • ES表
    原始数据和索引数据都存储在ElasticSearch中,支持模糊、语义和全文检索,适用于综合搜索
Partition 分区

一张表分成多个分区。因为在大数据(PB级)中,索引是不合适的,因为索引进行更新开销是非常大的。要把数据集合变小。
含义:将表按照某个或某几个字段(分区键)划分为更小的数据集。
作用:减少不必要的全表扫描,缩小查询范围,提升查询效率
存储:一个分区对应于表目录下的一个子目录,可以进行单值分区(分区键=value)或者范围分区(分区键=_less_than_value):
在这里插入图片描述
例如上图,采用state作为分区键,这样在数据库中,会有右边这个进行划分。这样在Select查询的时候,只需要遍历相应的区域即可,缩小查询范围。
注意:单值分区不能出现在表结构中,因为已经用这个划分文件了,再存储就产生了冗余。
分区键通常高频出现在Select … Where条件查询中,为了避免产生过多的子目录和小文件,只对离散字段进行分区,如日期、地域、民族等。需要提前对分区数量进行预估,尽量避免数据倾斜。

Bucket 分桶

用于提升join的效率。
通过分桶键哈希取模操作(key hashcode % N),将表或分区数据随机分发到N个桶中。桶数N一般为质数,方便桶数不同进行操作。
哈希取模的作用:数据划分:随机分为N个桶;数据聚合:Key相同的数据在同一个桶中。
在这里插入图片描述
分桶可以:

  • 提高Join查询效率:如果两个表的Join列都做了分桶,且分桶数相同或成倍数,那么相同列值的数据会分到编号相同或有对应关系的桶中,这样就不用全表遍历,对应的桶做Join就可以了;
  • 提高取样效率:从桶中直接抽取数据。

假如有两张表,都分仨桶,那么每条数据都是在同一个桶号中的(即,第一条数据都在0桶,第4条数据都在1桶),这样做join是比较容易的。

存储时,在表或分区目录下,每个桶存储为一个文件,文件的大小应控制在100~200MB之间(ORC表压缩后)。如果桶文件小于HDFS Block, 那么一个桶对应一个Block,否则会存储在多个Block 中。

分桶用法:

  1. 先分区后分桶;
  2. 分桶键和分桶数在建表时确定,不允许更改,否则必须重新分桶;
  3. 分桶键必须出现在表结构中

读时模式

数据进入数仓时,并不检查数据的规范性,而是查询时再验证。这样写入速度快,适合处理大规模数据。查询时处理尺度很宽松(弱校验),尽可能容忍各种错误。

Inceptor SQL

SQL DDL

数据库操作

Create/Drop/Alter/Use/Describe/Show Database(s)
完整建表语句:

CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] 
[<database_name>.]<table_name>
[(<col_name> <data_type> [COMMENT '<col_comment>'] [, <col_name> <data_type> ...])]
[COMMENT '<table_comment>'] 
[PARTITIONED BY [RANGE] (<partition_key> <data_type> [COMMENT '<partition_comment>'] 
[, <partition_key > <data_type>...])] 
[CLUSTERED BY (<col_name> [, <col_name>...])
[SORTED BY (<col_name> [ASC|DESC] [, <col_name> [ASC|DESC]...])] 
INTO <num_buckets> BUCKETS]
[
[ROW FORMAT <row_format>] 
[STORED AS (TEXTFILE|ORC|CSVFILE)] 
| STORED BY '<storage.handler.class.name>' [WITH SERDEPROPERTIES (<...>)] 
]
[LOCATION '<file_path>'] 
[TBLPROPERTIES ('<property_name>'='<property_value>', ...)];

表操作

Create/Drop/Alter/Truncate/Describe/Show Table(s)
创建内表

# 通过自定义Schema来创建内表
CREATE TABLE <table_name> 
	[(<col_name> <data_type> [, <col_name> <data_type> ...])];
# 通过已存在的表或视图来创建内表,只复制Schema,不复制数据
CREATE TABLE <table_name> 
	LIKE <existing_table_or_view_name>;
# 通过查询结果来创建内表,既复制Schema,又复制数据
CREATE TABLE <table_name>
	AS SELECT <select_statement>;

高级操作:
分区和分桶

分区

单值分区:一个分区对应分区键的一个值,目录名为“分区键=键值”
在这里插入图片描述
分区键不能和表结构中的列重复,因为分区键和数据已存储在分区目录名中,否则会造成数据冗余。

CREATE [EXTERNAL] TABLE <table_name>
	(<col_name> <data_type> [, <col_name> <data_type> ...])
	PARTITIONED BY (<partition_key> <data_type>, ...) 
	[CLUSTERED BY ...] 
	[ROW FORMAT <row_format>] 
	[STORED AS TEXTFILE|ORC|CSVFILE]
	[LOCATION '<file_path>'] 
	[TBLPROPERTIES ('<property_name>'='<property_value>', ...)];

在create语句之后,是不会创建文件的,因为还不知道是动态还是静态分区,系统只是知道你创建了分区。
单值分区分为两类:

  • 单值静态分区
# 单值静态分区导入时系统没有任何机制来保证数据导入的正确性,只有靠用户自己
/* 覆盖导入 */
INSERT OVERWRITE TABLE <table_name> 
	PARTITION (<partition_key>=<partition_value>[, <partition_key>=<partition_value>, ...]) 
	SELECT <select_statement>;
/* 追加导入 */
INSERT INTO TABLE <table_name> 
	PARTITION (<partition_key>=<partition_value>[, <partition_key>=<partition_value>, ...])
	SELECT <select_statement>;
/* 将HDFS数据或本地文件导入到分区目录下 */
LOAD DATA [LOCAL] INPATH '<path>[OVERWRITE] INTO TABLE <tablename> 
	PARTITION (<partition_key>=<partition_value>[, <partition_key>=<partition_value>, ...]);
  • 单值动态分区:导入数据时,系统可以动态判断目标分区
    自动设置分区,需要进行设置。动态定义必须写在静态之后。
/* 开启动态分区支持,并设置最大分区数*/
set hive.exec.dynamic.partition=true;
set hive.exec.max.dynamic.partitions=2000;
/* <dpk>为动态分区键, <spk>为静态分区键 */
INSERT (OVERWRITE | INTO) TABLE <table_name>
	PARTITION ([<spk>=<value>, ..., ] <dpk>, [..., <dpk>]) 
	SELECT <select_statement>;

新增、删除、重命名和清空分区:

ALTER TABLE <table_name> ADD PARTITION (<partition_key>=<value>);
ALTER TABLE <table_name> DROP PARTITION (<partition_key>=<value>);
ALTER TABLE <table_name> PARTITION (<partition_key>=<value>) RENAME TO PARTITION
(<partition_key>=<new_value>);
TRUNCATE TABLE <table_name> PARTITION (<partition_key>=<value>);

范围分区
每个分区对应分区键的一个区间,凡是落在指定区间内的数据都被存储在对应的分区下。
创建范围分区:

CREATE [EXTERNAL] TABLE <table_name>
(<col_name> <data_type>, <col_name> <data_type>, ...)
	PARTITIONED BY RANGE (<partition_key> <data_type>, ...) 
		(PARTITION [<partition_name>] VALUES LESS THAN (<cutoff>), 
			[PARTITION [<partition_name>] VALUES LESS THAN (<cutoff>),
			...
]
			PARTITION [<partition_name>] VALUES LESS THAN (<cutoff>|MAXVALUE) 
		)
[ROW FORMAT <row_format>] [STORED AS TEXTFILE|ORC|CSVFILE]
[LOCATION '<file_path>'] 
[TBLPROPERTIES ('<property_name>'='<property_value>', ...)];

范围分区需手工指定,可在建表时添加分区,也可建表后通过Alter Table添加或删除分区

分桶

按分桶键哈希取模的方式,将表中数据随机分发到若干桶文件中。
创建分桶表:INTO BUCKETS设定桶的数量,SORTED BY设定桶内排序,默认升序

CREATE [EXTERNAL] TABLE <table_name>
	(<col_name> <data_type> [, <col_name> <data_type> ...])]
	[PARTITIONED BY ...] 
	CLUSTERED BY (<col_name>) 
		[SORTED BY (<col_name> [ASC|DESC] [, <col_name> [ASC|DESC]...])] 
		INTO <num_buckets> BUCKETS
		[ROW FORMAT <row_format>] 
	[STORED AS TEXTFILE|ORC|CSVFILE]
	[LOCATION '<file_path>'] 
	[TBLPROPERTIES ('<property_name>'='<property_value>', ...)];

写入分桶表

/* 开启强制分桶,并设置Reduce任务数为分桶数 */
set hive.enforce.bucketing = true;
set mapreduce.job.reduces=4;
/* 写入分桶表 */
INSERT (OVERWRITE | INTO) TABLE <table_name>
SELECT <select_expression>, <select_expression>, ... FROM <table_name>
DISTRIBUTE BY <col_name> SORT BY <col_name> [ASC|DESC] [, col_name [ASC|DESC], ...]
[CLUSTER BY <col_list> ]

与分区键不同,分桶键必须是表结构中的列。分桶键和分桶数在建表时确定,不允许更改。

SQL DML

数据预处理:文件编码为UTF-8,\n为换行符

导入

Load导入
将文件中的数据导入已存在的表或分区。仅将数据文件移动到表或分区的目录中,不会对数据进行任何处理,如分桶、排序。不支持动态分区导入。
不推荐使用Load导入数据:在安全模式下的权限设置步骤较多。

# <path>是文件存储路径,如果标注LOCAL,指向本地磁盘路径,不标则指向HDFS路径
LOAD DATA [LOCAL] INPATH '<path>[OVERWRITE] INTO
	TABLE <tablename> 
	[PARTITION (<partition_key>=<partition_value>, ...)];

Insert导入

# 单值静态分区导入
INSERT (OVERWRITE | INTO) TABLE <table_name> 
	PARTITION (<partition_key>=<partition_value>[, <partition_key>=<partition_value>, ...]) 
	SELECT <select_statement>;
# 单值动态分区导入
/* 开启动态分区支持,并设置最大分区数*/
set hive.exec.dynamic.partition=true;
set hive.exec.max.dynamic.partitions=2000;
/* <dpk>为动态分区键, <spk>为静态分区键 */
INSERT (OVERWRITE | INTO) TABLE <table_name>
	PARTITION ([<spk>=<value>, ..., ] <dpk>, [..., <dpk>]) 
	SELECT <select_statement>; 

查询

分桶与聚合:
Distribute By:将Distribute By列值相同的数据发送给同一个Reduce任务,实现数据按指定列聚合

Inceptor PL/SQL

数据类型

%type属性
基于变量或数据库表的列定义一个变量。该变量的类型与被参照变量或列的类型相同;当被参照变量的类型改变之后,新定义变量的类型也会随之改变
%rowtype属性
基于数据库表定义一个记录型变量。该变量的字段名和字段类型就是被参照表的字段名和字段类型;当被参照表的结构发生变化时,新定义变量也会发生改变
标量类型
用于保存单个值,Inceptor支持的标量类型有Int、String、Double、Boolean等
复合类型
如Record、Collection等

PL/SQL语句块

/* 声明(可选)*/
DECLARE
transid STRING
/*执行体开始(必要)*/
BEGIN
SELECT trans_id into transid from transactions where acc_num=6513065
DBMS_OUTPUT.put_line(transid)
/*异常处理(可选)*/
EXCEPTION --WHEN too_many_rows
THEN
DBMS_OUTPUT.put_line ('too many rows')
/*执行体结束(必要)*/
END;

存储过程

/* 创建存储过程 */
CREATE OR REPLACE PROCEDURE hello_world()
IS
DECLARE
l_message STRING := 'Hello World!'
BEGIN
DBMS_OUTPUT.put_line (l_message)
END;
/* 调用存储过程 */
BEGIN
hello_world()
END;

复习

如何定位Inceptor?它与Hive有什么区别?
如何理解Inceptor读时模式。
分区的目的是什么?分区有几种类型?如何将数据导入分区表?
分桶的目的是什么?如何将数据导入分桶表?

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值