系列文章
- .Net微服务实战之技术选型篇
- .Net微服务实战之技术架构分层篇
- .Net微服务实战之DevOps篇
- .Net微服务实战之负载均衡(上)
- .Net微服务实战之CI/CD
- .Net微服务实战之Kubernetes的搭建与使用
- .Net微服务实战之负载均衡(下)
相关源码:https://github.com/SkyChenSky/Sikiro
前言
不少小伙伴看了我的博客的后跟我探讨问题时都离不开数据一致性、数据关联、数据重复创建的问题,只要大家做的分布式系统无论是否微服务化,或多或少都会遇到上述问题,而上述的问题的本质其实就是分布式事务、分布式数据关联与幂等性。这三个问题也是很多面试官在面试的时候检验应聘者是否有实践过分布式系统的经验的标准之一,而微服务作为分布式系统的架构风格,在实施过程中也无法幸免以上问题。
PS:评论可能比正文更精彩
分布式基础概念
用微服务架构风格设计出来的系统是典型的分布式系统。
分布式计算是指系统的工作方式,主要分为数据分布式和任务分布式:
数据分布式也称为数据并行,把数据拆分后,利用多台计算机并行执行多个相同任务。优点是缩短所有任务总体执行时间,缺点是无法减少单个任务的执行时间。
任务分布式也称为任务并行,单个串行的任务拆分成多个可并行子任务。优点是提高性能、可扩展性、可维护性,缺点是增加设计复杂性。
方式 |
描述 |
数据分布式 |
利用多台计算机并行执行多个相同任务 |
任务分布式 |
单个串行的任务拆分成多个可并行子任务 |
分布式系统必须面临的哪些问题?
我们日常工作的时候 ,接触到任务分布式的情况相对比较多例如:第三方支付请求,API编排数据关联。从场景划分主要分为单服务多数据库,多服务多数据库,多服务单数据库,以上三种场景都会存在多台服务器之间跨网络调用的情况,由原单进程单数据库内的简单实现的原子性、一致性变得不得不去面对因为跨网络请求得幂等性和数据一致性。
数据库一致性又分读和写,读对应着数据库跨库跨服务器的数据关联,写对应着分布式事务的数据最终一致性的处理。
数据关联的复杂度场景主要体现在分库分服务器与多接口数据关联的场景应该怎么解决?
分布式事务如果在单服务多数据库的场景下想必大家都会想出像Sql Sever的MSDTC的XA协议事务。如果是在多服务多数据库该选用怎样的分布式事务方案?
在分布式场景下幂等性的保证是无法避免的,网络是存在不确定性的,一个请求可能会成功,但也会因为客观因素导致失败,那么重新发起请求就无发避免的了,那么如何保证我不会重复创建数据与数据被覆盖呢?
下文我将从数据关联,分布式事务和幂等性三个角度进行叙述方案。
数据关联
数据关联的主要方案有三种,应用层数据聚合、冗余设计(反范式)、数据库从库集成。
方案名称 |
方案描述 |
应用层数据聚合 |
分别调用查询API,在业务逻辑层组装,适用于简单的关联。 |
冗余设计(反范式) |
在目标表添加冗余字段,适用于记录递增的,不适用于冗余字段更新频繁,实现起来简单,有扩展性问题 |
数据库从库集成 |
通过主从同步把相关表同步到一台服务器做跨库查询,适用于复杂查询、报表类的,有技术复杂度,从长远收益来看能应对多种场景 |
举个常见的例子:分布式情况下,比如现在有两个服务,分别是用户,订单。每个服务都是自己独立的数据库。用户数据库有用户信息表,订单数据都有关联用户的唯一id。
应用层数据聚合:
先调用订单服务得到订单列表后,再根据订单列表的用户ID集合调一次用户服务查询出用户列表。再通过内存遍历把订单列表与用户列表在业务层整合。
优点,实现简单;缺点,也是简单,该方案只能适合简单的查询过滤,以主表为驱动的关联。
public async Task<List<Order>> GetOrder() { //订单集合 var orderList = await _order.GetList(); //userId集合 var userIds = orderList.Select(a => a.UserId).ToList(); //关联用户集合 var users = await _user.GetByIds(userIds); //应用层数据聚合关联 orderList.ForEach(order => { order.Name = users.FirstOrDefault(a => a.UserId == order.UserId)?.Name; }); return orderList; }
冗余设计(反范式):
在订单表增加和用户有关信息的字段。
优点,实现简单,以应用层数据聚合方案有更多的过滤条件;缺点,冗余的字段如果更新存在同步问题,该方案适用于更新频繁少的递增日志类数据。
数据库从库集成:
通过主从同步技术,把相关的业务表同步到同一台服务器我们称为ReportDB,再通过在代码层面把数据源连接指向从库做跨库联表查询处理。
优点,通过强大的SQL解决复杂的报表类查询;缺点,拥有技术复杂度,需要数据库主从处理。