复杂业务系统架构:CQRS 读写分离与 ES/RabbitMQ 基础指南

针对业务量庞大、查询维度复杂的系统(如全房通的合租管理),传统的"增删改查全靠 MySQL"模式会面临巨大的性能瓶颈。为此,现代架构通常引入 CQRS(命令查询职责分离) 思想,结合 Elasticsearch (ES)RabbitMQ 来实现高性能的读写分离。

如果你之前熟悉 Solr 但较少接触 ES 和 RabbitMQ,本文档将帮你快速建立概念映射,并温习它们的基础语法。


一、核心架构思想:CQRS(读写分离)

CQRS 的全称是 Command Query Responsibility Segregation(命令查询职责分离)。 简单来说,就是把系统中的**读操作(查询数据)写操作(修改数据)**彻底拆分开,甚至使用不同的数据库来支撑。

为什么需要 CQRS?

  1. 写操作(命令 Command) :比如"分配房间"、"退租"。这些操作需要强一致性、事务支持(ACID)。所以必须由 MySQL 等关系型数据库来兜底。

  2. 读操作(查询 Query) :比如"查询某店面下、已租完、面积大于20平的房源列表"。这种多维度、高并发的组合查询,MySQL 的 B+ 树索引扛不住。所以交给 Elasticsearch (ES) 这种基于倒排索引的搜索引擎来处理。

数据怎么同步?(RabbitMQ 的角色)

MySQL 负责写,ES 负责读,那两边数据怎么保持一致? 这就是 RabbitMQ(消息队列) 的用武之地:

  1. 写操作完成:业务代码在 MySQL 中更新完数据后,立刻向 RabbitMQ 发送一条消息("房源 ZB0288 状态已变更")。

  2. 异步消费:系统中的同步服务(Consumer)监听到这条消息,从 MySQL 查出最新数据。

  3. 更新 ES:同步服务将拼装好的宽表大 JSON 写入 ES 索引。

优势:解耦了主业务流程,即便 ES 暂时挂了或同步很慢,用户的"退租"操作依然能在毫秒级完成,不会被卡住。


二、Elasticsearch (ES) 基础快速回顾

ES 和你熟悉的 Solr 底层都是 Lucene。你可以把 ES 当作一个"只支持 JSON 格式的、分布式的、超级快的非关系型数据库"。

1. 概念映射(MySQL vs ES vs Solr)

MySQL Elasticsearch Solr 说明
Database / Table Index (索引) Collection / Core 存储数据的逻辑集合
Row Document (文档) Document 一条具体的数据记录(JSON 格式)
Column Field (字段) Field 文档中的一个属性(如 house_code
Schema Mapping (映射) Schema.xml 定义字段的类型(text, keyword, integer 等)

注意:ES 7.x 之后已经废弃了 Type(表)的概念,一个 Index 就相当于一张宽表。

2. 基础语法(DSL - 领域特定语言)

ES 的交互全部基于 HTTP 和 JSON。

① 查询所有文档 (Match All) 相当于 SELECT * FROM table

复制代码
GET /qft_housing_index/_search
{
  "query": {
    "match_all": {}
  }
}

② 精确匹配 (Term) 相当于 WHERE store_id = 1001。用于数字、布尔值或不分词的字符串(keyword):

复制代码
GET /qft_housing_index/_search
{
  "query": {
    "term": {
      "store_id": 1001
    }
  }
}

③ 组合查询 (Bool) 这是最常用的,相当于 WHERE (a = 1 AND b = 2) OR (c = 3)

  • must:必须匹配(AND),计算相关性得分。

  • filter:必须匹配(AND),不计算得分,有缓存,性能极高

  • should:可选匹配(OR)。

  • must_not:必须不匹配(NOT)。

示例:查询店面为 1001 且状态为已租完的房源:

复制代码
GET /qft_housing_index/_search
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "store_id": 1001 } },
        { "term": { "rest_room_count": 0 } }
      ]
    }
  }
}

三、RabbitMQ 基础快速回顾

RabbitMQ 是一个消息代理(Message Broker),就像一个高级邮局。

1. 核心概念

  • Producer(生产者):发消息的程序(如"退租"接口)。

  • Consumer(消费者):收消息并处理的程序(如"同步 ES"的服务)。

  • Exchange(交换机):邮局的分类中心。生产者不直接把消息发给队列,而是发给交换机,交换机根据规则决定把消息投递到哪个队列。

  • Queue(队列):实际存放消息的信箱,消费者从这里取信。

  • Routing Key(路由键) :发消息时带的标签(如 house.update),交换机靠它来决定分发路径。

2. 常见的工作模式

最常用于数据同步的是 Topic(主题模式/路由模式)

  • 交换机类型为 topic

  • 队列绑定交换机时,指定自己关心的路由键规则(比如 house.* 表示关心所有房源变动,bill.# 表示关心所有账单及子账单变动)。

3. Java (Spring Boot) 基础语法示例

① 生产者发消息: 通常在增删改事务提交后调用:

复制代码
@Autowired
private RabbitTemplate rabbitTemplate;

public void updateHouseStatus(Long housingId) {
    // 1. 执行 MySQL 更新逻辑
    // ...
    
    // 2. 发送 MQ 消息通知 ES 同步
    // 参数:交换机名称, 路由键, 消息内容
    rabbitTemplate.convertAndSend("sync_exchange", "house.update", housingId);
}

② 消费者收消息: 监听指定的队列,拿到数据后处理(如查 MySQL 更新 ES):

复制代码
@Component
public class SyncEsConsumer {

    @RabbitListener(queues = "sync_house_queue")
    public void processHouseUpdate(Long housingId) {
        System.out.println("收到房源更新消息,准备同步 ES,房源ID:" + housingId);
        
        // 1. 根据 housingId 从 MySQL 查出宽表数据
        // 2. 将组装好的 JSON 调用 ES API 写入索引
    }
}

总结

  • 遇到写操作(增删改)代码时 :重点看 MyBatis/MySQL 的处理,最后找找有没有 rabbitTemplate.convertAndSend

  • 遇到消费者(@RabbitListener)代码时:这通常是在做"擦屁股"的脏活累活,比如查数据库组装宽表推给 ES。

  • 遇到读操作(列表查询)代码时 :如果看到拼装 BoolQueryBuilder 的代码,那就是在查 ES。用 Solr 的思维去理解那些过滤条件即可。

相关推荐
ting94520002 小时前
GRPO 算法全解析:从原理到实战
人工智能·架构
志栋智能4 小时前
运维超自动化:构建弹性IT架构的关键支撑
运维·服务器·网络·人工智能·架构·自动化
ai产品老杨4 小时前
GB28181与RTSP全协议兼容之道:基于Docker与微服务架构的AI视频中台架构解析(附源码交付方案)
docker·微服务·架构
池央4 小时前
基于腾讯云架构部署OpenClaw并实现与Telegram终端集成的全链路技术解析与实践指南
架构·云计算·腾讯云·腾讯云openclaw玩虾大赛
薛定猫AI4 小时前
【深度解析】Open Design:用本地优先架构重塑 AI UI 生成工作流
人工智能·ui·架构
candyTong10 小时前
一觉醒来,大模型就帮我排查完页面性能问题
前端·javascript·架构
空中海13 小时前
Kubernetes 入门基础与核心架构
贪心算法·架构·kubernetes
米高梅狮子14 小时前
08.CronJob和Service
云原生·容器·架构·kubernetes·自动化
SamDeepThinking16 小时前
中小团队需要一个资源微服务
后端·微服务·架构