微服务框架的存储架构

做完横向拆分后,每一个服务其实都对应了一个存储矩阵,服务中每一个业务处理模块都对应几个数据库,这几个数据库是水平切分的。最简单的水平切分规则是按主表主键大小,比如前100万条数据存A1数据库,100万-200万存A2数据库,200万-300万存A3数据库等。

数据库存储集群的方案目前业界有很多。关于关系型数据库和NoSQL使用场景也是争论不休。我们认为,如果是事务性的应用必须使用关系型数据库,比如电子商务和互联网金融,事务性比较弱的可以采用NoSQL,比如博客、新闻等应用,事务性可以作为是否选用NoSQL数据库的标准。即便是关系型数据库集群在编程方面也存在多种方案,比如:

这个技术都各有优缺点,有的不支持本地数据库级事务,像Amoeba;有的支持数据库本地事务但又不支持跨库情况下的Join、分页、排序、子查询,像Cobar。从本质上说,数据存储也是业务处理的一部分,举个简单的例子,还是那个转账的例子:A在Web页面提交一个转账给B申请,业务处理模块拿到转账申请从A账户扣除转账金额(数据库操作)并给B的余额加上转账金额(数据库操作),并记录操作日志和通知B。很明显,数据库操作是这个转账业务处理操作的一部分,所以如果在业务层直接进行分布式存储和按需存储,事情将变得简单的多(spring提倡的链式事务管理就是这种方式)。如果你不把数据库存储作为业务层处理的一部分,而封装成一个独立运行的模块,那么就会出现各种各样的问题。

如果将数据库路由键放入服务层,一个典型业务层接口类定义如下:

public interface SporeService {

  public List<Spore> searchByCondition(RouteKey routeKey);

  public Spore findById(Long id,RouteKey routeKey);

  public void update(Spore spore,RouteKey routeKey);

  public void insert(Spore spore,RouteKey routeKey);

  public void delete(Long id,RouteKey routeKey);

  }

 

这是业务层定义的接口,在这里增加了一个数据路由的key,为什么把数据路由的选择放到业务层接口,我们前面提到过,数据访问本身属于业务的一种操作。就比如你今天去A银行取钱和去B银行取钱完全是两种业务。数据路由键让每一个业务方法都可以对应一个数据库矩阵,而key就是该业务方法所需数据库的选择方法。可能有的程序员认为在每个方法增加一个RouteKey routeKey参数太过于繁琐,是不是可以定义成继承关系或注解等等,那相比这种方式更加繁琐,而且不适合于所有情况。