读 | Software Architecture:The Hard Parts 之耦合
读 | Software Architecture:The Hard Parts 之代码复用
读 | Software Architecture:The Hard Parts 之数据所有权
读 | Software Architecture:The Hard Parts 之分布式数据访问
读 | Software Architecture:The Hard Parts 之事务
读 | Software Architecture:The Hard Parts 之约定
将单体系统拆分为微服务时,需要为数据(表)指定所有者——哪个服务对数据(表)具有所有权。正常理解,具有写操作的服务才会涉及到所有权,这样就可以划分为三种情形:
如果一张表只有一个服务在写,就认为服务拥有这张表,可以毫无负担地把这张表分拆给服务,组成一个独立的微服务。
如果一张表被部分服务写,此时这部分服务对表具有联合所有权,需要进一步明确数据所有权,有 4 种处理方法:
1. Table Split
如果多个服务的写表操作是分散在不同的字段,那么可以将一张表打散为几张表,每张表包含不同服务写到的字段,通过表 id 进行关联。
伴随而来的问题是需要在不同的表之间(至少)进行增/删同步,并且因为对分拆后的其他表没有所有权,还需要通过服务间调用来同步,这就涉及到 CAP 理论中阐述的问题,是选择可用性优先(服务间调用失败,系统整体状态不一致时,仍然可用),还是一致性优先(服务间调用失败则不可用,保障系统整体状态的一致性)。
2. Data Domain
当被多个服务共享时,可以将共享的表放到同一个 Schema 或 Database 里,此时共享数据不归属于任一服务,而被所有相关服务共享。
带来两个问题:
3. Deletegate
可以将写操作委托给某一个服务,由它统一做。如何选择委托,有两种方式:
委托的主要问题是:它也是一个服务,但干了职责外的事情。同时,委托导致了服务间耦合和通信。因此它适用于不要求原子性事务、接受通过异步通信方式来达到最终一致性要求的情形。
4. Service Consolidation
这种方式其实是转换思路,如果数据不能分开,就把服务合起来。所谓山不过来,我就过去。
缺点是服务的粒度变粗了,测试范围变大、部署风险增大、容错性降低等问题随之而来。
如果一张表被所有服务写,此时所有服务对表具有公共所有权,如果服务数量变得很大,将带来诸多问题(变更控制、连接饿死、可扩展性、容错)。
一种常用方法是指定一个默认服务作为数据所有者,只有它能直接写数据,其他服务通过调用默认服务来写数据。服务间调用可以使用 REST、gRPC、消息队列等通用技术。
在一个复杂系统内的总图可能如下