最近写了一个分表查询的lib,封装了一些数据结构,可以简化对于分表查询的实现。这个问题主要体现在,对分表进行查询时,原有的单表查询需要进行修改,或者需要在内存中进行处理。修改单表的sql可能导致性能问题,原有的优化可能失效。内存处理则需要一些技巧,搞不好会把太多内容加载到内存中,导致内存被耗光。

我的lib主要使用iterator的模式来解决数据库的读取的问题,每次只读取1000条,放在内存中,当iterator的next移动到缓冲区的尾部时,再次读取1000条,如此可以保证获得所有的数据。当然我们也可以采用双buffer的方式,一个用于提前读取,一个用于输出。不过现在还没这么做。

虽然command模式不是lib的重点,但是这个模式却教会了我一些东西。因为最开始的时候,我并没有想到它。开始,我想的是查询分成几类:

1. 查询结果做union all;

2.查询结果做order by

3. 查询结果做group by

4. 查询结果去重

我发现每个查询都有这样一些参数,一组表名,一个数据库查询的封装,如果需要排序的话,得要一个comparator,如果需要合并的话,得要一个MapReduce。

最初的实现是:

class QueryService{         
  Iterator<T> query1(a,b);          
  Iterator<T> query2(a,b,c);          
  Iterator<T> query3(a,b,d);          
}

后来发现参数太多,初始化比较麻烦,于是改成:

X{         
a,b          
}          
X1 extends X{          
c          
}          
X2 extends X{          
d          
}          
class QueryService{          
  Iterator<T> query1(X);          
  Iterator<T> query2(X1);          
  Iterator<T> query3(X2);          
}

接着发现,这个设计缺乏扩展性,如果将来需要添加新的查询类型,或者想要换一种实现方式,就得修改整个query service。而且X X1 X2只有属性,太浪费了,没有实现封装的目的。更重要的,各个查询其实如果设置好上下文,调用方法都一样,返回值一样。这不就是command的用法吗。

Cmd{         
Iterator run();          
}          
X implements Cmd{          
a,b          
}          
X1 extends X{          
c          
}          
X2 extends X{          
d          
}          
class QueryService{          
  Iterator<T> query(Cmd);          
}

这样上面的难题就迎刃而解了。为什么我一开始没想到呢。我想到了不同的种类的查询,过分关注他们的不同点,忽略了查询的共同点。所以以后工作中,一定要先抽取共性,再寻求个性。共性有利于整体考虑,个性有利于细化架构,发现新的机会。