各式结构化数据 动态 接入-存储-查询 的处理办法 (第一部分)

各式结构化数据的动态接入存储查询,这一需求相信有很多人都遇到过,随着实现技术路线选择的不同,遇到的问题出入大了,其解决办法也是大相径庭。数据存储在哪儿,是关系型数据库,还是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等(请原谅我的灵感来自于《绝命毒师》),它们肯定是要事先定义好的,我们数据库中能够接入的数据必须由这些组成,如 StringIntegerLongBigDecimalBooleanCalendarDateTimeBlob等,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 ^[sS]{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 ^[sS]{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(truetrue);来生成表。

 
其实动态管理这一步,就不只是动态建表这么简单,上面说的比较狭隘,从广义上理解,它应该被分解为下面几个操作: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、悬而未决,你的建议。
 
上面讲了一、定义数据,二、动态管理,还有:三、数据接入,四、数据查询 两个步骤,将在以后的文章中详细说明。





原文地址:https://www.cnblogs.com/wgp13x/p/4019600.html