各式結構化數據的動態接入存儲查詢,這一需求相信有很多人都遇到過,隨着實現技術路線選擇的不同,遇到的問題出入大了,其解決辦法也是大相徑庭。數據存儲在哪兒,是關系型數據庫,還是NoSQL數據庫,是MySQL還是Oracle,怎么建立索引,建立什么類型的索引,都是大學問。下面,我要把我對這一解決辦法的思考總結一下,有成熟的也有不成熟的,希望大家一起共同探討。
關鍵詞:
結構化數據,
動態,
接入, 存儲, 查詢
首先,我們得定義一下在本文中什么是結構化數據,這里的結構化數據主要是指扁平化的、可以由基礎數據類型組合成的數據,例如:
{"data":{"name":"wang anqi","age":28,"money":1653.35,"XX_date":"2013-12-3"}
},它們是可以很容易
被
存入關系型數據庫的,我們要討論的就是這種數據。對應的,
非結構化數據這里是指那些需要存儲在文件系統中的,不是扁平化的數據。
那么,什么又是“各式結構化數據”呢,在本文中?這是一個數據集合,有可能集合中的每一條數據結構都是不盡相同的,例如:
{"data":{"name":"wang anqi","age":28,"money":1653.35,"XX_date":"2013-12-3"}
},和
{"angel":{"address":"清涼山公園","user":289770363}
}同時存在於一個數據集合中,它們結構不同,簡單地說:第一條數據有四個屬性,第二條數據只有兩個屬性,屬性名稱和類型都不一樣。“各式”
包括了不定數量的
屬性
,不定的
屬性名稱、
不定的
數據
類型。
解釋清楚名詞了,再解釋一下動詞:“動態接入”。在普遍情境下,你只會遇到將固定數據結構的數據存儲入庫,這里的入庫主要還是指MySQL一類的關系型數據庫。那么你可以選擇使用Hibernate等ORM工具或不使用,來進行數據的存儲讀取,在使用ORM工具的情況下,要首先定義好數據的數據結構,寫死在xml里或是java代碼里。
一般情況下,你是不會遇到這樣的需求的:對於不能事先確定數據結構的數據,我要把它們存儲到關系型數據庫中,並提供“合法性檢驗”、“更新”、“查詢”等基本數據操作。要說的是,如果要把它們存儲到HBase這種NoSQL數據庫中,那是再好不過的了,配合着HBase與Solr的集成(詳見之前的博客:
大數據架構-使用HBase和Solr將存儲與索引放在不同的機器上
),搜索也不是件難事,唯一可能出現的難點在於:Solr對於Schema中filedName的配置,因為結構是動態的,所以fildName也是動態的,這其實也是很好處理的,有位微軟的同學已經跟我咨詢過這個問題了;事實上,這樣的例子是很常見的。
但是往往,事與願違,很有可能存在着其它的約束條件制約着你:必須使用關系型數據庫,那么一整套解決辦法是需要設計的。因為當你使用Hibernate時,你不能再把一個數據結構寫死在代碼里,因為它不是固定的,你該如何入庫,該如何查詢數據,這都是問題。
要處理好“各式結構化數據動態接入管理
”,應該分成以下幾步:一、定義數據;二、動態管理;三、數據接入;四、數據查詢。
一、定義數據
假如相關業務單位提供的有這么一類數據,這里用一條數據舉例來說明:產品A 產品序列號為:A8815001 生產日期為:2013/12/09 13:33:33 供應商為:阿里巴巴 產品質量為:509g 是否合格為:是。我們可以從中大致看出各字段的數據類型,通過面向對象的方法定義出這么一個產品A類也很容易。現在我們要把這條數據存儲到關系型數據庫中,還是需要一引起先前處理的。
定義數據是基礎,無論你的數據結構再怎么不固定,它也應該有個名字,它的屬性也應該是由基礎數據類型構成的。基礎數據類型就像是構成世界萬物的基本元素Ka、Ca、H、O等(請原諒我的靈感來自於《絕命毒師》),它們肯定是要事先定義好的,我們數據庫中能夠接入的數據必須由這些組成,如:
String
、
Integer
、
Long
、
BigDecimal
、
Boolean
、
Calendar
、
Date
、
Time
、
Blob
等,
Time
是以什么樣的格式存在的,18:09:32還是18:9:32.3?
Date
是以
什么樣的格式存在的,2000-10-8 18:09:32還是2000/10/8 18:9:32.3?
因此基礎數據類型,需要作為一項很重要的業務約定,同各個“干系人”共享。就是說,在這里,我定義的基礎數據類型,並不是我一個人說的算了的,而是要同相關業務單位開會溝通,這些基礎數據類型是否可以囊括它們提供的數據結構的所有內容。畢竟,要接入的數據類型是由他們提供的,他們事先也需要把各種數據類型定義好,比如上面的產品A的結構,都要以文檔
《產品A數據結構定義》
的形式記錄下來。
在這一步需要產生一個文檔,《基礎數據定義文檔》。
光是有基礎數據類型的概念還遠遠不夠,可能在產品A的數據結構文檔中,還定義了某一屬性值是否可為空,它的取值范圍是多少,以及其它業務相關的配置等等,那么就需要有一張數據定義表,來記錄對數據的要求。舉個例子,我們要接入管理的數據有:產品A,根據文檔《產品A數據結構定義》和
《基礎數據定義文檔》和《業務需求文檔》
,它的數據結構可以在數據庫中被定義成這樣:
類型名稱(DATA_TYPE) | 屬性中文名稱(ATTR_CNAME) | 屬性名稱(ATTR_NAME) | 屬性類型(ATTR_TYPE) | 是否可空(IS_NULLABLE) | 屬性長度(ATTR_LENGTH) | 是否可查(IS_SEARCHABLE) | 屬性約束(ATTR_RES) | ...... |
產品A | 產品序列號 | PRO_SEQ | String | F | 32 | T | ^[\s\S]{0,32}$ | |
產品A | 生產日期 | PRO_DATE | Date | F | 19 | T | ^\d{4}-\d{2}-\d{2}( )+\d{2}(:\d{2}){2}$ | |
產品A | 供應商 | SUPPLY | String | T | 60 | T | ^[\s\S]{0,60}$ | |
產品A | 產品質量 | WEIGHT | Integer | F | 5 | T | ^\d{1,5}(\.\d*)?$ | |
產品A | 是否合格 | IS_QUALIFIED | Boolean | T | 1 | F | ^\d{1,1}(\.\d*)?$ | |
...... |
說明一下,這是的“是否可查”表示的是是否可以針對
此屬性
作為查詢條件,進行數據搜索,這是業務相關的;這里的“屬性約束”一列,它采用“正則表達式”的方式,在驗證數據正確性時,將起到很大的作用。驗證數據正確性這部分邏輯,放在數據接入中做。
這樣一來,我們就能把對“產品A”這種數據類型的具體的數據要求放在數據庫中存儲起來,我們估且把這些數據要求集合而成的表命名為:屬性表(TBL_ATTRIBUTE),當然這是可以通過使用
Hibernate的方式來進行增刪改的操作的。別忘了,一個數據類型的增刪改,還需要改一張表:數據類型管理表(TBL_DATATYPE),它存儲了所有的被動態接入管理的數據類型,其表結構及其可能存儲的數據內容如下所示:
類型名稱(DATA_TYPE) | 表名(TABLE) | 創建時間(CREATE_TIME) | 修改時間(MODIFY_TIME) | ...... |
產品A | TBL_PRO_A | 2014/10/8 8:43:23 | 2014/10/8 15:2:12 | |
產品B | TBL_PRO_B | 2014/10/8 10:16:56 | 2014/10/8 16:37:7 | |
...... |
二、動態管理
定義完了要接入的數據類型,我們需要的是有這么一個函數來完成:addDataType(
DataType dt, List<Attribute> attrList)的功能,只要我們定義好了DataType數據類型、
List<Attribute>屬性列表,那么我們就要能夠動態地把這種數據類型加入“王安琪豪華數據”中進行管理。數據類型的管理容易,通過
數據類型管理表(TBL_DATATYPE)和
屬性表(TBL_ATTRIBUTE),將傳入的
DataType dt 和 List<Attribute> attrList 入庫即可,主要的難點還是在於動態的把此種數據類型的存儲表建立起來,因為每定義一種數據類型就要創建一張表。在這一步,我們需要產生或更新三張表:
數據類型管理表(TBL_DATATYPE)、
屬性表(TBL_ATTRIBUTE)和產品A表(TBL_PRO_A)。
對於舉例的“產品A”,它生成的存儲表中的內容應該是這樣的:
PRO_SEQ | PRO_DATE | SUPPLY | WEIGHT | IS_QUALIFIED |
A0001234567 | 2011-06-09 12:29:19 | 杭州諾基亞西門子科技有限公司 | 398 | 1 |
Angel89Wang | 2013-04-05 08:10:23 | 江蘇金陵科技有限公司 | 125 | |
...... |
在填入這些數據之前,我們應該把這張表建立起來,建立的依據就是
數據類型管理表(TBL_DATATYPE)和
屬性表(TBL_ATTRIBUTE)中對“
產品A
”的定義,要創建的表名為:
TBL_PRO_A,各個列名為:PRO_SEQ(32
位
字符串型)、PRO_DATE(時間型)、SUPPLY(60
位
字符串型)、WEIGHT(5位整數型示)、IS_QUALIFIED(布爾型)。
在這里,我使用的是Hibernate所支持的tableName.hbm.xml來動態生成表的,當然在此之前,需要先把
tableName.hbm.xml文件按照定義的格式生成好。然后再調用
SchemaUpdate schemaUpdate = new SchemaUpdate(config);
schemaUpdate.execute(true, true);來生成表。
其實動態管理這一步,就不只是動態建表這么簡單,上面說的比較狹隘,從廣義上理解,它應該被分解為下面幾個操作:1、動態創建;2、動態修改;3、動態刪除。
動態創建就是通過
addDataType(
DataType dt, List<Attribute> attrList)來實現的
,
那么我們還需要:
removeDataType(String dataTypeName)來實現動態刪除,
updateDataType(
DataType dt, List<Attribute> attrList)來實現動態修改。
動態創建就是剛剛說的那一部分,通過在
數據類型管理表(TBL_DATATYPE)、
屬性表(TBL_ATTRIBUTE)中添加一個數據類型,並動態生成一張存儲表
。動態修改就比較麻煩了,
數據類型管理表(TBL_DATATYPE)、
屬性表(TBL_ATTRIBUTE)中內容容易修改,可是
如果數據存儲表中存有數據,應該怎么處理。我采用了和一般數據庫通用的處理方式,若表中有數據,則不給動態修改,必須通過刪除再創建的方式來達到效果。動態刪除,我采用的處理方式是,先刪除此種數據類型定義,也即在
數據類型管理表(TBL_DATATYPE)和
屬性表(TBL_ATTRIBUTE)把相應數據刪除,然后再刪除實際存儲表中的數據。當然,具體的處理方法,還是要跟相關業務單位協商,了解他們的需求,如果他們就是不要刪除數據存儲表中的數據呢。
動態建表
是這里比較容易出問題的地方,因為如果我們新建一種數據類型:“typeA”,然后再把這個數據類型刪除,此時就要處理好它在Hibernate上下文中的位置,也要從Hibernate上下文中刪除,據我所知,
Hibernate對這個還沒有支持,也可能是我才疏學淺,歡迎各位提供解決辦法。
存在問題
1、數據字典問題
數據字典是數據管理系統中經常使用的,這主要是用來保證靈活性的,它基本是由key-value鍵值對來實現的,在實際數據存儲中存儲key,但在顯示時取出value用來顯示。它的好處為:更改value不改key,可以做到顯示靈活配置,比如TB在系統中原先顯示為“淘寶”,后來想要它顯示為“TaoBao”,那么只要在數據字典項中更改value值即可。
那么問題就來了,假如數據類型A中某屬性
應用了某數據字典,key-value1,它要存儲的是key,那么我們就還要對這一數據字典進行管理。在我們的系統中也要存在這份數據字典,並且指明此屬性對應的數據字典是哪張表,哪一列為key,哪一列為value。這當然是可以實現的,稍微麻煩一些。
再假如數據類型A中某屬性應用了某數據字典,key-value1,數據類型B中某屬性應用了key-value2,它們的數據字典還可能相同的地方,比如value值都是相同的,為了節約存儲空間,我們把這兩個數據字典整合進一張表中key1-key2-value,處理方法如上,實現可以,但麻煩。數據字典問題不止是存在於定義數據和動態管理這兩個步驟中,下面的數據接入、數據查詢遇到的問題將更加棘手。
處理辦法:
a、唯一化key。
b、你的建議。
2、動態建表問題
上面說了,在刪除數據類型時,要
在Hibernate上下文中
處理好
它,但是沒有找到好的解決辦法。歡迎不吝賜教。
處理辦法:
a、懸而未決,你的建議。
上面講了
一、定義數據,二、動態管理,
還有:
三、數據接入,
四、數據查詢 兩個步驟,將在以后的文章中詳細說明。