当前位置: 首页 > 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 配置文件。 请先将…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

离线语音识别方案分析

随着人工智能技术的不断发展&#xff0c;语音识别技术也得到了广泛的应用&#xff0c;从智能家居到车载系统&#xff0c;语音识别正在改变我们与设备的交互方式。尤其是离线语音识别&#xff0c;由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力&#xff0c;广…...

DeepSeek越强,Kimi越慌?

被DeepSeek吊打的Kimi&#xff0c;还有多少人在用&#xff1f; 去年&#xff0c;月之暗面创始人杨植麟别提有多风光了。90后清华学霸&#xff0c;国产大模型六小虎之一&#xff0c;手握十几亿美金的融资。旗下的AI助手Kimi烧钱如流水&#xff0c;单月光是投流就花费2个亿。 疯…...

LangChain【6】之输出解析器:结构化LLM响应的关键工具

文章目录 一 LangChain输出解析器概述1.1 什么是输出解析器&#xff1f;1.2 主要功能与工作原理1.3 常用解析器类型 二 主要输出解析器类型2.1 Pydantic/Json输出解析器2.2 结构化输出解析器2.3 列表解析器2.4 日期解析器2.5 Json输出解析器2.6 xml输出解析器 三 高级使用技巧3…...

精益数据分析(98/126):电商转化率优化与网站性能的底层逻辑

精益数据分析&#xff08;98/126&#xff09;&#xff1a;电商转化率优化与网站性能的底层逻辑 在电子商务领域&#xff0c;转化率与网站性能是决定商业成败的核心指标。今天&#xff0c;我们将深入解析不同类型电商平台的转化率基准&#xff0c;探讨页面加载速度对用户行为的…...