web应用从单点向高并发架构演变时往往遇到最大的问题就是数据库的分布式存储。因为web应用本身就可以集群部署,但其所使用的数据库确是单点的。如果一个web应用开始的时候没有考虑数据库的分布式架构,那么等到要进行数据库集群改造时会发现困难重重,此时通常的做法是将原系统拆分成多个子系统,然后每个子系统访问一个数据库,这几乎重写了整个系统(如果这还不能满足需求,大型企业接下来会增加数据存储总线)。很多厂商都是这么做的,包括淘宝。如果你开始你能够考虑到数据库集群,并且这种集群的设置并不增加开发工作量和难度,那么将来可以为企业节省大量人力物力。
一般来说我们在开发一个应用时,需要对应用进行分层,一般分为界面层、业务层、存储层,这有利于开发,也有利于将来部署运行。这里并不特指Web程序,桌面应用也是一样。应用系统从单点运行演变为分布式应用一般需要经历下面3个步骤。
图(1),系统的各层属于一个应用,彼此之间以API的方式(进程内)调用,可以满足小并发量需求,一般的桌面应用使用此种运行方式。图(2)系统的业务层被拆分,拆分成许多独立的子系统(或同一系统的业务处理模块单独运行,部署多个),前端界面和业务层以RPC进行调用,业务层和存储层以API的方式(进程内)调用,此种运行方式可以满足中、高并发量的处理请求。图(3)就是分布式的最终状态了,所有功能都进行集群部署,层与层之间都是RPC调用。
关于分布式,这里不再多说,本文主要讨论的是存储架构的问题。我们知道最简单的一个Web应用就可以进行图(2)那样的部署(如果需要登陆,那么还需采用session共享技术),这是因为Web应用本身界面层和业务层默认就是http调用,属于远程调用的一种。我们假设有一个电子商务类应用,运行方式如下图:
因为Web应用本身界面是由页面组成,并且可以缓存,我们也可以把界面层看成进行了集群部署。所以说,对于一个简单的web应用是可以很容易做到界面层和业务处理层集群部署和运行的。那么随着业务量增大和并发请求的增加,一个web应用最先遇到的瓶颈就是存储。从开发角度最容易看出这个问题,一个默认web应用,在访问数据库时都是使用同一条数据库连接(或同一个连接池),只能指向同一个数据库。所以,如果我们能够在一个web应用中,让业务层按需建立多个数据库连接,那么也就实现了存储的集群化。
一般来说一个应用遇到存储瓶颈,可以通过缓存进行优化,如果缓存也不能满足情况,就只能拆分数据库了。拆分数据库一般分为两步走,首先是数据库按业务垂直拆分,不要业务模块的数据存储到不同的数据库,这也称为数据库的垂直拆分,如下图(1)。这是指同一个应用来说的,微服务架构本身包括多种应用,每种应用有自己的数据库访问模块,每种应用又可以包括多个业务处理模块,垂直拆分后,每个业务模块对应一个数据库。
因为微服务架构本身就提倡将大应用拆分成多个子服务,每个应用有自己的数据库,这已经相当于进行了一次存储的垂直拆分。再进一步垂直拆分,一个子服务又可以使用多个数据库,到这一步,一般的高并发量就足够处理了。但是一个系统往往有些模块会持续不断的增加数据,比如电子商务中的订单模块,这会导致单表数据非常大。时间久会给查询和计算带来严重瓶颈,这个时候就需要数据库水平拆分登场了,如图(2),数据库水平拆分是把之前某个业务处理模块对应的一个数据库,再按水平方向拆分,使一个业务处理模块对应操作几个数据库,使原来一张表中的数据按规则存储到几个库中。
水平拆分的前提条件是拆分的相关表必须属于同一个聚合(领域驱动中的聚合)。聚合中的根就是拆分依据的主表,我们根据主表数据计算规则进行数据库拆分。这种拆分方式非常重要,因为这种拆分方式形成的数据库横向集群支持数据库级事务。比如说一个用户聚合,包括用户、资金账户、联系人3张表。那么用户就是主表,此时如果我们按用户ID拆分数据库,你会发现虽然是集群存储,但当某个用户访问自己的数据时,这些数据始终在同一个库,同一个库就支持数据库事务。