85

服务读写分离架构,绝不推荐

 6 years ago
source link: http://mp.weixin.qq.com/s/kToQ14qOzBu1l1KkIjDcBg?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

服务读写分离架构,绝不推荐

Original 58沈剑 架构师之路 2017-12-18 15:20 Posted on

缘起

在《服务读写分离(读服务,写服务),是否可行?》中,对背景做了交代,互联网架构设计上,数据库可以读写分离,服务能否读写分离呢?

下面是两种常见的“服务读写分离”架构:

一、单纯服务读写分离

Image

如上图,服务化之后:

  • 业务方通过RPC分别调用读服务和写服务

  • 服务层分为读服务与写服务

  • 底层是高可用的数据库集群

二、服务和数据库同时读写分离

Image

读服务与写服务读写的是不同的数据库,如上图:

  • 写服务访问写库

  • 读服务访问读库

写库与读库是一个组从同步的集群。

这种架构设计好还是不好,网友进行了激烈的讨论,感兴趣的同学可以看下《服务读写分离(读服务,写服务),是否可行?》的评论,这里,分享下个人的观点。

三、先说结论

楼主旗帜鲜明反对服务区分读写分离。

四、小理由

  • 调用方对同一个基础服务,某一个RPC接口,在读服务,还是写服务,容易困惑

  • 对于同一个基础服务,服务数量翻倍了,运维更加复杂

五、强理由

  • 一般来说,垂直拆分,是按照“子业务”维度进行拆分,而不是按照“读写”维度进行拆分,这是模块化设计的基本准则

  • 完全打破了“服务化数据库私有”的微服务初衷

两个服务因为同一份数据库资源访问而耦合在一起,当数据库资源发生变化的时候(例如:ip变化,域名变化,表结构变化,水平切分变化等),有两个依赖点需要修改。

而好的设计,有变化产生时,只有一个需要修改(低耦合,高内聚),前段时间的“耦合”系列文章,已经多次提到了这一点:

小小的数据库,大大的耦合

小小的IP,大大的耦合

小小的公共库,大大的耦合

服务化了,耦合却更加严重了

  • 没法很好的添加缓存

大部分互联网业务是读多写少的业务,数据库读取最容易成为瓶颈,常见提升读性能的方式是,增加缓存

如上图,读服务的下游增加一个缓存,当有读请求访问时:

a)先访问缓存,如果命中,直接返回

b)如果缓存不命中,访问数据库,然后将数据放入缓存中,以便下一次能够命中

额,然后,这个架构中,这个方案是不可行的。

因为,写服务修改数据库时,缓存中的数据没有办法得到淘汰!!!

OK,有朋友说,写数据库之前,可以由写服务来淘汰缓存:

即,读服务与写服务都可以操作缓存。额,这个设计,又违背了“服务化缓存私有”的微服务初衷,两个服务因为同一份缓存资源访问而耦合在一起,当缓存资源发生变化的时候,有两个依赖点需要修改。

况且,如果真的两个服务访问相同的数据库和缓存,为什么不合成一个服务呢?

硬要拆成两个服务,不是自己玩自己么?

OK,有另外的朋友说,可以由写服务发消息来淘汰缓存:

a)缓存私有,只有读服务操纵缓存

b)数据库发生写请求时,写服务给MQ发消息,由读服务来淘汰缓存

这种设计:

a)读服务来淘汰缓存,本质是一个写请求,不是很奇怪么?

b)引入了一个MQ组件,引入更大的一致性风险

c)读服务和写服务如果是一个进程,岂不是更好么,干嘛硬要跨进程通信呢?

所以,还是一个服务更好:

Image

a)调用方无二义性,不纠结

b)好维护

c)数据库,缓存私有,无耦合

六、结论

互联网微服务架构,应该按照“子业务”进行微服务拆分,而不应该按照“读写”来进行微服务拆分,避免过度设计

以上仅为个人架构经验,希望逻辑是清晰的,供大伙参考,欢迎共同探讨(估计反对的声音会很多)。

末了,不少朋友提到了CQRS,未来撰文谈谈个人对CQRS的使用场景的理解。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK