本篇文章為大家帶來了關於mysql的相關知識,其中主要介紹了關於資料庫拉鍊表的相關問題,拉鍊表是一種資料模型,主要是針對資料倉儲設計中表儲存資料的方式而定義的,顧名思義,所謂拉鍊,就是記錄歷史,下面一起來看一下,希望對大家有幫助。
推薦學習:mysql影片教學
在資料倉儲的資料模型設計過程中,常常會遇到這樣的需求:
1、資料量比較大;
2、表格中的部分欄位會被update,如用戶的地址,產品的描述信息,訂單的狀態等等;
3、需要查看某一個時間點或者時間段的歷史快照信息,比如,查看某一個訂單在歷史某一個時間點的狀態,例如,查看某一個使用者在過去某一段時間內,更新過幾次等等;
#4、變化的比例和頻率不是很大,比如,總共有1000萬的會員,每天新增和發生變化的有10萬左右;
5、如果對這邊表每天都保留一份全量,那麼每次全量中會保存很多不變的信息,對存儲是極大的浪費;
對於這種表有幾種方案可選:
以上方案比較
方案一
這種方案就不用多說了,實作起來很簡單,每天drop掉前一天的數據,重新抽一份最新的。
優點很明顯,節省空間,一些普通的使用也很方便,不用在選擇表的時候加一個時間分區什麼的。
缺點同樣明顯,沒有歷史數據,先翻翻舊帳只能透過其它方式,例如從管線表裡面抽動。
方案二
每天一份全量的切片是比較穩健的方案,而且歷史資料也在。
缺點就是儲存空間佔用量太大太大了,如果對這邊表每天都保留一份全量,那麼每次全量中會保存很多不變的信息,對存儲是極大的浪費,這點我感觸還是很深的…
當然我們也可以做一些取捨,例如只保留近一個月的資料?但是,需求是無恥的,數據的生命週期不是我們能完全左右的。
拉鍊表
拉鍊表在使用上基本上兼顧了我們的需求。
首先它在空間上做了一個取捨,雖說不像方案一那樣佔用量那麼小,但是它每日的增量可能只有方案二的千分之一甚至是萬分之一。
其實它能滿足方案二所能滿足的需求,既能取得最新的數據,也能加入篩選條件也取得歷史的數據。
所以我們還是有必要來使用拉鍊錶的。
拉鍊表是一種資料模型,主要是針對資料倉儲設計中表格儲存資料的方式而定義的,顧名思義,所謂拉鍊,就是記錄歷史。記錄一個事物從開始,一直到當前狀態的所有變化的資訊。拉鍊表可以避免以每一天儲存所有記錄造成的大量儲存問題,同時也是處理緩慢變化資料(SCD2)的常見方式。
百度百科的解釋:拉鍊表是維護歷史狀態,以及最新狀態資料的一種表,拉鍊表根據拉鍊粒度的不同,實際上相當於快照,只不過做了優化,去除了一部分不變的記錄,透過拉鍊表可以很方便的還原出拉鍊時點的客戶記錄。
1、收集當日全量資料到ND(NowDay當日)表;
2、可從歷史表中取出昨日全量資料儲存到OD( OldDay上日)表;
3、兩個表進行全字段比較,(ND-OD)就是當日新增和變化的數據,也就是當天的增量,用W_I表示;
4、兩個表格進行全字段比較,(OD-ND)為狀態到此結束需要封鏈的數據,需要修改END_DATE,用W_U表示;
5、將W_I表的內容全部插入到歷史表中,這些是新增記錄,start_date為當天,而end_date為max值,可以設為'9999-12-31';
6、對歷史表進行W_U部份的更新操作,start_date保持不變,而end_date改為當天,也就是關鏈操作,歷史表(OD)和W_U表比較,START_DATE,END_DATE除外,以W_U表為準,兩者交集將其END_DATE改成當日,說明該記錄失效。
舉個簡單例子,例如有訂單表:
6月20號有3筆記錄:
訂單建立日期 | 訂單編號 | 訂單狀態 |
---|---|---|
2012 -06-20 | 001 | 建立訂單 |
#2012-06-20 | 002 | #創建訂單 |
2012-06-20 | 003 | 支付完成 |
到6月21日,表中有5筆記錄:
訂單建立日期 | 訂單編號 | 訂單狀態 |
---|---|---|
2012-06-20 | 001 | 建立訂單 |
2012-06-20 | 002 | 建立訂單 |
2012-06-20 | 003 | ##支付完成|
004 | #建立訂單 | |
005 | 建立訂單 |
訂單編號 | 訂單狀態 | |
---|---|---|
001 | 建立訂單 | |
002 | 」建立訂單 | |
003 | 支付完成 | |
#004 | 建立訂單 | |
#005 | 建立訂單 | |
006 | 建立訂單 |
##訂單編號 | 訂單狀態 | dw_bigin_date | dw_end_date | |
---|---|---|---|---|
#001 | 建立訂單 | 2012-06-20 | 2012-06-20 | ##2012-06-20 |
支付完成 | 2012-06-21 | 9999-12-31 | 2012-06- 20 | |
建立訂單 | 2012-06-20 | 9999-12-31 | # 2012-06-20 | |
支付完成 | 2012-06-20 | 2012-06-21 | #2012-06-20 | |
已出貨 | 2012-06-22 | 9999-12-31 | #2012-06-21 | |
建立訂單 | 2012-06-21 | #9999-12- 31 | 2012-06-21 | |
建立訂單 | 2012-06-21 | 2012-06-21 | #2012-06-21 | |
支付完成 | #2012-06-22 | 9999-12-31 | 2012-06-22 | |
建立訂單 | #2012-06 -22 | 9999-12-31 |
訂單狀態 | dw_bigin_date | date | ||
---|---|---|---|---|
支付完成 | 2012-06-21 | 9999-12-31 | 2012-06- 20 | |
建立訂單 | 2012-06-20 | 9999-12-31 | # 2012-06-20 | |
支付完成 | 2012-06-20 | 2012-06-21 | #2012-06-21 | |
建立訂單 | 2012-06-21 | 9999-12-31 | #2012-06-21 | |
#建立訂單 | 2012-06-21 | #2012-06-21 |
訂單狀態 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
建立訂單 | 2012-06-20 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
建立訂單 | ##2012-06-20 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
支付完成 | 2012-06-21 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
建立訂單 | #2012-06-21 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#建立訂單 |
可以看出,這樣的歷史拉鍊表,既能滿足對歷史資料的需求,又能很大程度的節省儲存資源; 拉鍊表示例2:#在歷史表中對人的一生的記錄可能就這樣幾筆記錄,避免了按每一天記錄客戶狀態造成的海量存儲的問題:
CREATE VOLATILE TABLE VT_xxxx_NEW AS xxxx WITH NO DATA ON COMMIT PRESERVE ROWS;CREATE VOLATILE SET TABLE VT_xxxx_CHG,NO LOG AS xxxx WITH NO DATA ON COMMIT PRESERVE ROWS;2、取得當日全量數據 INSERT INTO VT_xxxx_NEW(xx) SELECT (xx,cur_date, max_date) FROM xxxx_sorce;3、抽取新增或有變化的數據,從xxxx_NEW臨時表到xxxx_CHG臨時表; INSERT INTO VT_xxxx_CHG(xx)SELECT xx FROM VT_xxxx_NEWWHERE (xx) NOT IN (select xx from xxxx_HIS where end_date='max_date');4、更新歷史表的失效記錄的end_date為max值 UPDATE A1 FROM xxxx_HIS A1, VT_xxxx_CHG A2SET End_Date='current_date'WHERE A1.xx=A2.xx AND A1.End_Date='max_date';5、將新增或有變化的資料插入目標表 INSERT INTO xxxx_HIS SELECT * FROM VT_xxxx_CHG; 以商品資料為例 存在商品表t_product,表格結構如下:
##2019-12-202019-12-20 004
12月21日(10條資料)
12月22日(18条数据)
MySQL数仓代码实现MySQL初始化 在MySQL中 lalian 库和商品表用于到原始数据层 -- 创建数据库create database if not exists lalian;-- 创建商品表create table if not exists `lalian`.`t_product`( goods_id varchar(50), -- 商品编号 goods_status varchar(50), -- 商品状态 createtime varchar(50), -- 商品创建时间 modifytime varchar(50) -- 商品修改时间); 在MySQL中创建ods和dw层来模拟数仓 -- ods创建商品表create table if not exists `lalian`.`ods_t_product`( goods_id varchar(50), -- 商品编号 goods_status varchar(50), -- 商品状态 createtime varchar(50), -- 商品创建时间 modifytime varchar(50), -- 商品修改时间 cdat varchar(10) -- 模拟hive分区)default character set = 'utf8';-- dw创建商品表create table if not exists `lalian`.`dw_t_product`( goods_id varchar(50), -- 商品编号 goods_status varchar(50), -- 商品状态 createtime varchar(50), -- 商品创建时间 modifytime varchar(50), -- 商品修改时间 cdat varchar(10) -- 模拟hive分区)default character set = 'utf8'; 增量导入12月20号数据 原始数据导入12月20号数据(4条) insert into `lalian`.`t_product`(goods_id, goods_status, createtime, modifytime) values('001', '待审核', '2019-12-18', '2019-12-20'),('002', '待售', '2019-12-19', '2019-12-20'),('003', '在售', '2019-12-20', '2019-12-20'),('004', '已删除', '2019-12-15', '2019-12-20'); 注意:由于这里使用的MySQL来模拟的数仓所以直接使用insert into的方式导入数据,在企业中可能会使用hive来做数仓使用 kettle 或者 sqoop 或 datax 等来同步数据。 # 从原始数据层导入到ods 层insert into lalian.ods_t_productselect *,'20191220' from lalian.t_product ;# 从ods同步到dw层insert into lalian.dw_t_productselect * from lalian.ods_t_product where cdat='20191220'; 查看dw层的运行结果 select * from lalian.dw_t_product where cdat='20191220';
增量导入12月21数据 原始数据层导入12月21日数据(6条数据) UPDATE `lalian`.`t_product` SET goods_status = '待售', modifytime = '2019-12-21' WHERE goods_id = '001'; INSERT INTO `lalian`.`t_product`(goods_id, goods_status, createtime, modifytime) VALUES ('005', '待审核', '2019-12-21', '2019-12-21'), ('006', '待审核', '2019-12-21', '2019-12-21'); 将数据导入到ods层与dw层 # 从原始数据层导入到ods 层insert into lalian.ods_t_productselect *,'20191221' from lalian.t_product ;# 从ods同步到dw层insert into lalian.dw_t_productselect * from lalian.ods_t_product where cdat='20191221'; 查看dw层的运行结果 select * from lalian.dw_t_product where cdat='20191221';
增量导入12月22日数据 原始数据层导入12月22日数据(6条数据) UPDATE `lalian`.`t_product` SET goods_status = '已删除', modifytime = '2019-12-22' WHERE goods_id = '003';UPDATE `lalian`.`t_product` SET goods_status = '已删除', modifytime = '2019-12-22' WHERE goods_id = '006';INSERT INTO `lalian`.`t_product`(goods_id, goods_status, createtime, modifytime) VALUES('007', '待审核', '2019-12-22', '2019-12-22'),('008', '待审核', '2019-12-22', '2019-12-22'); 将数据导入到ods层与dw层 # 从原始数据层导入到ods 层 insert into lalian.ods_t_product select *,'20191222' from lalian.t_product ; # 从ods同步到dw层 insert into lalian.dw_t_productpeizhiwenjian select * from lalian.ods_t_product where cdat='20191222'; 查看dw层的运行结果 select * from lalian.dw_t_product where cdat='20191222';
查看dw层的运行结果 select * from lalian.dw_t_product;
從上述案例,可以看到:表每天保留一份全量,每次全量中會保存很多不變的信息,如果數據量很大的話,對存儲是極大的浪費 ,表可以設計為拉鍊錶,既能滿足反應資料的歷史狀態,又可以最大限度地節省儲存空間。 方案二: 使用拉鍊表保存歷史快照拉鍊表不儲存冗餘的數據,只有某 能夠查詢到歷史快照 額外的增加了兩列( 12月20日商品拉鍊表的資料
12月20日的資料是全新的資料導入到dw表
12月21日商品拉鍊表的資料
拉鍊表中沒有儲存冗餘的數據,即只要數據沒有變化,無需同步
12月22日商品拉鍊表的資料
拉链表中没有存储冗余的数据,即只要数据没有变化,无需同步
MySQL数仓拉链表快照实现操作流程:
代码实现 在MySQL中lalian库和商品表用于到原始数据层 -- 创建数据库create database if not exists lalian;-- 创建商品表create table if not exists `lalian`.`t_product2`( goods_id varchar(50), -- 商品编号 goods_status varchar(50), -- 商品状态 createtime varchar(50), -- 商品创建时间 modifytime varchar(50) -- 商品修改时间)default character set = 'utf8'; 在MySQL中创建ods和dw层 模拟数仓 -- ods创建商品表create table if not exists `lalian`.`ods_t_product2`( goods_id varchar(50), -- 商品编号 goods_status varchar(50), -- 商品状态 createtime varchar(50), -- 商品创建时间 modifytime varchar(50), -- 商品修改时间 cdat varchar(10) -- 模拟hive分区)default character set = 'utf8';-- dw创建商品表create table if not exists `lalian`.`dw_t_product2`( goods_id varchar(50), -- 商品编号 goods_status varchar(50), -- 商品状态 createtime varchar(50), -- 商品创建时间 modifytime varchar(50), -- 商品修改时间 dw_start_date varchar(12), -- 生效日期 dw_end_date varchar(12), -- 失效时间 cdat varchar(10) -- 模拟hive分区)default character set = 'utf8'; 全量导入2019年12月20日数据 原始数据层导入12月20日数据(4条数据) insert into `lalian`.`t_product_2`(goods_id, goods_status, createtime, modifytime) values('001', '待审核', '2019-12-18', '2019-12-20'),('002', '待售', '2019-12-19', '2019-12-20'),('003', '在售', '2019-12-20', '2019-12-20'),('004', '已删除', '2019-12-15', '2019-12-20'); 将数据导入到数仓中的ods层 insert into lalian.ods_t_product2select *,'20191220' from lalian.t_product2 where modifytime >='2019-12-20'; 将数据从ods层导入到dw层 insert into lalian.dw_t_product2select goods_id, goods_status, createtime, modifytime, modifytime,'9999-12-31', cdat from lalian.ods_t_product2 where cdat='20191220'; 增量导入2019年12月21日数据 原始数据层导入12月21日数据(6条数据) UPDATE `lalian`.`t_product2` SET goods_status = '待售', modifytime = '2019-12-21' WHERE goods_id = '001';INSERT INTO `lalian`.`t_product2`(goods_id, goods_status, createtime, modifytime) VALUES('005', '待审核', '2019-12-21', '2019-12-21'),('006', '待审核', '2019-12-21', '2019-12-21'); 原始数据层同步到ods层 insert into lalian.ods_t_product2select *,'20191221' from lalian.t_product2 where modifytime >='2019-12-21'; 编写ods层到dw层重新计算 dw_end_date select t1.goods_id, t1.goods_status, t1.createtime, t1.modifytime, t1.dw_start_date, case when (t2.goods_id is not null and t1.dw_end_date>'2019-12-21') then '2019-12-21'else t1.dw_end_date end as dw_end_date , t1.cdatfrom lalian.dw_t_product2 t1left join (select * from lalian.ods_t_product2 where cdat='20191221')t2 on t1.goods_id=t2.goods_idunionselect goods_id, goods_status, createtime, modifytime, modifytime,'9999-12-31', cdat from lalian.ods_t_product2 where cdat='20191221'; 执行结果如下:
拉链历史表,既能满足反应数据的历史状态,又可以最大程度的节省存储。我们做拉链表的时候要确定拉链表的粒度,比如说拉链表每天只取一个状态,也就是说如果一天有3个状态变更,我们只取最后一个状态,这种天粒度的表其实已经能解决大部分的问题了。 推荐学习:mysql视频教程 |
以上是一起來聊聊資料庫拉鍊表的詳細內容。更多資訊請關注PHP中文網其他相關文章!