当前位置: 首页 > news >正文

数据仓库-拉链表

在数据仓库中制作拉链表,可以按照以下步骤进行:

  1. 确定需求:首先明确需要使用拉链表的场景和需求。例如,可能需要记录历史数据的变化,以便进行时间序列分析等。
  2. 设计表结构:在数据仓库中,拉链表通常由两个表组成:当前表和历史表。当前表存储最新的数据,历史表存储过去的数据。两个表的结构应该相同,包含相同的字段。
  3. 建立当前表:创建一个当前表,用于存储最新的数据。该表应包含需要记录的所有字段,以及一个表示有效期的字段。有效期字段用于标识该条数据在何时失效。
  4. 建立历史表:创建一个历史表,用于存储过去的数据。该表的结构与当前表相同,但还应包含一个表示生效日期的字段。生效日期字段用于标识该条数据在何时生效。
  5. 数据插入与更新:当有新数据插入时,将其插入到当前表中,并设置相应的有效期。同时,将过期的数据从当前表中移动到历史表中,并更新它们的生效日期和有效期。
  6. 数据查询:在查询数据时,同时查询当前表和历史表。根据查询的时间范围,确定在哪个表中查找数据。
  7. 定期维护:定期对历史表进行维护,例如删除过期的数据、优化索引等,以确保拉链表的性能和存储空间的有效利用。

需要注意的是,拉链表在数据仓库中的应用可能因具体需求和场景而有所不同。因此,在实际制作拉链表时,建议根据数据仓库的设计规范、业务需求和数据特点进行调整和优化。同时,也要确保数据的完整性、一致性和准确性。

参考:漫谈数据仓库之拉链表(原理、设计以及在Hive中的实现)_拉链表实现方式_小强签名设计的博客-CSDN博客

二、什么是拉链表

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

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

1. 拉链表的使用场景

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

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

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

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

  拉链表在使用上基本兼顾了我们的需求。
  首先它在空间上做了一个取舍,虽说不像方案一那样占用量那么小,但是它每日的增量可能只有方案二的千分之一甚至是万分之一。
  其实它能满足方案二所能满足的需求,既能获取最新的数据,也能添加筛选条件也获取历史的数据。

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

三、拉链表的设计和实现
1. 如何设计一张拉链表

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

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

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

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

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

说明:

  • 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’。(此处要好好理解,是拉链表比较重要的一块。 where条件筛选当前有效数据,开始日期小于等于当前日期并且结束日期大于等于当前日期,则为有效。
2. 在Hive中实现拉链表

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

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

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

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

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

  1. 我们可以监听 Mysql 数据的变化,比如说用 Canal,最后合并每日的变化,获取到最后的一个状态。
  2. 假设我们每天都会获得一份切片数据,我们可以通过取两天切片数据的不同来作为每日更新表,这种情况下我们可以对所有的字段先进行 concat,再取 md5,这样就 ok 了。
  3. 流水表!有每日的变更流水表。
  4. 通过 etl 工具对操作型数据库按照时间字段增量抽取到 ods 或者数据仓库(每天抽取前一天的数据),形成每天的增量数据(实际中使用最多的情形)

(1)拉链表实现方式一

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';
)

现在我们假设我们已经已经初始化了 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,CASEWHEN A.t_end_time = '9999-12-31' AND B.user_num IS NOT NULL THEN '2017-01-01'ELSE A.t_end_timeEND AS t_end_timeFROM dws.user_his AS ALEFT JOIN ods.user_update AS BON A.user_num = B.user_num
UNIONSELECT C.user_num,C.mobile,C.reg_date,'2017-01-02' AS t_start_time,'9999-12-31' AS t_end_timeFROM ods.user_update AS C
) AS T
(2)拉链表实现方式二

操作型数据库的用户表结构:

CREATE EXTERNAL TABLE ods.user ( user_num STRING COMMENT '用户编号', mobile STRING COMMENT '手机号码', reg_date STRING COMMENT '注册日期' ,last_modify_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';

每天增量抽取的用户表结构和抽取条件:

  1)表结构和上面的表结构保持一致,我们取表名为 ods.user_update

  2)增量抽取条件:select * from ods.user where last_modify_date = '$date'

拉链表:

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

CREATE EXTERNAL TABLE dws.user_his ( user_num STRING COMMENT '用户编号', mobile STRING COMMENT '手机号码', reg_date STRING COMMENT '用户编号',last_modify_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:

1)

merge into dws.user_his tar 
using
(select user_num,mobile from ods.user_update 
) sou on tar.user_num=sou.user_num 
and tar.mobile=sou.mobile 
and tar.t_start_date < '$date' 
and tar.t_end_date >  '$date' 
when matched then 
update set tar.t_end_date='9999-12-31';

按照主键筛选,在 dws.user_his 表中出现过的并且现在为有效数据的,全部更新为闭链数据。

INSERT  TABLE dws.user_his 
SELECT C.user_num, C.mobile, C.reg_date, c.last_modify_date'2017-01-02' AS t_start_time, '9999-12-31' AS t_end_time FROM ods.user_update AS C;

比如我们要1月2号的数据,取出来的数据为 select from user where t_start_date <= ‘2017-01-02’ and t_end_date >= ‘2017-01-02’

与1月2号数据完全一致。

1. 拉链表和流水表

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

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

2. 查询性能

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

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

  可以采用备份的方案,保证无误和可行。(保存增量数据,并对 t_dw_orders_his 表每个月备份一次全量数据。如需回滚,最多重跑30天数据即可)

六、总结

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

  1. 使用拉链表的时候可以不加 t_end_date,即失效日期,但是加上之后,能优化很多查询。
  2. 可以加上当前行状态标识,能快速定位到当前状态。
  3. 在拉链表的设计中可以加一些内容,因为我们每天保存一个状态,如果我们在这个状态里面加一个字段,比如当天修改次数,那么拉链表的作用就会更大。

相关文章:

数据仓库-拉链表

在数据仓库中制作拉链表&#xff0c;可以按照以下步骤进行&#xff1a; 确定需求&#xff1a;首先明确需要使用拉链表的场景和需求。例如&#xff0c;可能需要记录历史数据的变化&#xff0c;以便进行时间序列分析等。设计表结构&#xff1a;在数据仓库中&#xff0c;拉链表通…...

【Docker】一些可以直接用的Docker环境

这里罗列一些打包的镜像&#xff0c;方便直接使用。 cu11.6ubuntu18.04 docker push kevinchina/deeplearning:cu11.6ubuntu18.04 FROM nvidia/cuda:11.6.2-cudnn8-devel-ubuntu18.04 RUN apt-get update && apt-get install -y wget git vim curl RUN wget http://…...

Unity2D中瓦片地图的创建与绘制教程

Unity2D中瓦片地图的创建与绘制 素材切割创建地图创建瓦片绘制地图瓦片调色板画笔拓展素材资源链接 素材切割 选中以下素材&#xff0c;以Tiles为例&#xff08;素材链接在文章最下方&#xff09; 修改素材属性。 将Sprite Mode属性改为Multiple多张&#xff08;不然切割不了&…...

现代的简洁,诠释轻奢的精致!福州中宅装饰,福州装修

轻奢风是一种生活新时尚 优雅、低调、舒适、简单&#xff0c;不断地推陈出新 站在时尚的前沿&#xff0c;引领潮流 中宅装饰集团轻奢风格产品 追求高品质生活细节 以设计精致的空间构造营造出 一种优雅、时尚生活氛围 将低调奢华之美注入现代家居设计中 客厅|The Sitt…...

运用ChatGPT辅助新手学习躺赢者PRO飞控二次开发示例(2023年10月28日)

运用ChatGPT辅助新手学习躺赢者PRO飞控二次开发示例&#xff08;2023年10月28日&#xff09; 1、以飞控预设的飞行任务demo中void flight_subtask_1(void)代码为例分析一下变量flight_subtask_cnt的作用&#xff1f; //逆时针转动90度&#xff0c;完成后降落 void flight_sub…...

【Java】HashCode方法重写注意事项

HashCode方法 HashCode方法是属于Object父类提供的方法&#xff0c;HashCode方法返回该对象的哈希码值。支持该方法是为哈希表提供一些优点&#xff0c;例如&#xff0c;java.util.Hashtable提供的哈希表HashCode的常规协定是&#xff1a;在Java应用程序执行期间&#xff0c;在…...

039-第三代软件开发-PDF阅读器

第三代软件开发-PDF阅读器 文章目录 第三代软件开发-PDF阅读器项目介绍PDF阅读器1 初始化PDF view2 qml 中使用3 创建模块 关键字&#xff1a; Qt、 Qml、 pdf、 LTDev、 本地 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&#xff08;Qt Met…...

计算机毕业设计选题推荐-跑腿平台微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…...

RocketMQ生产者消息发送出去了,消费者一直接收不到怎么办?(Rocket MQ订阅关系一致性)

问题: 使用RocketMQ消息队列&#xff0c;生产者将数据发送出去了&#xff0c;但是生产者一致没接收到&#xff08;或者是间隔好几分钟&#xff0c;突然接收到一条数据&#xff09;怎么办&#xff1f;并且通过rocket web控制台查看消息的状态为NOT_ONELINE或者NOT_CONSUME&#…...

使用Golang开发硬件驱动

1. 介绍 Golang是一种简洁、高效的编程语言&#xff0c;它的强大并发性能和丰富的标准库使得它成为了开发硬件驱动的理想选择。在本文中&#xff0c;我们将探讨如何使用Golang开发硬件驱动程序&#xff0c;并提供一个实例来帮助你入门。 2. 准备工作 在开始之前&#xff0c;…...

设计模式(19)命令模式

一、介绍&#xff1a; 1、定义&#xff1a;命令模式&#xff08;Command Pattern&#xff09;是一种行为设计模式&#xff0c;它将请求封装为一个对象&#xff0c;从而使你可以使用不同的请求对客户端进行参数化。命令模式还支持请求的排队、记录日志、撤销操作等功能。 2、组…...

QModelIndex 与QStandardItem相互转换

目录 1、 QModelIndex 转换成QStandardItem 2 、QStandardItem 转换成 QModelIndex 3、示例 4、总结 1、 QModelIndex 转换成QStandardItem QStandardItem * itemQStandardItemModel::​itemFromIndex(const QModelIndex & index) const 借助QStandardItemModel来完成…...

Linux - 进程地址空间

前言 首先&#xff0c;我们先要对 内存当中存储 各个数据之间的 结构要有一个 大概的了解&#xff1a; 各个区当中存储的数据使用类型不同&#xff0c;所以&#xff0c;这些数据在使用方式上是有差别的。比如下面这个例子&#xff1a; 在C 语言当中我们不能直接对 上述的 str…...

系统架构设计师-第16章-嵌入式系统架构设计理论与实践-软考学习笔记

嵌入式系统( Embedded System) 是为了特定应用而专门构建的计算机系统&#xff0c;其架构是随着嵌入式系统的逐步应用而发展形成的。嵌入式软件架构的设计与嵌入式系统的体系架构是密不可分的。因此&#xff0c;本常首先介绍嵌入式系统硬件相关知识&#xff08;系统特征、硬件组…...

pod进阶

目录 资源限制 CPU 资源单位 内存 资源单位 实例 健康检查 探针的三种规则&#xff1a; Probe支持三种检查方法&#xff1a; 示例1&#xff1a;exec方式 示例2&#xff1a;httpGet方式 示例3&#xff1a;tcpSocket方式 示例4&#xff1a;就绪检测 扩展 资源限制 当定…...

系列四十七、Spring的事务传播行为案例演示(七)#NOT_SUPPORTED

一、演示Spring的传播行为&#xff08;NOT_SUPPORTED&#xff09; 1.1、StockServiceImplNOT_SUPPORTED /*** Author : 一叶浮萍归大海* Date: 2023/10/30 15:43* Description: 演示NOT_SUPPORTED的传播行为* 外部不存在事务&#xff1a;不开启新的事务* 外部存在…...

54.RabbitMQ快速实战以及核心概念详解

MQ MQ&#xff1a;MessageQueue&#xff0c;消息队列。这东西分两个部分来理解&#xff1a; 队列&#xff0c;是一种FIFO 先进先出的数据结构。 消息&#xff1a;在不同应用程序之间传递的数据。将消息以队列的形式存储起来&#xff0c;并且在不同的应用程序之间进行传递&am…...

Qt TreeView 设置节点不可编辑

目录 1. 创建treeview 2、节点不可编辑 3、设置logo 4、实例代码 1. 创建treeview //声明模型 QStandardItemModel *model;//创建4行&#xff0c;1列的模型 model new QStandardItemModel(4,1);//添加标题 model->setHeaderData(0, Qt::Horizontal, tr("Tree View…...

python django获取某个角色的某个数据和——例如:获取所有订单的应付金额总和

model关系如下&#xff1a; class Order(models.Model):订单product models.ForeignKey(Product, on_deletemodels.SET_NULL, blankTrue, nullTrue, verbose_name"产品")no models.CharField(max_length50, blankTrue, nullTrue, verbose_name订单编号, db_indexT…...

如何在React项目中引用less

安装less npm install less less-loader --save-dev暴露 webpack 文件 利用 npx create-react-app 搭建的 React 项目&#xff0c;默认隐藏 webpack 配置文件&#xff0c;引入 less 需要修改 webpack 配置文件&#xff0c;因此我们需要执行命令暴露 webpack 配置文件。 请先将…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...

jdbc查询mysql数据库时,出现id顺序错误的情况

我在repository中的查询语句如下所示&#xff0c;即传入一个List<intager>的数据&#xff0c;返回这些id的问题列表。但是由于数据库查询时ID列表的顺序与预期不一致&#xff0c;会导致返回的id是从小到大排列的&#xff0c;但我不希望这样。 Query("SELECT NEW com…...

rm视觉学习1-自瞄部分

首先先感谢中南大学的开源&#xff0c;提供了很全面的思路&#xff0c;减少了很多基础性的开发研究 我看的阅读的是中南大学FYT战队开源视觉代码 链接&#xff1a;https://github.com/CSU-FYT-Vision/FYT2024_vision.git 1.框架&#xff1a; 代码框架结构&#xff1a;readme有…...

电脑桌面太单调,用Python写一个桌面小宠物应用。

下面是一个使用Python创建的简单桌面小宠物应用。这个小宠物会在桌面上游荡&#xff0c;可以响应鼠标点击&#xff0c;并且有简单的动画效果。 import tkinter as tk import random import time from PIL import Image, ImageTk import os import sysclass DesktopPet:def __i…...