Day20:RabbitMQ 1

目标

1
2
3
4
5
1. 为什么需要 MQ(★★★★★)
2. RabbitMQ 整体架构(★★★★★)
3. Producer、Exchange、Queue、Consumer(★★★★★)
4. 四种 Exchange 类型(★★★★★)
5. RabbitMQ 消息发送流程(★★★★★)

为什么需要 MQ?

MQ 主要解决异步、解耦、削峰三个问题。

RabbitMQ 架构

RabbitMQ 核心四个角色:

text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Producer
生产消息

|
v

Exchange
交换机, 负责路由

|
v

Queue
队列

|
v

Consumer
消费消息

Exchange 是什么?

根据规则,把消息发送到不同 Queue。

四种 Exchange 类型

  1. Direct Exchange(直连)
    根据routing key精确匹配。
  2. Fanout Exchange(广播)
    不看routing key所有绑定Queue都收到。
  3. Topic Exchange(主题)
    支持通配符。根据routing key模糊匹配。
  4. Headers Exchange(头信息)
    根据消息Header匹配。实际比较少用。

1. 为什么需要 MQ?说三个作用

  1. 解耦
  2. 削峰
  3. 异步

2. 为什么 Producer 不直接发送 Queue,而必须经过 Exchange?

因为消息不知道要发给谁。消息可能发给一个queue,也可能发给多个queue。Exchange 负责消息路由。

3. RabbitMQ 四种 Exchange 类型分别是什么?

direct、fanout、topic、headers

4. 你的“总对总”项目为什么适合使用 MQ?

为了利用死信队列实现的延迟队列。重复发报文。实现最大努力一致性。

5. 如果 Consumer 处理很慢,Queue 里面堆积大量消息怎么办?

  1. 增加消费者。
  2. 提高消费者处理能力
  3. 增加 Queue 数量
  4. 限制生产速度

Day21:RabbitMQ 2

目标

text
1
2
3
4
5
1. RabbitMQ 如何保证消息可靠性(★★★★★)
2. 消息为什么会丢失?怎么解决?(★★★★★)
3. 消息重复消费怎么办?(★★★★★)
4. 如何实现幂等?(★★★★★)
5. 死信队列与延迟消息(★★★★★)

RabbitMQ 如何保证消息可靠性?

从三个方面保证:

  1. 生产者开启 Confirm 机制,保证消息成功发送到 RabbitMQ;
  2. Exchange、Queue、Message 开启持久化,保证 RabbitMQ 重启消息不丢失;
  3. 消费者采用手动 ACK,业务处理成功后再确认消息。

1. Producer 端可靠性

Confirm 机制

生产者发送消息后 RabbitMQ 返回确认。

rabbitTemplate.setConfirmCallback(…)

Return 机制

消息到了 Exchange 但是没有 Queue 匹配。消息会被丢弃。

开启

1
mandatory=true

2. RabbitMQ 端可靠性

消息持久化
1
durable=true
Message 持久化
1
deliveryMode=PERSISTENT

3. Consumer 端可靠性

手动 ACK

消息重复消费怎么办

方法1:唯一业务ID

方法2:消费记录表
记录message_id

方法3:Redis 去重

利用

1
SETNX message:1001

执行失败说明已经执行。

1. RabbitMQ 消息为什么会丢失?三个阶段分别是什么?

消息抵达broke、抵达queue、抵达消费者都有可能丢失消息。

发送消息时记录日志。当消息抵达broke时,有一个回调函数,更新日志。当消息被正确投递到queue时,有一个回调函数,更新日志。开启ack,当消息被正确消费手动ack,更新日志。

2. 为什么消费者必须手动 ACK?

如果消费失败自动ack后,消息会丢失。

3. 为什么 MQ 会导致重复消费?

如果开启手动ack,当nack、处理业务逻辑还没有ack时,消费者宕机,消息会重新发送。可以通过幂等性、记录message_id的消费记录、redis的setnx等手段解决。

4. 什么是幂等?

同一个请求执行一次和执行多次,对系统最终状态产生相同影响。

5. 你的总对总报文发送失败,需要 5 分钟后重新发送,你怎么设计?

我会设计一个延迟重试队列,设置消息 TTL。消息过期后进入死信交换机,根据 routingKey 路由到业务队列,消费者重新消费,实现延迟重试。

Day22:Elasticsearch 1

目标

text
1
2
3
4
5
6
1. 为什么需要 Elasticsearch(★★★★★)
2. ES 为什么查询快(★★★★★)
3. 倒排索引原理(★★★★★)
4. ES 基本概念(★★★★★)
5. text 和 keyword 区别(★★★★★)
6. Mapping 设计(★★★★★)

为什么需要 Elasticsearch?

Elasticsearch 是一个基于倒排索引的分布式全文搜索引擎,适合海量数据的快速检索、全文搜索和聚合分析。

ES 为什么快?

  1. 倒排索引
  2. 分片
    一个索引可以分成多个 shard。并行查询。
  3. 缓存
    例如 filter 查询可以利用缓存。

倒排索引

正排其实是通过id找内容。倒排反过来,通过关键词找id再找到数据。

倒排索引包含两个部分:

  • Term Dictionary 词典:保存有哪些关键词。
  • Posting List 倒排列表:关键词对应哪些文档。

ES 通过倒排索引,将文档内容拆分成 Term,并维护 Term 到文档 ID 的映射关系,因此查询关键词时可以直接定位相关文档,而不需要扫描所有数据。

ES 基本概念

Index: 数据库
Document: 一行数据
Field: 字段
Mapping: 数据库表结构

text 和 keyword 区别

text用于全文搜索。会分词。
keyword不分词完整保存。适合精确查询。

类型 用途
text 搜索
keyword 过滤、排序、聚合

1. 为什么数据库 LIKE 查询不适合全文搜索,而 ES 可以?

数据库是查询时扫描匹配,ES 是提前建立索引,查询时定位。

2. 什么是倒排索引?为什么倒排索引快?

es会把句子进行拆分维护一个字典表,然后建立关键词到文档id的关联关系。可以通过关键词直接定位到数据。

3. ES 为什么需要分片?

ES 分片主要是为了水平扩展,将一个大的 Index 拆分到多个节点,提高存储容量和查询写入能力。

4. text 和 keyword 区别?

text会进行分词,适合搜索。keyword是关键词,不分词,适合过滤、排序、聚合。

5. 为什么 ES 不直接替代 MySQL?

数据库更擅长事务、关联查询、强一致性。
es擅长全文搜索、海量检索、聚合分析。

Day23:Elasticsearch 2

目标

text
1
2
3
4
5
1. ES 聚合查询(★★★★★)
2. ES 深分页问题(★★★★★)
3. ES 查询优化(★★★★★)
4. ES 写入优化(★★★★☆)
5. MySQL/PostgreSQL 与 ES 数据同步方案(★★★★★)

常见聚合类型

  1. Terms Aggregation(最常用)
    类似GROUP BY,例如:
    按照省统计:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    "aggs":{
    "province_count":{
    "terms":{
    "field":"province"
    }
    }
    }
    }
  2. Metric Aggregation
    统计最大值、最小值、平均值。例如:
    土地面积平均值:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    "aggs":{
    "avg_area":{
    "avg":{
    "field":"area"
    }
    }
    }
    }
  3. Date Histogram
    时间统计。

ES 深分页问题

分页查询第100万条:

1
2
3
4
{
"from":1000000,
"size":10
}

ES 不是直接跳过去。每个 shard 需要先查询1000010条数据,然后排序。最后丢弃前1000000条。

解决方案

  1. search_after(★★★★★)
    基于游标

    1
    2
    3
    4
    5
    6
    {
    "search_after":[
    "2026-01-01",
    100
    ]
    }
  2. scroll
    类似数据库游标适合大量导出。例如导出1000万数据。不过 scroll 不适合实时搜索。

  3. ES 查询优化

    1. 避免返回无用字段
    2. filter 替代 query
    3. 合理设计 Mapping
      不要所有字段text。

ES 写入优化

  1. Bulk 批量写入
  2. 调整 refresh_interval
    ES 默认1秒刷新一次生成segment。大量写入时可以调大。
  3. 合理设置分片数量
    分片太少无法并行。太多管理成本高。

MySQL/PostgreSQL 和 ES 数据同步

方案1:定时同步
缺点实时性差。

方案2:MQ 异步同步(推荐)
流程:

text
1
2
3
4
5
6
7
8
9
PostgreSQL 数据变化

发送消息

RabbitMQ

ES消费者

更新索引

方案3:CDC
例如监听数据库 WAL 日志,PostgreSQL有WAL工具Debezium。

流程:

text
1
2
3
4
5
6
7
PostgreSQL WAL

Debezium

Kafka

ES

1. search_after 为什么比 from + size 更适合深分页?

根据上一页最后一条数据的位置继续查询