为了账号安全,请及时绑定邮箱和手机立即绑定

多语言持久性

一种架构元模式(架构元模式):

这是我写的书《建筑元模式》中的一章。任何反馈都非常欢迎。这本书是免费的(CC BY许可下),并将以PDF格式提供下载。

解绑你的数据,让你的数据更加自由流动。 使用多个专业的数据库。

又称作:多语言持久性

这些有变种

独立的存储

  • 专门的数据库,
  • 私有和共享的数据库,
  • 数据文件/内容分发网络 (CDN)。

衍生存储

  • 只读副本,
  • 报表数据库 / CQRS 视图数据库 [MP],
  • 物化视图 [DDIA] / 内存视图,
  • 查询服务 [MP],
  • 搜索索引,
  • 历史数据,
  • 数据库缓存 / 缓存旁路。

结构层:数据服务层,供高层组件使用。

类型:扩展程序,源于共享库

参考文献:原始文章链接[1],相关的CQRS文章链接[2],[MP]的第7章,[DDIA]的第11章以及网上散布的各种信息。

注:[1] 原文链接 (https://martinfowler.com/bliki/PolyglotPersistence.html),[2] CQRS文章链接 (https://martinfowler.com/bliki/CQRS.html)。

你可以为系统中的每种数据或数据访问模式选择专用的数据库。这可以提高性能(因为每个数据库引擎都针对特定的使用场景进行了优化),在数据库之间分配负载,并可能解决性能需求之间的矛盾(例如,当你需要低延迟同时又需要大量存储空间时)。然而,你可能需要雇佣几位专家来管理这些数据库,以便充分利用并支持它们。此外,将数据分布在多个数据库中,应用程序需要负责保持数据的一致性(通过实现事务或者确保客户端获取到的数据是最新版本)。

性能

_多语言持久性_被应用以提升性能,这通过以下方式实现:

  • 针对特定的数据使用场景进行优化。一个数据库不可能在所有方面都表现良好。
  • 将读取流量重定向到只读的数据库副本。只处理写入操作的主数据库。
  • 将频繁使用的数据缓存到快速的内存数据库中,这样大多数客户端请求就可以不访问持久存储直接得到响应。
  • 构建系统中其他服务的状态视图,以避免查询它们。
  • 维护一个外部索引或内存图像,用于不需要历史数据的任务中。
  • 将旧数据存入较慢的存储介质。
  • 将只读的顺序数据存储为文件,通常更靠近下载它们的用户。

不过,读写分离带来了副本延迟,当数据一致性对客户端很重要的时候,这就会给客户端带来麻烦。

依赖项

一般来说,每个服务都依赖于它使用的所有数据库。如果它们共享数据集(一个或多个数据库是从其他数据库派生的),数据库之间也可能存在额外的依赖。

适用范围

多语言持久性帮助

  • 高负载和低延迟的项目。专门的数据库在分配适当任务时表现出色。缓存和只读副本可以减轻主数据库的压力。外部索引在这种情况下非常有用。
  • 事件溯源。事件溯源可以用来维护系统的当前状态。
  • 冲突的情况。一个无状态模块的每个实例在其处理每个请求时继承了其使用的数据库的许多特性。因此,如果有多个数据库,服务实例的特性可能会随每个请求而变化。

_多语言持久性_可能造成问题:

  • 小型项目。正确地设置和维护多个数据库其实并不简单。
  • 高可用性。你系统所用的每个数据库都会以自己独特的方式出问题。
  • 用户体验。对于具有读写数据库分离的系统,数据库之间的复制延迟会让你选择 编写同步代码等待读数据库更新,或者冒着返回过时结果给用户的风险。
关系:

多语言持久

独立存储的变体

在这种场景下,许多多语言持久案例使用多个仓库仅仅是因为没有一种单一的技术能够满足应用程序的所有需求,所用的数据库包含系统数据的不同子集:

专业数据库

数据库在不同的最佳使用场景中有所不同。(https://www.jamesserra.com/archive/2015/07/what-is-polyglot-persistence/)。你可以使用几种不同的数据库来为每种持久化的数据类型获得最佳性能

私有数据库和共享数据库

如果几个“服务”或“分片”通过系统的一部分数据联系起来,那么可以将该部分数据放入一个单独的数据库,该数据库可供所有参与者使用。所有其他数据仍然仅属于“分片”或“服务”私有。

数据文件 / 内容分发网络 (CDN,简称CDN)

有些数据喜欢保存在文件中。Web 框架从操作系统文件加载网页模板,并将图片和视频存储在 内容分发网络 (CDN)。该网络在全球范围内分发数据,以便每个用户可以从最近的服务器下载内容(这既更快又更便宜)。

注:具有衍生存储的变体

在其他情况下,有一个可写的主数据库作为主要的数据源,而其他数据库是从这个主数据库派生出来的。主要原因是减少主数据库的读取请求,并通常支持额外的特性:用于物化视图的数据聚合、用于全文搜索的索引、存储历史数据的大量数据集、用于内存缓存的低延迟特性。

派生数据库的更新可能包括以下几种途径:

  • 主数据库作为变更数据捕获(DDIA)功能,即记录变更的日志,
  • 应用程序更改主数据库之后(参见下面的缓存策略),
  • 另一个服务作为事件源(DDIA, MP),即应用程序事件流,
  • 一个专用索引器,定期爬取主数据库或网站的内容。

只读副本(不可修改)

数据库部署了多个实例,其中一个为主实例,接受所有的数据写入。这些变更会被复制到其他读副本,这些读副本用于读请求。将工作负载分散到多个实例中从而提高系统的最大吞吐量。这不仅大大提高了系统的可靠性,而且几乎可以实现瞬时恢复数据库故障。

报告数据库 / CQRS 视图数据库 [MP]

大家都知道,一个数据库要么适用于OLTP(事务),要么适用于OLAP(查询)。这里我们有两个数据库:一个优化了写入操作(通过事务保护写入),另一个则优化了复杂的分析查询。这些数据库不仅在模式上有所不同(如OLAP模式优化了查询),在类型上也可能不同(例如SQL和NoSQL)。

一个 报告数据库 从同一个子系统的可写数据库中获取数据,而一个 CQRS 视图 则从另一个服务接收一系列事件,从中筛选出对其所有者服务有用的事件(如上图最后一个案例所示)。这样,CQRS 视图 让其所有者服务查询其复制的数据,这些数据原本归其他服务所有。

物化视图(Materialized View)/ 内存图像(Memory Image)内存图像

事件源架构(作为_事件驱动架构_或_微服务_的一部分)主要是关于记录变化。服务会发布并持久化系统中发生的任何事件,放弃维护当前状态,转而记录变化的历史。因此,一个服务需要通过加载快照并回放进一步发生的任何事件来聚合历史记录,重建当前状态(类似于其他架构风格在数据库中存储状态),使其能够更新并开始操作。

查询服务(MP)

查询服务订阅了多个全功能服务的事件,并将其聚合到其数据库中,使其成为整个系统当前状态的实时视图。如果任何其他服务需要处理来自多个服务的数据,它会从查询服务中检索数据,查询服务已经将这些流合并,并通过一个简洁的API方法提供。

搜索引擎索引

在某些领域,普通数据库引擎并不自然支持某种特定类型的搜索。全文搜索,尤其是通过自然语言处理(NLP)增强的全文搜索,就是一个这样的例子。地理空间数据同样可能需要这种类型的搜索。如果你对主要数据库感到满意,你可以部署专门用于此类搜索的产品来建立外部搜索索引,并从主数据库定期更新数据。

历史记录

将销售记录存储在数据库中很常见。然而,一旦超过一个月或两个月,这些历史记录基本上不会再被修改。尽管偶尔会被查询,但它们仍会拖慢数据库速度。一些公司会将几个月前的数据转移到更便宜的档案存储上,此存储不允许修改数据,查询功能也较有限,以保持主要数据集的小型和快速。

缓存旁路策略 / 缓存旁路策略

数据库查询消耗大量资源,而数据库的扩展能力有限。这意味着在高负载系统中,尽可能绕过主数据库查询可以带来好处,这通常通过将最近的查询及其结果存储在内存中的缓存中实现。每个传入的查询首先从快速缓存中查找,如果找到,则可以直接返回结果,无需访问主数据库。

保持 缓存 与主数据库一致是最难的部分。这里有一些策略:写穿缓存写后缓存写绕过缓存预刷新缓存

进化

zh: 多语种持久化 结合派生存储通常可以与 CQRS 相关联:

使用读写数据库的服务被分为了独立的读服务和写服务。

摘要

多语言持久性 使用多个专用数据库通常来提高性能,通常需要在应用中实现事务处理或者数据同步延迟作为代价。

参考文献

[DDIA] 数据密集型应用的设计:可靠、可伸缩和可维护系统的核心理念。 马丁·克利普曼. 2017. 奥莱利媒体公司.

[MP] 微服务模式:以Java为例。 Chris Richardson. Manning出版公司 (2018)

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消