微服务设计迷思-数据存储

概述

后台数据的存储,其演变路线是非常明晰的。从全部用传统DBMS(oracle/mysql)到NoSQL配合db,直到现在的分布式数据库(NewSQL)。对应的,后台服务的主流架构也由单体式到SOA到微服务。当然,OLAP还引入了hbase,hive等大数据分析系统;然后各专业领域还有es或者neo4j之类的数据库,这里先不讨论这个。

这么多年,后端的设计其实没有大的改变,主要解决的就是流量问题:越来越庞大的访问量,和随之产生的数据量。银行证券等金融系统,最开始使用的是db2,sysbase等硬件数据库,然后是oracle这种较为成熟的单点数据库,这两年才逐渐拥抱开源,用pgsql代替oracle或是引入分布式数据库。访问量更为庞大、同时实时性也要求更高的互联网服务(很多金融系统的实时性其实很差,比如跨国swift汇款,可能要一个星期才到账。它涉及到风控等外部因素,实时性并不是第一位的。当然也有要求高的,比如股票购买。),则更为激进,在移动互联网时代就大规模引入redis、mongodb等NoSQL组件,以提高响应速度。

单点时代

即使C10K时代,单点数据库一般也足够满足需求。这时候最多考虑的是HA问题,mysql等常见db都提供了副本集的设计,也有比较成熟的集群架构(比如PXC架构)。这些技术在当今也很常见,中小规模的互联网公司仍在使用。oracle自带的分区功能也可以较少单表读写压力。

分片

稍大规模的服务,也可以根据key值进行手动的分片。虽然麻烦了一些,但是仍然够用,事务的一致性可以通过db得到保证。但是如果引入了NoSQL,如redis,不同组件之间的数据一致性和事务性无法得到保证。

在代码里进行分库分表耦合太深,所以出现了mycat这一类透明代理,这样代码里仍然把数据库当作单点处理。代理反向解析sql语句,将请求送入正确的db,并进行数据汇总。当然OLAP用这个会有join问题,分页问题等。

mongodb虽然支持集群,但是不支持表之间的join操作,所以其实也算是一种分片。关于分片的设计,前面的博客有分析(数据密集型应用设计)。

一库一服

有人说微服务与SOA最大的区别就是一库一服,实际上这并没有解决任何问题,只是转移了问题,并且带来了新的问题。

将服务拆分成更小粒度的服务,每个服务使用单独的数据库。这些单独的数据库仍然可以使用分片进行水平扩展分解压力,显然这是一种分治的思想。但是分库破坏了db的acid特性,会导致数据失去强一致性。

以支付服务为例,在同一个db中,可以利用数据库的事务性保证付款和减少库存两个操作的一致性。如果通过微服务,支付服务和库存服务独立的情况下,只能拆分成支付服务扣钱->通知库存服务减少->库存减少成功/失败->回调支付服务确认,这就是所谓的TCC解决方案。

显然,某些业务(比如股票/火车票购买)是不能用这种模型的,扣钱成功必须保证买到商品,否则用户肯定会投诉的。所以这时候就只能用二阶段提交等一致性搞好的方案,但是效率又得不到保证。

分布式数据库

NewSQL的分布式数据库,从db层面上解决了数据扩展性的问题。说白了,它是将分布式事务的问题从服务层面重新转回db层面。于是一切又回到了单点时代,对于服务而言,分布式数据库就当作一个单点来使用即可。

当然分布式数据库至今仍然有一些问题,不过已经可以在生产上使用。

未来

从程序设计的角度来看,一切正在回到初始。

ServiceMesh和NewSQL,这两项技术将一切外部组件的复杂性进行了屏蔽,那么很多业务就重新回到CURD的简单工作了。

然而终究没有银弹,在很长一段时间内,上面的技术都会并存于世。合理的进行选型设计才是架构师应该考虑的问题。

多嘴说一句,Dubbo, SpringCloud这种架构注定会过时的,它将外部组件的复杂性引入了代码,这不符合低耦合高聚集的程序设计原则,注定会被ServiceMesh替代。