为数据库表设计可扩展的字段

数据库表设计可扩展字段
在平时的系统设计中,要充分考虑扩展和复⽤,后⾯维护过程中出现类似的场景的时候,能够有效的复⽤之前的。在快速响应业务的同时,也确保系统的稳定性。如何设计扩展性强的数据库结构呢,这⾥从⽇常⼯作中学习了⼀些经验,有⾃⼰团队内部实现的,也有其他团队的实践。
1、⼆进制位
在数据库中设计⼀个字段,暂且叫“options”,这个字段存储的是数值,可以理解为⼆进制的组合。例如⼀个⽤户既有A标签(标签可以是服务),⼜有B标签,这时候类似“有没有”或者“是否包含”的场景,⾮常适合这种。⼀个字段搞定多个布尔业务场景。
下图简单描述位数和业务以及具体存储的关系。
问题1:options允许直接设置值吗?
不允许,必须通过append或者remove来去掉⼀个特定位数的值。否则会导致问题,例如我⽤了第⼀位,第⼆个业务⽤了第⼆位,第⼆次直接设置了值,那就把原先的冲⾛了。
问题2:如何实现append或者remove的⽅法?
业务激活把options这个属性设置为私有,然后通过⼆进制的操作来添加或者去掉值。
问题3:如何⽐较options是否包含特定的位数?
⾸先,⼆进制哪⼀位表⽰那个业务场景,最好在定义常量,例如2的3次⽅表⽰⽤户含有A服务。然后通过⼯具类来判断⽤户是否⽀持或者包含次服务。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52package com.taobao.logistics.domain.dataobject;
public class OptionsTest {抽油机模型
private static final Long A_SERVICE = (long)Math.pow(2, 0);
private static final Long B_SERVICE = (long)Math.pow(2, 1);
private static final Long C_SERVICE = (long)Math.pow(2, 2);
private static final Long D_SERVICE = (long)Math.pow(2, 3);
/**
* ⼆进制中的属性名称,可以直接对应数据库中的字段
*/
private Long options;
/**
* 添加⼀个特定的位数,targetProperty是这个位的数值,例如2的3次⽅等
* @param targetProperty
*/
public void appendOptions(long targetProperty) {
if(null== options) {
options = new Long(0);
}
this.options |= targetProperty;
}
/**
* 在options中移除特定的位数,判断是否包含
* @param targetProperty
*/
public void removeOptions(long targetProperty) {
if(null== options) {
options = new Long(0);
}
ainOptions(targetProperty)){
this.options &= (this.options - targetProperty);
}
}
public boolean containOptions(long targetProperty) {
if(null== options) {
return false;
}
return((this.options & targetProperty) == targetProperty);
}
public static void main(String[] args) {
OptionsTest op = new OptionsTest();
op.appendOptions(A_SERVICE);
op.appendOptions(B_SERVICE);
op.appendOptions(C_SERVICE);
op.appendOptions(D_SERVICE);
System.out.println("options的值:"+op.options);
System.out.println("移除第0位和第2位后的,options的值:"+op.options);
System.out.println("是否包含测试,第3位:"+op.containOptions(D_SERVICE));
}
}
2、feature或者attribute字段来存储KV接⼝数据,以此来进⾏扩展
在设计表字段的时候,有些新增的字段我们是⽆法预料的,新增加的字段,如果没有检索需求,是可以通过key-value的形式来在⼀个数据库字段中进⾏扩展的,这样业务上⾯增加了⼀个新字段,只需要简单定义⼀下key即可。其余的数据库表变更就不⽤做了,⽅便快捷。
例如下图,key和value通过“:”来做分割,不同的KV之间通过“;”来做分割,然后通过代码来做DB中数据的保存和隔离。
问题1:外部在调⽤的时候,能否⾃定义key?
这个建议最好不要外部直接⾃定义,如果A团队维护的feature字段,B团队能够在A团队完全不知情的情况下写⼊⼀个新的key,我觉得是有点危险的。⽐较给⼒的做法是,B团队如果需要在feature中增加⼀个key,那向A团队申请,A团队在配置或者常量中增加这个key(只有配置过的常量才能写⼊),这样就能达到扩展并且相对安全的⽬的了。
问题2:value中如果包含分隔符怎么办?
在插⼊value的时候,最好是做⼀个校验,判断value是否包含feature中定义的分隔符,如果包含,可以转义或者替换⼀下。否则会造成在解析分割的时候出现混乱的情况。
问题3:feature的内容超过数据库的长度怎么办?
⼀般情况下,feature字段最好预留长⼀点,这样保证插⼊相对多的数据。另外可以在数据库中申请多个feature,例如feature1、feature2,这样保持⾜够扩展,但是这并不是长远之计,后⾯会有基于数据库中新表的扩展。
问题4:feature中的数据如何解析?
这个其实在上⾯的图中就能理解了,解析字符串,然后转换为java中的Map数据结构,之后外部调⽤,统统依赖⼼的map属性来完成。
上代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68package com.taobao.logistics.domain.dataobject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.alibabamon.lang.ArrayUtil;
import com.alibabamon.lang.StringUtil;
public class FeatureTest {
private static final String K_V_SPLIT = ":";
private static final String KV_KV_SPLIT = ";";
private static final String KEY_NAME = "name";
private static final String KEY_AGE = "age";
private static final String KEY_SEX = "sex";
/
**
* 把定义的key放在List中,⽤于做校验
*/
private static final List<String> KEY_ALLOW = new ArrayList<String>();
static{
KEY_ALLOW.add(KEY_AGE);
KEY_ALLOW.add(KEY_NAME);
KEY_ALLOW.add(KEY_SEX);
}
/**
* 原始的feature内容,对应数据库中的表字段
*/
private String feature;
/**
* 解析之后的KV对应关系,存储在Map中,⽅便对象操作
*/
private Map<String,String> featureMap;
public static void main(String[] args) {
FeatureTest ft = new FeatureTest();
ft.addFeature(KEY_NAME, "iamzhongyong");
ft.addFeature(KEY_AGE, "11");
ft.addFeature(KEY_SEX, "0");
ft.addFeature("funk", "zhongyong");
System.out.println("⽬前Feature中的内容:"+ft.feature);
System.out.println("移除Sex之后的内容:"+ft.feature);
System.out.println("输出feature中的name:"+ft.getFeature(KEY_NAME));
}
/**
* 校验传⼊的key是否合法金银花绿原酸
*/
public boolean checkKeyIsAllow(String key){
return ains(key);
}
/**
* 根据特定的key获取value值
* @param key
* @return
*/
public String getFeature(String key){
initFeatureMap();
String value = (key);
return value==null? null: value;
}
/
**
* 移除⼀个keu对应的value内容
* @param key
* @return
*/
public boolean removeFeature(String key){
initFeatureMap();
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
boolean flag = false;
ainsKey(key)){
resetFeature();
flag = true;
}else{
flag = false;
}
return flag;
}
/**
* 移除所有的feature内容
自动化机械手臂*/
public void removeAllFeature() {
this.featureMap = null;
this.feature = null;
}
private void resetFeature(){
StringBuffer sb = new StringBuffer();
for(String key : featureMap.keySet()) {
String aValue = (key);
sb.append(key);
sb.append(":");
sb.append(aValue);
sb.append(";");
}
this.feature = sb.toString();
}
private void initFeatureMap() {
if(null== featureMap) {
featureMap = FeatureMap(feature);
}
}
private Map<String, String> getFeatureMap(String features) {
Map<String, String> featureMap = new HashMap<String, String>();
if(StringUtil.isNotBlank(features)) {
String[] featureArray = StringUtil.split(features, KV_KV_SPLIT);
if(ArrayUtil.isNotEmpty(featureArray)) {
for(String feature : featureArray) {
if(StringUtil.isNotBlank(feature)) {
String[] aKeyAndValue = new String[2];
int index = feature.indexOf(K_V_SPLIT);
if(index > 0) {
aKeyAndValue[0] = feature.substring(0, index);
aKeyAndValue[1] = feature.substring(index + 1);
if(ArrayUtil.isNotEmpty(aKeyAndValue)) {
String key = aKeyAndValue[0];
String value = aKeyAndValue[1];
if(StringUtil.isNotBlank(key)&& StringUtil.isNotBlank(value)) {
featureMap.put(key, value);
}
}
}
}
}
}
}
return featureMap;
}
/**
AR空间定位* 添加⼀个KV的数据到feature中去
* @param name
* @param value
*/
public void addFeature(String name, String value){
无油烟锅
if(StringUtil.isNotBlank(name) && StringUtil.isNotBlank(value) && checkKeyIsAllow(name)) { initFeatureMap();
featureMap.put(name, value);
resetFeature();
}
}
}
3、构建扩展表,灵活⽀持KV类扩展
刚才的feature中的扩展,有个弊端,就是feature不能⽆限的扩展,有没有办法能够相对灵活的扩展,当然有了呵呵。设计⼀个扩展表,这个扩展表来表⽰⼀个扩展的key和value的值。这样增加key的时候,就能相关⽐较灵活了。
数据库表字段设计如下:
其中a表是业务主表,a_ext是业务的扩展表,biz_id记录了a表中的业务主键ID,kv_type_id来定义扩展的key的信息,可以理解类似feature中的key,另外biz_value值是扩展字段对应的值。
通过⼀个例⼦说明:
基于上述三个点,我觉得在系统扩展性⽅⾯能相对⽐较好,这样能够相对⽐较灵活的添加新东西。
发现有些图⽚不能正常展⽰,iteye的图⽚上传功能着实不好⽤。我在附件中添加了PDF格式的⽂档。
(388.1 KB)
下载次数: 49

本文发布于:2024-09-22 20:27:53,感谢您对本站的认可!

本文链接:https://www.17tex.com/tex/3/193306.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:扩展   数据库   字段   团队   业务
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议