Hive - 数据流转与DDL设计

 

 

数据流转设计

 

表的分类

  • 按所有权分类可分为:外部表(外表)和托管表(内部表、内表)。
  • 按表的存储格式分类可分为:Text表、Orc表、Torc表、Holodesk表、Hyperbase表、ES表
  • 按表是否分区可分为:分区表和非分区表
  • 按表是否分桶可分为:分桶表和非分桶表

 

表的应用场景

  • 数据集市交互式分析:Holodesk
  • 数据仓库统计分析/批处理(批量+CURD):ORC事务表/Holodesk、ORC表/Holodesk
  • 高并发精确检索:Hyperbase(数据)+全局索引(索引)
  • 全文检索:Hyperbase(数据)+ Search(索引)、Search
  • 综合搜索:Search

 

T+1 数据流转

T+1 是数据仓库领域最为常见的数据集成模式,结构化数据来源一般为RDB。需要将这些数据通过ETL工具或者增量导入到HDFS中,最后根据实际业务需求通过inceptor将数据写入到对应的表中。

① sqoop/tdt 支持全量/增量导入数据,可以装载到hdfs中,tdt可以直接写入orc表中

② 需要做大量聚合分析的写入orc表或者argodb表,有数据合并、删除、修改操作的写入orc事务表或者argodb表高并发的精确查询写入hyperbase表

精确查询、模糊查询、多维度灵活组合查询写入Search表

 

准实时同步数据流转

越来越多数据仓库向准实时方向演进。

Oracle/DB2/MySQL 可以通过部署CDC(如ogg、canal)+Transporter,实现准实时地将数据录入到集群中。之后根据业务需求选择合适的表:

① 数据集市

② 高并发精确检索

③ 综合搜索

 

实时数据流转

通过Kafka接入实时数据,数据来源可以是kafka/flume/java程序等等,经过slipstream的处理,之后根据实际业务需求,将数据入库到TDH中

① 处理后的数据再次进入kafka,之后对数据再进一步处理,这样的情况一般是kafka作为数据总线的情况,不同的业务通过slipstream去不断地和kafka进行数据上的交互

② 入库Argodb的目的是为了进行准实时的数据分析,有一个前提是对于数据的实时性要求不能太高,在秒级别

③ 经过slipstream(spark/flink)处理后录入到holodesk/Search/Hyperbase的数据可以继续通过ArgoDB将表改变成其他格式,方便其他业务场景的需求。

 

 

DDL 设计

表类型 存储引擎 存储特点 支持操作 适合场景
TEXT表 hdfs

行存储,支持无压缩文本,gzip/bzip2压缩格式

批量insert和load,

truncate

分区分桶

多应用于数仓贴源层、ods层、文件交换区;作为临时表,应用于导入或导出时建立的临时表

ORC表 hdfs 列压缩,压缩比3~8倍

批量insert, truncate

分区分桶

多用于离线跑批场景,适合做统计分析(如count, join, group by, order by, sum等)
ORC事务表 hdfs 列式存储,压缩比和orc表相近

insert,update, merge,delete

分区,分桶(必须)

相比普通orc表,事务表支持curd、acid,应用于增量数据导入,拉链以及要求事务特性的存储过程中。
Holodesk表 Holodesk 列式存储,压缩比3~5倍

insert,update,merge,delete

分桶

交互式分析,应用于自助分析以及BI上拖拉拽生成报表等场景。
Hyperbase表 Hbase 列族式存储,snappy存储

insert,update,delete

分区

应用于高并发历史数据查询、精确查询以及非结构化数据存储等场景。
ES表 ElasticSearch 数据切分为shard存储

insert, delete

分区

搜索,应用于日志,文本等半结构化/非结构化数据分析场景。

 

分区设计

  • 在逻辑上,分区表和未分区表没有区别
  • 在物理上,分区表中的数据按分区键的值放在HDFS上表目录下的对应子目录下,一个分区对应一个子目录
  • 目的:数据按分区键的值(或值的范围)放在不同目录中,可以有效减少查询时扫描的数据量,提升查询效率。

 

分区分类

  • 单值分区:一个分区对应分区键的一个值
  • 范围分区:一个分区对应分区键值的一个范围(区间)
  • 不支持范围分区和单值分区混用来进行多层分区

 

分区设计要点

  • 分区字段的取名一定要和数据源表的对应列表相同
  • 一般不使用多级分区,但多级分区过滤效果非常出色可以考虑使用
  • 分区数控制在0-200个,最大不要超过200个分区
  • 建议分区采用range partition
  • 建议分区字段选择:日期字段(或地区字段)
  • 建议单个分区内桶的数目略小于Inceptor配置的CPU数目
    • 设计一个分区数据时,尽量确保一轮CPU能处理完所有数据
    • 建议单个分区分桶数在500个以内(分区数 * 分桶数 < 10000 )

 

分区字段选择

  • 一般原则:根据系统的业务类型来选择分区字段
  • 例如:
    • 通常来讲事实表是数据都包含时间属性,而报表业务也多在一定的时间范围内做统计分析,那么根据时间字段进行分区是常用的选择。
    • 而如果业务更多按照部门做统计分析,那么更适合按照部门代码,地域代码进行分区。所以贴近业务的特点选择分区是第一要素。
  • 结合数据的分布特点
    • 例如:
      • 按照用户期望按照某个字段A做分区,但是这个字段A的分布绝对倾斜(譬如字段A一共有1000个不同的数值,但是50%的值都是0,假如按照这个字段分区,那么对应的分区就占了全表50%的数据,这样会导致SQL业务的低效),这个时候选择字段A就不是一个合理的选择。
  • 注意数据倾斜问题
    • 只要数据分布不是绝对倾斜,我们是可以通过Range分区指定不同大小的方式来有效的规避倾斜问题的。因为,上一个例子的场景中,还是可以选用字段A用于数据分区。
    • 例如:
      • 企业用户数据,假如2016年用户数是2015年的3倍,我们在选择分区的时候2016年按照1个月为1个分区,而2015年每三个月为一个分区,而不是简单的数值等值切分Range。另外一个常见的情况是按照地域进行分区,我们建议把业务量较多的地域单独放在一个区,其余业务量不多的地域进行整合,从而保证各个区之间数据量大致均衡,避免出现大部分分区没有数据或者数据非常少的情况。如果发现某些区数据特别倾斜,则需要考虑进一步或者更换分区字段。
    • 生产上一般建议使用时间字段做范围分区,并根据数据的每天和每月增量的大小来界定每个分区的范围。

 

分区个数选择

  • 分区个数的选择需要综合考量数据的特性,在选择好分区字段后,我们需要根据数据的特点确定分区个数的可选择区间。
  • 实际情况中我们看到多数DDL设计都是分区数量过多,譬如单个分区的数据量不超过1GB,或者按照天来分区,这些都是不合适的设计。
  • 生产上一般我们建议分区数量在100以内。

 

ORC表分区示例

某客户生产上有一张表,tb_fct_vip_s_det_m总记录数9亿多条。有时间字段statmt,并且ETL作业中会频繁使用该字段过滤某一天的数据。每月的数据量大概在3000万条。考虑到如果按天分区会导致分区数过多,但又要尽可能的减少执行SQL时扫表的数据量,所以采用按月分区的方案。对表做了5年规划,共61个分区。

CREATE TABLE tb_fct_vip_s_det_m(
    statsdt date NOT NULL COMMENT '统计日期',
    ...
    currmonthtotalfavamt decimal(14,2) DEFAULT NULL COMMENT '本月累计优惠金额'
)
PARTITIONED BY RANGE(
    statsdt COMMENT '统计日期')(
    PARTITION stats_less_than_201401 VALUES LESS THAN('2014-01-01'),
    PARTITION stats_less_than_201402 VALUES LESS THAN('2014-02-01'),
    PARTITION stats_less_than_201403 VALUES LESS THAN('2014-03-01'),
    ...
    PARTITION stats_less_than_201812 VALUES LESS THAN('2018-12-01'),
    PARTITION stats_less_than_maxvalue VALUES LESS THAN(MAXVALUE))
CLUSTERED BY (cporgcd) INTO 19 BUCKETS stored as orc_transactions;

 

分桶设计

  • 分桶字段选择
    • 进行分桶之前需要对表的数据分布情况进行大致的分析,一般遵循的原则为,选择离散高的字段进行分桶。可以通过收集的数据特征,如Distinct Value来做参考,值越大的可以优先作为考虑对象。分桶字段选择时,注意尽量使记录分布均匀,以避免数据倾斜。
    • 建议分桶字段:关系型数据库中的主键、邮件ID、客户ID、UUID等。
  • 分桶个数选择
    • 一般可以通过数据量维度计算分桶个数。
    • 根据数据量计算分桶数公式:分桶数 = 文本格式大小(M)/500M
  • 在考虑分桶个数的时候,同时要考虑是否已经分过区。对于已经分过区的表,要按照单区的大小进行桶数的估计,而不是依照原始表。
  • 对表分桶可以将表中记录按分桶键的哈希值分散进多个桶文件中,这些文件被称为“桶”
  • 一个表可以同时进行分区和分桶
  • 分桶有三方面的好处:
    • 有助于快速取样
    • 减少操作量,提高查询效率
    • 减少Shuffle的数量

 

分桶示例

某客户在数据仓库建设的过程中,有如下一张大表,PIMS_PMART2.TB_FCT_VIP_S_DET_FL_M,总大小为23.5亿条,ORC格式24.8G,61个分区,31个桶,目前的分桶字段为collcporgcd,查看某个分区下每个桶的大小情况,如下:发现桶的大小极不均匀,有的桶265M,有的桶只有2.9M,这种是典型的分桶字段选择不合理的情况。

Step1:选出合理的分桶字段

  • 以(字段中重复值最多的记录数/表的总记录数)的值作为是否可以作为分桶字段的依据。
// 查询字段中重复值最多的记录数
COL_COUNT=select column1,count(1) cnt from table_name group by column1 order by cnt desc limit 1;
// 查询表的总记录数
TOTAL_COUNT=select count(1) from table_name;
  • 如果COL_COUNT/TOTAL_COUNT的值小于5%,或者COL_COUNT的值在100万以下,那么该字段可作为分桶字段。
  • 特别的,如果一个表中确实找不到分布均匀的字段作为分桶,可以单独对表追加一个唯一ID作为分桶字段。
  • 以该表为例,通过对该表每个字段做分析发现,custcd这个字段COL_COUNT=2114438,TOTAL_COUNT=2357030761,两者比值仅为0.09%,非常适合作为分桶字段。

 

Step2:算出合理的分桶数

  • 该表已经存在数据的分区每个分区约6000万条记录,ORC格式700M。
  • 根据计算公式:分桶数=MAX(6000万/100万,700M/100M)=60
  • 为了使数据分布更加均匀,我们一般建议选择质数作为分桶数
  • 所以该表的最佳分桶数为59或61

 

ORC表建表语法

ORC非事务表的建表只需在建表语句中用 stored as orc

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>']
    ...
    [STORED AS ORC]
    [LOCATION '<file_path>']
    [TBLPROPERTIES ('<property_name>'='<property_value>',...)];

非分区表建表语法:

CREATE TABLE <table_name> (<column> <data_type>,<column> <data_type>,...)
CLUSTERED BY (<bucket_key>) INTO <n> BUCKETS
STORED AS ORC
TBLPROPERTIES("transactional"="true");

单值分区表(Unique Value Partition)建表语法:

CREATE TABLE <table_name> (<column> <data_type>,<column> <data_type>, ...)
PARTITIONED BY (<partition_key> <data_type>)
CLUSTERED BY (<bucket_key>) INTO <n> BUCKETS
STORED AS ORC
TBLPROPERTIES("transactional"="true");

范围分区表(Range Partition) 建表语法:

CREATE TABLE <table_name> (<column> <data_type>,<column> <data_type>, ...)
PARTITIONED BY RANGE(<partition_key1> <data_type>,<partition_key2> <data_type>, ...)
(
    PARTITION [<partition_name_1>] VALUE LESS THAN(<key1_bound_value1>, <key2_bound_value1>,...),
    PARTITION [<partition_name_2>] VALUE LESS THAN(<key1_bound_value2>, <key2_bound_value2>,...),
    ...
)
CLUSTERED BY (<bucket_key>) INTO <n> BUCKETS
STORED AS ORC
TBLPROPERTIES("transactional"="true");

 

 

小结

  • ORC非事务表的建表只需在建表语句中用STORED AS ORC指定存储格式为ORC即可。
  • ORC事务表的建表则需要几个额外的重点步骤:
    • 为表分桶:为了保证增删改过程中的性能,我们要求ORC事务表必须是部分排序或者全局排序的,但是全局排序又过于耗费计算资源,因此我们要求ORC表必须是分桶表
    • TBLPROPERTIES里需要加上 "transactional"="true",以标识ORC表必须是分桶表
    • 如果表的数据量比较大,建议在分桶的基础上再分区,ORC事务表支持单值分区和范围分区。

 

posted @   HOUHUILIN  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示