文档章节

漫谈数据仓库之拉链表(原理、设计以及在Hive中的实现)

大数据之路
 大数据之路
发布于 2013/07/12 22:10
字数 3251
阅读 700
收藏 8
点赞 3
评论 1

0x00 前言

本文将会谈一谈在数据仓库中拉链表相关的内容,包括它的原理、设计、以及在我们大数据场景下的实现方式。

全文由下面几个部分组成:

  1. 先分享一下拉链表的用途、什么是拉链表。
  2. 通过一些小的使用场景来对拉链表做近一步的阐释,以及拉链表和常用的切片表的区别。
  3. 举一个具体的应用场景,来设计并实现一份拉链表,最后并通过一些例子说明如何使用我们设计的这张表(因为现在Hive的大规模使用,我们会以Hive场景下的设计为例)。
  4. 分析一下拉链表的优缺点,并对前面的提到的一些内容进行补充说明,比如说拉链表和流水表的区别。

0x01 什么是拉链表

拉链表是针对数据仓库设计中表存储数据的方式而定义的,顾名思义,所谓拉链,就是记录历史。记录一个事物从开始,一直到当前状态的所有变化的信息。

我们先看一个示例,这就是一张拉链表,存储的是用户的最基本信息以及每条记录的生命周期。我们可以使用这张表拿到最新的当天的最新数据以及之前的历史数据。

注册日期 用户编号 手机号码 t_start_date t_end_date
2017-01-01 001 111111 2017-01-01 9999-12-31
2017-01-01 002 222222 2017-01-01 2017-01-01
2017-01-01 002 233333 2017-01-02 9999-12-31
2017-01-01 003 333333 2017-01-01 9999-12-31
2017-01-01 004 444444 2017-01-01 2017-01-01
2017-01-01 004 432432 2017-01-02 2017-01-02
2017-01-01 004 432432 2017-01-03 9999-12-31
2017-01-02 005 555555 2017-01-02 2017-01-02
2017-01-02 005 115115 2017-01-03 9999-12-31
2017-01-03 006 666666 2017-01-03 9999-12-31

我们暂且不对这张表做细致的讲解,后文会专门来阐述怎么来设计、实现和使用它。

拉链表的使用场景

在数据仓库的数据模型设计过程中,经常会遇到下面这种表的设计:

  1. 有一些表的数据量很大,比如一张用户表,大约10亿条记录,50个字段,这种表,即使使用ORC压缩,单张表的存储也会超过100G,在HDFS使用双备份或者三备份的话就更大一些。
  2. 表中的部分字段会被update更新操作,如用户联系方式,产品的描述信息,订单的状态等等。
  3. 需要查看某一个时间点或者时间段的历史快照信息,比如,查看某一个订单在历史某一个时间点的状态。
  4. 表中的记录变化的比例和频率不是很大,比如,总共有10亿的用户,每天新增和发生变化的有200万左右,变化的比例占的很小。

那么对于这种表我该如何设计呢?下面有几种方案可选:

  • 方案一:每天只留最新的一份,比如我们每天用Sqoop抽取最新的一份全量数据到Hive中。
  • 方案二:每天保留一份全量的切片数据。
  • 方案三:使用拉链表。

为什么使用拉链表

现在我们对前面提到的三种进行逐个的分析。

方案一

这种方案就不用多说了,实现起来很简单,每天drop掉前一天的数据,重新抽一份最新的。

优点很明显,节省空间,一些普通的使用也很方便,不用在选择表的时候加一个时间分区什么的。

缺点同样明显,没有历史数据,先翻翻旧账只能通过其它方式,比如从流水表里面抽。

方案二

每天一份全量的切片是一种比较稳妥的方案,而且历史数据也在。

缺点就是存储空间占用量太大太大了,如果对这边表每天都保留一份全量,那么每次全量中会保存很多不变的信息,对存储是极大的浪费,这点我感触还是很深的......

当然我们也可以做一些取舍,比如只保留近一个月的数据?但是,需求是无耻的,数据的生命周期不是我们能完全左右的。

拉链表

拉链表在使用上基本兼顾了我们的需求。

首先它在空间上做了一个取舍,虽说不像方案一那样占用量那么小,但是它每日的增量可能只有方案二的千分之一甚至是万分之一。

其实它能满足方案二所能满足的需求,既能获取最新的数据,也能添加筛选条件也获取历史的数据。

所以我们还是很有必要来使用拉链表的。

0x02 拉链表的设计和实现

如何设计一张拉链表

下面我们来举个栗子详细看一下拉链表。

我们接上在《漫谈数据仓库之维度建模》中的电商网站的例子,现在以用户的拉链表来说明。

我们先看一下在Mysql关系型数据库里的user表中信息变化。

在2017-01-01这一天表中的数据是:

注册日期 用户编号 手机号码
2017-01-01 001 111111
2017-01-01 002 222222
2017-01-01 003 333333
2017-01-01 004 444444

在2017-01-02这一天表中的数据是, 用户002和004资料进行了修改,005是新增用户:

注册日期 用户编号 手机号码 备注
2017-01-01 001 111111
2017-01-01 002 233333 (由222222变成233333)
2017-01-01 003 333333
2017-01-01 004 432432 (由444444变成432432)
2017-01-02 005 555555 (2017-01-02新增)

在2017-01-03这一天表中的数据是, 用户004和005资料进行了修改,006是新增用户:

注册日期 用户编号 手机号码 备注
2017-01-01 001 111111  
2017-01-01 002 233333  
2017-01-01 003 333333  
2017-01-01 004 654321 (由432432变成654321)
2017-01-02 005 115115 (由555555变成115115)
2017-01-03 006 666666 (2017-01-03新增)

如果在数据仓库中设计成历史拉链表保存该表,则会有下面这样一张表,这是最新一天(即2017-01-03)的数据:

注册日期 用户编号 手机号码 t_start_date t_end_date
2017-01-01 001 111111 2017-01-01 9999-12-31
2017-01-01 002 222222 2017-01-01 2017-01-01
2017-01-01 002 233333 2017-01-02 9999-12-31
2017-01-01 003 333333 2017-01-01 9999-12-31
2017-01-01 004 444444 2017-01-01 2017-01-01
2017-01-01 004 432432 2017-01-02 2017-01-02
2017-01-01 004 654321 2017-01-03 9999-12-31
2017-01-02 005 555555 2017-01-02 2017-01-02
2017-01-02 005 115115 2017-01-03 9999-12-31
2017-01-03 006 666666 2017-01-03 9999-12-31

说明

  • t_start_date表示该条记录的生命周期开始时间,t_end_date表示该条记录的生命周期结束时间。
  • t_end_date = '9999-12-31'表示该条记录目前处于有效状态。
  • 如果查询当前所有有效的记录,则select * from user where t_end_date = '9999-12-31'。
  • 如果查询2017-01-02的历史快照,则select from user where t_start_date <= '2017-01-02' and t_end_date >= '2017-01-02'。(*此处要好好理解,是拉链表比较重要的一块。**)

在Hive中实现拉链表

在现在的大数据场景下,大部分的公司都会选择以Hdfs和Hive为主的数据仓库架构。目前的Hdfs版本来讲,其文件系统中的文件是不能做改变的,也就是说Hive的表智能进行删除和添加操作,而不能进行update。基于这个前提,我们来实现拉链表。

还是以上面的用户表为例,我们要实现用户的拉链表。在实现它之前,我们需要先确定一下我们有哪些数据源可以用。

  1. 我们需要一张ODS层的用户全量表。至少需要用它来初始化。
  2. 每日的用户更新表。

而且我们要确定拉链表的时间粒度,比如说拉链表每天只取一个状态,也就是说如果一天有3个状态变更,我们只取最后一个状态,这种天粒度的表其实已经能解决大部分的问题了。

另外,补充一下每日的用户更新表该怎么获取,据笔者的经验,有3种方式拿到或者间接拿到每日的用户增量,因为它比较重要,所以详细说明:

  1. 我们可以监听Mysql数据的变化,比如说用Canal,最后合并每日的变化,获取到最后的一个状态。
  2. 假设我们每天都会获得一份切片数据,我们可以通过取两天切片数据的不同来作为每日更新表,这种情况下我们可以对所有的字段先进行concat,再取md5,这样就ok了。
  3. 流水表!有每日的变更流水表。

ods层的user表

现在我们来看一下我们ods层的用户资料切片表的结构:

CREATE EXTERNAL TABLE ods.user (
  user_num STRING COMMENT '用户编号',
  mobile STRING COMMENT '手机号码',
  reg_date STRING COMMENT '注册日期'
COMMENT '用户资料表'
PARTITIONED BY (dt string)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
STORED AS ORC
LOCATION '/ods/user';
)

ods层的user_update表

然后我们还需要一张用户每日更新表,前面已经分析过该如果得到这张表,现在我们假设它已经存在。

CREATE EXTERNAL TABLE ods.user_update (
  user_num STRING COMMENT '用户编号',
  mobile STRING COMMENT '手机号码',
  reg_date STRING COMMENT '注册日期'
COMMENT '每日用户资料更新表'
PARTITIONED BY (dt string)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
STORED AS ORC
LOCATION '/ods/user_update';
)

拉链表

现在我们创建一张拉链表:

CREATE EXTERNAL TABLE dws.user_his (
  user_num STRING COMMENT '用户编号',
  mobile STRING COMMENT '手机号码',
  reg_date STRING COMMENT '用户编号',
  t_start_date ,
  t_end_date
COMMENT '用户资料拉链表'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
STORED AS ORC
LOCATION '/dws/user_his';
)

实现sql语句

然后初始化的sql就不写了,其实就相当于是拿一天的ods层用户表过来就行,我们写一下每日的更新语句。

现在我们假设我们已经已经初始化了2017-01-01的日期,然后需要更新2017-01-02那一天的数据,我们有了下面的Sql。

然后把两个日期设置为变量就可以了。

INSERT OVERWRITE TABLE dws.user_his
SELECT * FROM
(
    SELECT A.user_num,
           A.mobile,
           A.reg_date,
           A.t_start_time,
           CASE
                WHEN A.t_end_time = '9999-12-31' AND B.user_num IS NOT NULL THEN '2017-01-01'
                ELSE A.t_end_time
           END AS t_end_time
    FROM dws.user_his AS A
    LEFT JOIN ods.user_update AS B
    ON A.user_num = B.user_num
UNION
    SELECT C.user_num,
           C.mobile,
           C.reg_date,
           '2017-01-02' AS t_start_time,
           '9999-12-31' AS t_end_time
    FROM ods.user_update AS C
) AS T

0x03 补充

好了,我们分析了拉链表的原理、设计思路、并且在Hive环境下实现了一份拉链表,下面对拉链表做一些小的补充。

拉链表和流水表

流水表存放的是一个用户的变更记录,比如在一张流水表中,一天的数据中,会存放一个用户的每条修改记录,但是在拉链表中只有一条记录。

这是拉链表设计时需要注意的一个粒度问题。我们当然也可以设置的粒度更小一些,一般按天就足够。

查询性能

拉链表当然也会遇到查询性能的问题,比如说我们存放了5年的拉链数据,那么这张表势必会比较大,当查询的时候性能就比较低了,个人认为两个思路来解决:

  1. 在一些查询引擎中,我们对start_date和end_date做索引,这样能提高不少性能。
  2. 保留部分历史数据,比如说我们一张表里面存放全量的拉链表数据,然后再对外暴露一张只提供近3个月数据的拉链表。

0xFF 总结

我们在这篇文章里面详细地分享了一下和拉链表相关的知识点,但是仍然会有一会遗漏。欢迎交流。

在后面的使用中又有了一些心得,补充进来:

  1. 使用拉链表的时候可以不加t_end_date,即失效日期,但是加上之后,能优化很多查询。

  2. 可以加上当前行状态标识,能快速定位到当前状态。

  3. 在拉链表的设计中可以加一些内容,因为我们每天保存一个状态,如果我们在这个状态里面加一个字段,比如如当天修改次数,那么拉链表的作用就会更大。

 

本文转载自:http://www.jianshu.com/p/799252156379#

共有 人打赏支持
大数据之路
粉丝 1486
博文 516
码字总数 344497
作品 0
武汉
架构师
加载中

评论(1)

尘道
尘道
写的非常好啊!
大数据经典学习路线(及供参考)之 一

1.Linux基础和分布式集群技术 学完此阶段可掌握的核心能力: 熟练使用Linux,熟练安装Linux上的软件,了解熟悉负载均衡、高可靠等集群相关概念,搭建互联网高并发、高可靠的服务架构; 学完此...

柯西带你学编程
05/22
0
0
大数据经典学习路线(及供参考)

转:https://blog.csdn.net/yuexianchang/article/details/52468291 目录(?)[+]

junzixing1985
04/15
0
0
大数据和云计算技术周报(第5期)

写在第5期周报 Q1:老生常谈,上一期周报反响如何? A1:上周推出了第3期以及第4期NoSQL特辑,热度持续高涨,同学们打赏不断。并请到社区书法家张总给社区题字,社区成功晋级“四有社区”:有...

znzqhb07nr
2017/12/18
0
0
Hive与传统数据库对比

由于Hive采用了SQL的查询语言HQL,因此很容易将Hive理解为数据库。其实从结构上来看,Hive和数据库除了拥有类似的查询语言,再无类似之处。本文将从多个方面来阐述Hive和数据库的差异。数据库...

shida1009
2017/12/13
0
0
Hive 随谈(三)– Hive 和数据库的异同

摘要:由于 Hive 采用了 SQL 的查询语言 HQL,因此很容易将 Hive 理解为数据库。其实 从结构上来看,Hive 和数据库除了拥有类似的查询语言,再无类似之处。本文将 从多个方面来阐述 Hive 和数...

红薯
2010/04/13
3.7K
1
基于Hadoop生态圈的数据仓库实践 —— ETL(二)

二、使用Hive转换、装载数据 1. Hive简介 (1)Hive是什么 Hive是一个数据仓库软件,使用SQL读、写、管理分布式存储上的大数据集。它建立在Hadoop之上,具有以下功能和特点: 通过SQL方便地访...

wzy0623
2016/07/06
0
0
基于Hadoop生态圈的数据仓库实践 —— ETL(一)

一、使用Sqoop抽取数据 1. Sqoop简介 Sqoop是一个在Hadoop与结构化数据存储(如关系数据库)之间高效传输大批量数据的工具。它在2012年3月被成功孵化,现在已是Apache的顶级项目。Sqoop有Sqo...

wzy0623
2016/07/01
0
0
架构学习资料汇总

知名网站架构分析 探索Google App Engine背后的奥秘(1)–Google的核心技术 探索Google App Engine背后的奥秘(2)–Google的整体架构猜想 探索Google App Engine背后的奥秘(3)- Google App Eng...

peter8015
2016/04/22
186
0
Hadoop、Pig、Hive、Storm、NoSQL 学习资源收集【Updating】

(一)hadoop 相关安装部署 1、hadoop在windows cygwin下的部署: http://lib.open-open.com/view/1333428291655 http://blog.csdn.net/ruby97/article/details/7423088 http://blog.csdn.n......

大数据之路
2013/06/25
0
3
hive和关系型数据库RDBMS的异同

摘要:由于 Hive 采用了 SQL 的查询语言 HQL,因此很容易将 Hive 理解为数据库。其实 从结构上来看,Hive 和数据库除了拥有类似的查询语言,再无类似之处。本文将 从多个方面来阐述 Hive 和数...

浮躁的码农
2015/08/03
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

StringUtils类中isEmpty与isBlank的区别

org.apache.commons.lang.StringUtils类提供了String的常用操作,最为常用的判空有如下两种isEmpty(String str)和isBlank(String str)。 StringUtils.isEmpty(String str) 判断某字符串是否为...

说回答
9分钟前
0
0
react native使用redux快速上手

先看个简单demo //app.jsimport React, {Component} from 'react';import {StyleSheet, Button, View} from 'react-native';import TestView from './src/testView'export default......

燕归南
10分钟前
0
0
页面输出JSON格式数据

package com.sysware.utils;import java.io.IOException;import javax.servlet.ServletResponse;import org.apache.log4j.Logger;import com.sysware.SyswareConstant;pub......

AK灬
32分钟前
0
0
springCloud-2.搭建Eureka Client的使用

1.使用IDEA,Spring Initializr创建 2.填写项目资料 3.选择spring boot版本,插件选择Cloud Discovery→Eureka Discovery 4.选择保存地址 5.修改application.yml eureka: client: s...

贺小康
35分钟前
0
0
CenOS 6.5 RPM 安装 elasticsearch 6.3.1

下载 wget --no-check-certificate https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.3.1.rpm...

阿白
37分钟前
0
0
1.4 创建虚拟机&1.5 安装CentOS7&1.6 配置ip(上)&1.7 配置ip(下)

1.4 创建虚拟机 知识点 虚拟机网络链接模式 桥连 直接将虚拟网卡桥接到一个物理网卡上面。需要手工为虚拟系统配置IP地址、子网掩码,而且还要和宿主机器处于同一网段,这样虚拟系统才能和宿主...

小丑鱼00
44分钟前
0
0
TrustAsia(亚洲诚信)助力看雪2018安全开发者峰会

2018年7月21日,看雪2018安全开发者峰会在北京国家会议中心圆满落下帷幕。拥有18年悠久历史的老牌安全技术社区——看雪学院联手国内最大开发者社区CSDN,汇聚业内顶尖的安全开发者和技术专家...

亚洲诚信
45分钟前
0
0
Spring注解介绍

@Resource、@AutoWired、@Qualifier 都用来注入对象。其中@Resource可以以 name 或 type 方式注入,@AutoWired只能以 type 方式注入,@Qualifier 只能以 name 方式注入。 但它们有一些细微区...

lqlm
55分钟前
0
0
32位汇编在64位Ubuntu上的汇编和连接

本教程使用的操作系统是Ubuntu Linux 18.04 LTS版本,汇编器是GNU AS(简称as),连接器是GNU LD(简称ld)。 以下是一段用于检测CPU品牌的汇编小程序(cpuid2.s): .section .dataoutput...

ryanliue
今天
0
0
CentOS系统启动报错Failed to mount /sysroot解决方法

xfs_repair -v -L /dev/dm-0

Mr_Tea伯奕
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部