CQRS模式

背景

 简单需求

当系统中的数据模型层级较少、数据模型足够简单时,模型与数据库可以直接进行映射。这种简单数据模型使我们不需要针对其相互关系进行复杂的建模设计,直接在工程中使用经典的三层模型就足以支撑项目需求。

这种简单系统需求,过度设计反而会增加后续维护、重构的成本(并不能保证预设计能完美符合后续需求)。同时,对于简单系统,我们大部分的需求都只涉及其中的少量数据模型逻辑处理。

直接对数据模型进行CURD就能满足需求,简单需求,不需要特别区别查询和增删改的程序结构。

复杂需求

系统具有一定复杂性,这种复杂性可能是源于访问频次、数据量或者是数据模型数量。这时候我们遇到的问题是数据在查询和更新的需求差距逐渐变大。

  • 频次:数据的查询频次会远高于新增、更新、删除频次。

  • 数据量:数据量变大后会增加对数据进行分库分表的设计诉求,从而导致数据查询变得的复杂性(涉及分表关键字)。

  • 数据模型数量:数据模型数量的增大,会导致在进行新增、更新与删除操作时同时影响的数据模型变多,而在查询时同时跨多模型的查询条件会让查询的性能具有极大的挑战性。

 当需求具有一定的复杂性后,根据引入复杂性的不同,会导致系统功能上需要用更加复杂的设计来对需求的复杂性进行支撑。同时可以发现,引入的不同复杂性在增删改和查询方面的带来的功能需求差别很大。

需求的复杂性会放大程序中查询和增删改的设计差异。

简介

CQRS(Command-Query Responsibility Segregation)即命令查询职责分离,其中我们称增删改为命令型操作,是一种软件架构模式,也是处理复杂问题的一种具体实现方案。旨在通过将命令(写操作)和查询(读操作)分开处理来解决应用程序的复杂性,以提高系统的可维护性和性能。

CQRS本质上是一种读写分离设计思想,这种框架设计模式将命令型业务和查询型业务分开单独处理。通过这种方式,CQRS可以针对命令和查询单独进行业务模型上的设计,从而用更加适合各自场景的方案与组件来提供能力。

基本原理

传统的系统架构中,写操作与读操作通常是相互耦合的。这意味着,即使读操作的流量很高,每个写操作都会对读操作造成影响,导致整个系统变慢甚至宕机。为了解决这个问题, CQRS模式将读操作和写操作分开,让它们互不影响,从而提高系统性能和扩展性。

简单做法

简单的CQRS的做法是:CQ两端数据库表共享,CQ两端只是在上层代码上分离。

这种做法在不对数据库进行分离设计的情况下,CQ两端在上层代码进行分离个字单独维护,例如命令型的都用xxxManagerController、xxxManagerService来定义,而查询则直接用xxxController、xxxService定义。

因为使用同一个数据库,所以没有CQ两端的数据一致性问题。但因为已经对上层代码进行了抽离,所以可以满足一些设计特性如:

  • 命令应基于任务,而不是以数据为中心。

  • 命令可以放置在队列上进行异步处理,而不是同步处理。

  • 查询从不修改数据库。 查询返回的 DTO 不封装任何领域知识。

这种方案可以满足代码逻辑上的分离维护,但由于是使用同一数据库表,所以无法根据CQ两种业务的特点单独进行模型设计。

实现方式

CQRS的实现方式主要包括逻辑分层模型分离事件驱动事件溯源等。

逻辑分层

逻辑分层是非常重要的。通过将系统分为命令层和查询层,CQRS可以更好地满足不同操作的需求。命令层主要关注如何修改系统的状态,包括创建、更新和删除等操作。而查询层则关注如何从系统中读取数据,包括查询、过滤和排序等操作。通过将命令和查询分离,可以使系统更加专注于不同操作的处理,提高系统的可理解性和可维护性。

模型分离

模型分离也是非常重要的。通常情况下,CQRS会有两套不同的模型,一套用于处理命令,另一套用于处理查询。命令模型主要关注如何修改系统的状态,而查询模型主要关注如何从系统中读取数据。通过模型分离,可以根据不同模型的需求进行优化,提高系统的效率和可维护性。此外,模型分离还可以使系统更容易进行扩展和升级。

事件驱动

事件驱动是CQRS的另一个重要特点。在处理命令时,系统会生成相应的事件。这些事件不仅记录了状态的变化,还可以用于更新查询模型。通过事件驱动,系统可以实现松耦合的组件间通信,提高系统的可扩展性和灵活性。此外,事件驱动还可以帮助系统实现实时数据处理和分析。

事件溯源

事件溯源是与CQRS常常一起使用的技术。事件溯源通过存储和重放事件来持久化对象的状态,而不是直接存储对象的状态。通过记录和回放事件,系统可以重建对象的状态历史,实现更精确的数据追踪和分析。事件溯源还可以帮助系统实现数据恢复和回滚,提高系统的容错性和可靠性。

当结合使用 CQRS 和事件溯源模式时,需要考虑以下方面:

  • 在任何写入和读取存储分离的系统中,基于此模式的系统只会最终一致。 正在生成的事件与正在更新的数据存储之间的存在一定延迟。

  • 本模式会增加复杂性,因为必需创建代码以启动和处理事件,组合或更新查询或读取模型所需的适当视图或对象。 结合事件溯源模式使用时,CQRS 模式的复杂性会使实现难以顺利完成,需要使用设计系统的其他方法。 但是,事件溯源可以更加轻松地对域创建模型,从而可以很方便地重新生成视图或创建新视图,因为它保留了想要执行的数据更改。

  • 通过重放和处理特定实体或实体集合的事件来生成用于读取模型或数据投影的具体化视图可能需要大量的处理时间和资源。 特别是当如果需要长时间求和或分析值时,因为需要检查所有相关的事件。 通过以计划的间隔(例如已发生的特定操作的总计数或实体的当前状态)实现数据快照来解决此问题。

特点 

1.更好的可维护性: 由于CQRS模式明确了每个应用程序功能的职责,因此可以更轻松地进行测试和维护。这使得开发团队可以更专注于各自领域的工作,最终提高工作效率。

2. 更好的可扩展性: CQRS模式允许处理读/写操作的服务器独立地扩展,从而可以更具灵活性地应对高流量和其他挑战。

3. 更好的性能: 将写操作和读操作分别处理,可以使写操作和读操作的性能独立优化,这可以使系统更快,更容易适应不断变化的负载。

应用场景

适用于那些需要高效的读/写分离应用场景,以下是常见的应用场景:

1. 高负载应用: CQRS模式的良好扩展性使其成为高负载应用的首选,如电子商务、在线游戏等。

2. 大型企业解决方案: CQRS模式可以帮助企业简化复杂的解决方案,提高可维护性,更好地适应业务需求。

3. 监控系统: CQRS模式也是一种理想的解决方案,当需要在聚合实时数据方面进行迅速读取时,可以使用CQRS模式实现可靠和高效的监视解决方案。

性能

在代码分离的基础上,可以再将数据存储的模型进行物理分离,读取存储可以是写入存储的只读副本,使用多个只读副本可以提高查询性能;也可以为读取模型单独设计库表。单独对查询和更新进行模型设计可以减小设计和实现的难度。并且此时读取数据库可使用自己的已针对查询进行优化的数据架构。比如读数据库可以直接存储查询数据宽表从而避免进行join操作或者复杂的查询映射。甚至可以针对读取操作使用mongo或者es等nosql数据库对查询逻辑进行增强。

分离后的数据将存在在不同的数据库中,查询的数据由C端同步过来。通常,这是通过在每次更新数据库时使写入模型发布事件来实现的。 而说到数据同步则就有同步执行和异步执行两种方案:

  • 同步:导致性能降低,但是可以保证数据的强一致性。

  • 异步:拥有较高的性能,但需要系统接受最终一致性的。

同样的,这种同步也可以解释为对缓存进行的更新,即:查询数据库是使用缓存,而写入数据库使用普通SQL,两者之间数据同步通过领域事件实现最终一致性。

使用评估

如果希望通过使用CQRS实现的系统性能提升,需要评估当前系统架构以及个人经验是否有以下能力:

  • 复杂性设计:尽管CQRS基础理念较为容易理解,但是这种模式会导致系统的构建复杂度上升,尤其是进一步使用事件溯源模式时。

  • 消息队列处理:在进行高性能设计的时候,通常会使用消息处理命令和发布更新事件。在此情况下,应用程序必须处理消息失败或重复的消息。

  • 最终一致性:如果分离读取和写入数据库,读取数据可能会过时。 必须更新读取模型存储,以反映对写入模型存储区所做的更改,并且在用户根据过时的读取数据发出请求时,可能很难检测到这种情况。

选型建议 

对于以下场景不建议引入CQRS:

  • 领域或者业务十分简单。

  • 基本的CRUD就可以支撑完整的系统数据访问需求。

如果系统存在一定的复杂性,并且有以下的特点,则可以根据业务特点,选用适合的CQRS实现方式。

  • 在用户操作中,需要在用户界面中进行一系列的复杂操作来最终定义、组装、修改领域模型。写模型需要有完成的命令处理堆栈,包括:输入验证、业务处理、业务验证。而读模型只需要返回视图中所用到的DTO数据。读模型与写模型只需要最终一致性关系。

  • 对于用户的操作访问,需要以较小的粒度定义命令,并通过合并命令的方式避免命令冲突。

  • 数据写入和数据读取之前存在比较大的性能区别,需要分开进行数据优化。尤其是读取次数远大于写入次数的场景,可以对读模型进行水平扩展。

  • 当团队人员可以分拆分,组成专门针对复杂业务写场景的组,以及专门针对高频查询和用户界面的组。

  • 当系统随时间不断演进,不断包含多个版本的模型,或者业务规则会定期修改。可以在写模式中包含多个版本的模型,而读模式中使用统一的视图模型。

  • 与其他系统集成时,希望不会受到其他系统故障的影响(读写库表分离)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值