其他中间件
Day20:RabbitMQ 1
目标
1 | 1. 为什么需要 MQ(★★★★★) |
为什么需要 MQ?
MQ 主要解决异步、解耦、削峰三个问题。
RabbitMQ 架构
RabbitMQ 核心四个角色:
1 | Producer |
Exchange 是什么?
根据规则,把消息发送到不同 Queue。
四种 Exchange 类型
- Direct Exchange(直连)
根据routing key精确匹配。 - Fanout Exchange(广播)
不看routing key所有绑定Queue都收到。 - Topic Exchange(主题)
支持通配符。根据routing key模糊匹配。 - Headers Exchange(头信息)
根据消息Header匹配。实际比较少用。
1. 为什么需要 MQ?说三个作用
- 解耦
- 削峰
- 异步
2. 为什么 Producer 不直接发送 Queue,而必须经过 Exchange?
因为消息不知道要发给谁。消息可能发给一个queue,也可能发给多个queue。Exchange 负责消息路由。
3. RabbitMQ 四种 Exchange 类型分别是什么?
direct、fanout、topic、headers
4. 你的“总对总”项目为什么适合使用 MQ?
为了利用死信队列实现的延迟队列。重复发报文。实现最大努力一致性。
5. 如果 Consumer 处理很慢,Queue 里面堆积大量消息怎么办?
- 增加消费者。
- 提高消费者处理能力
- 增加 Queue 数量
- 限制生产速度
Day21:RabbitMQ 2
目标
1 | 1. RabbitMQ 如何保证消息可靠性(★★★★★) |
RabbitMQ 如何保证消息可靠性?
从三个方面保证:
- 生产者开启 Confirm 机制,保证消息成功发送到 RabbitMQ;
- Exchange、Queue、Message 开启持久化,保证 RabbitMQ 重启消息不丢失;
- 消费者采用手动 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
目标
1 | 1. 为什么需要 Elasticsearch(★★★★★) |
为什么需要 Elasticsearch?
Elasticsearch 是一个基于倒排索引的分布式全文搜索引擎,适合海量数据的快速检索、全文搜索和聚合分析。
ES 为什么快?
- 倒排索引
- 分片
一个索引可以分成多个 shard。并行查询。 - 缓存
例如 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
目标
1 | 1. ES 聚合查询(★★★★★) |
常见聚合类型
Terms Aggregation(最常用)
类似GROUP BY,例如:
按照省统计:1
2
3
4
5
6
7
8
9{
"aggs":{
"province_count":{
"terms":{
"field":"province"
}
}
}
}Metric Aggregation
统计最大值、最小值、平均值。例如:
土地面积平均值:1
2
3
4
5
6
7
8
9{
"aggs":{
"avg_area":{
"avg":{
"field":"area"
}
}
}
}Date Histogram
时间统计。
ES 深分页问题
分页查询第100万条:
1 | { |
ES 不是直接跳过去。每个 shard 需要先查询1000010条数据,然后排序。最后丢弃前1000000条。
解决方案
search_after(★★★★★)
基于游标1
2
3
4
5
6{
"search_after":[
"2026-01-01",
100
]
}scroll
类似数据库游标适合大量导出。例如导出1000万数据。不过 scroll 不适合实时搜索。ES 查询优化
- 避免返回无用字段
- filter 替代 query
- 合理设计 Mapping
不要所有字段text。
ES 写入优化
- Bulk 批量写入
- 调整 refresh_interval
ES 默认1秒刷新一次生成segment。大量写入时可以调大。 - 合理设置分片数量
分片太少无法并行。太多管理成本高。
MySQL/PostgreSQL 和 ES 数据同步
方案1:定时同步
缺点实时性差。
方案2:MQ 异步同步(推荐)
流程:
1 | PostgreSQL 数据变化 |
方案3:CDC
例如监听数据库 WAL 日志,PostgreSQL有WAL工具Debezium。
流程:
1 | PostgreSQL WAL |
1. search_after 为什么比 from + size 更适合深分页?
根据上一页最后一条数据的位置继续查询










