数据库与周边基础设施完全指南:关系型、NoSQL、向量库、Redis、Elasticsearch 与 RocketMQ
做系统做到一定阶段,团队几乎都会遇到同一类问题:
- 核心业务数据到底该放 MySQL 还是 PostgreSQL?
- MongoDB 到底适合做什么,不适合做什么?
- Redis 真的只是缓存吗?
- Elasticsearch 到底是数据库、搜索引擎,还是另一个“大号索引”?
- 向量数据库是不是 AI 时代的新主库?
- RocketMQ 是不是“能存消息,所以也算数据库”?
这些问题之所以反复出现,不是因为概念本身复杂,而是因为很多文章只讲“定义”,不讲边界,不讲取舍,也不讲系统组合方式。
这篇文章不打算列百科词条,而是按真实工程视角把这几个基础设施讲清楚:
- 它们各自解决什么问题
- 它们的数据模型是什么
- 它们的优势和短板在哪里
- 它们在一个系统里应该如何分工
- 哪些选型是合理演进,哪些是典型过度设计
如果你是后端工程师、架构师,或者正在搭系统,这篇文章应该能直接帮你建立一套足够稳的判断框架。
先立边界:数据库、缓存、搜索引擎、消息队列不是一回事
这是最值得先讲清的一段。很多架构混乱,不是因为技术不会用,而是因为边界一开始就没划清。
四者的职责边界
| 类别 | 核心职责 | 典型产品 | 最擅长 | 不该承担的角色 |
|---|---|---|---|---|
| 数据库 | 持久化业务真相源 | MySQL、PostgreSQL、MongoDB | 存储、查询、约束、事务 | 异步事件总线、全文搜索引擎 |
| 缓存 | 加速热点访问、保存短期状态 | Redis | 低延迟读写、计数、状态、过期 | 业务长期真相源 |
| 搜索引擎 | 高效做全文检索和复杂搜索 | Elasticsearch | 倒排索引、全文检索、复杂搜索条件 | 事务主库 |
| 消息队列 | 传递事件、解耦系统、削峰异步 | RocketMQ | 异步处理、重试、广播、顺序消费 | 业务查询存储 |
一句话概括:
- 数据库负责“数据最终是什么”。
- 缓存负责“数据怎样更快拿到”。
- 搜索引擎负责“数据怎样更容易被搜到”。
- 消息队列负责“事情发生后谁来异步处理”。
这四类基础设施可以互相配合,但不能互相替代。
一个典型误用例子
以“支付成功后发券”为例:
- 订单状态应该写数据库
- 订单热点信息可以放 Redis
- 用户搜索订单内容可以走 Elasticsearch
- “支付成功”这个事件可以发到 RocketMQ
如果把这些职责搅在一起,就会出现:
- 用 Redis 保存订单最终状态,结果状态不可追溯
- 用 MQ 当业务日志查询源,排查困难
- 用 Elasticsearch 当交易主库,结果一致性出问题
- 用数据库轮询代替事件流,最终主链路越来越慢
边界先清楚,架构才不会越做越乱。
一张总览表先建立认知
| 技术类别 | 核心问题 | 数据模型 | 擅长什么 | 不擅长什么 |
|---|---|---|---|---|
| 关系型数据库 | 核心业务数据正确存储 | 表、行、列、关系 | 事务、约束、JOIN、报表 | 语义检索、高吞吐事件分发 |
| 文档数据库 | 半结构化对象存储 | JSON/BSON 文档 | 灵活模型、整体读写 | 复杂关系与强约束 |
| Key-Value 数据库 | 极快映射访问 | key -> value |
缓存、状态、计数 | 多条件复杂查询 |
| 列族数据库 | 海量宽表与分布式写入 | RowKey + 列族 | 大规模写入、时序、埋点 | 复杂 OLTP |
| 图数据库 | 关系路径分析 | 点、边、属性 | 图谱、路径、关联分析 | 通用业务主库 |
| 向量数据库 | 相似度检索 | 向量 + 元数据 | RAG、语义搜索、召回 | 强事务与精确查询 |
| Redis | 高性能内存数据结构服务 | String/Hash/List/Set/ZSet 等 | 缓存、会话、限流、实时状态 | 主交易存储 |
| Elasticsearch | 全文检索与复杂搜索 | 文档 + 倒排索引 | 关键词搜索、筛选聚合 | 强事务主库 |
| RocketMQ | 异步事件传递 | Topic / Message / Group | 解耦、削峰、重试、顺序 | 替代数据库 |
关系型数据库是什么
关系型数据库,英文是 Relational Database,典型代表包括:
- MySQL
- PostgreSQL
- Oracle
- SQL Server
它的核心不只是“数据放在表里”,而是:
- 数据结构清晰
- 关系可以被明确表达
- 约束可以由系统保证
- 多个操作可以纳入同一事务
- SQL 可以稳定地支撑查询、统计、审计和治理
它解决的根问题是什么
在业务系统里,有些数据不是“尽量正确”,而是“必须正确”:
- 一个订单只能属于一个用户
- 一笔支付不能重复扣款
- 一个手机号不能注册多个账号
- 库存不能扣成负数
- 财务流水必须可追溯
这类问题的共同特征是:
- 有结构
- 有规则
- 有约束
- 有事务边界
- 错了代价很高
关系型数据库就是为这类问题设计的。
关系型数据库的数据模型
关系型数据库最常见的要素包括:
- 表(Table)
- 行(Row)
- 列(Column)
- 主键(Primary Key)
- 外键(Foreign Key)
- 索引(Index)
- 约束(Constraint)
- 事务(Transaction)
下面用一个非常典型的电商结构来说明。
CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(64) NOT NULL UNIQUE,
email VARCHAR(128) NOT NULL UNIQUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
order_no VARCHAR(64) NOT NULL UNIQUE,
total_amount DECIMAL(10, 2) NOT NULL,
status VARCHAR(32) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE order_items (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_id BIGINT NOT NULL,
product_id BIGINT NOT NULL,
quantity INT NOT NULL,
unit_price DECIMAL(10, 2) NOT NULL,
CONSTRAINT fk_order_items_order FOREIGN KEY (order_id) REFERENCES orders(id)
);
这个建模的价值在于:
order_no可以唯一约束- 订单必须归属某个用户
- 子项必须归属某个订单
- 金额、数量、状态都有明确数据类型
- 查询和审计边界很清楚
这类能力是很多业务系统最需要的底座。
SQL 的价值,不只是“会查数据”
SQL 最重要的价值在于它是声明式的,而且经过几十年工程验证,已经非常成熟。
典型查询
查询某个用户最近 30 天订单:
SELECT
o.order_no,
o.total_amount,
o.status,
o.created_at
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE u.username = 'alice'
AND o.created_at >= NOW() - INTERVAL 30 DAY
ORDER BY o.created_at DESC;
聚合统计订单状态分布:
SELECT
status,
COUNT(*) AS order_count,
SUM(total_amount) AS total_amount
FROM orders
GROUP BY status
ORDER BY order_count DESC;
事务为什么关键
最经典的例子是库存扣减和订单创建要一起成功或一起失败:
START TRANSACTION;
UPDATE products
SET stock = stock - 1
WHERE id = 1001 AND stock > 0;
INSERT INTO orders (user_id, order_no, total_amount, status)
VALUES (1, 'ORD202606270001', 99.00, 'CREATED');
COMMIT;
如果没有事务,你会很快遇到这些事故:
- 库存扣了,但订单没建成功
- 订单建了,但库存没扣成功
- 并发请求把库存扣成负数
- 接口重试导致重复写入
很多人讨论“新架构”时容易忽略这一点:真正贵的不是技术栈,而是业务错误。
PostgreSQL 为什么常被更偏工程能力的团队偏爱
如果说 MySQL 是“非常普及、非常稳妥”的业务主库选择,那么 PostgreSQL 常常被很多偏平台型、数据型或架构型团队偏爱,原因也很直接:
- SQL 标准支持更完整
- 数据类型更丰富
- JSONB、数组、地理空间、全文检索、窗口函数等能力更强
- 扩展生态非常丰富,例如
pgvector、PostGIS - 在一个系统内能承载更复杂的查询和扩展需求
这不是说 PostgreSQL 一定“比 MySQL 高级”,而是它在“功能上限”这件事上往往更宽。
一个 PostgreSQL 例子:JSONB 与结构化查询结合
PostgreSQL 很适合处理“核心字段结构化,但部分扩展字段灵活”的场景。
CREATE TABLE events (
id BIGSERIAL PRIMARY KEY,
event_type TEXT NOT NULL,
payload JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_events_payload_gin ON events USING GIN (payload);
INSERT INTO events (event_type, payload)
VALUES (
'order_paid',
'{
"orderNo": "ORD202606270001",
"userId": 1001,
"channel": "wechat",
"amount": 199.00
}'::jsonb
);
SELECT
id,
payload->>'orderNo' AS order_no,
payload->>'channel' AS channel
FROM events
WHERE event_type = 'order_paid'
AND payload->>'channel' = 'wechat';
这个模式在实际工程里很常见:
- 核心关系数据仍然放标准表
- 扩展字段放 JSONB
- 查询又不至于像“无结构文档”那样失控
PostgreSQL 与向量检索的一个现实优势
如果你的向量场景规模不大、系统复杂度要控制,可以直接基于 PostgreSQL 的 pgvector 做第一版,而不是一上来部署独立向量数据库。
这是一条非常务实的路径:
- 小规模数据先用 PostgreSQL
- 跑通检索和效果评估
- 数据量、QPS、过滤复杂度上来之后,再考虑拆到专用向量库
这比“还没形成稳定需求就先引入三种新中间件”稳得多。
关系型数据库的优势与边界
优势
| 优势 | 说明 |
|---|---|
| 强事务 | 特别适合订单、支付、库存、账务 |
| 强约束 | 主键、唯一键、外键、检查约束非常重要 |
| SQL 成熟 | 查询、聚合、报表、审计能力强 |
| 治理能力强 | 模型清晰,适合团队协作和长期演进 |
| 工具链成熟 | 监控、备份、迁移、审计都很完善 |
局限
| 局限 | 说明 |
|---|---|
| 模式变更要谨慎 | 大表 schema 变更通常要规划 |
| 横向扩展复杂 | 分库分表之后复杂度会明显上升 |
| 不适合灵活文档为主的场景 | 字段变化极快时维护成本变高 |
| 不擅长语义检索 | 向量相似度不是它的核心能力 |
最适合的典型场景
- 用户、权限、组织
- 订单、支付、退款
- 商品、库存、价格
- 财务、账本、对账
- 核心主数据管理
常见误解
误解一:关系型数据库已经过时
不是。绝大多数真正重要的业务系统,核心真相源仍然是关系型数据库。
误解二:有 JSON 字段就等于有了 NoSQL
不是。JSON 字段是很实用的补充能力,但并不等于“关系型数据库已经变成文档数据库”。
非关系型数据库是什么
NoSQL 不是某一个产品,而是一大类数据库的统称。更准确地理解,它表示:
- 不只依赖关系模型
- 不把 SQL 和表关系视为唯一方式
- 针对特定问题采用更适合的数据模型
NoSQL 的主流类型
| 类型 | 代表产品 | 数据模型 | 典型用途 |
|---|---|---|---|
| 文档数据库 | MongoDB、Couchbase | JSON/BSON 文档 | 内容、画像、配置、半结构化数据 |
| Key-Value 数据库 | Redis、DynamoDB | 键值映射 | 缓存、会话、计数 |
| 列族数据库 | HBase、Cassandra | 宽表 / 列族 | 时序、埋点、海量写入 |
| 图数据库 | Neo4j、JanusGraph | 节点、边、属性 | 图谱、推荐、风控关联 |
NoSQL 解决的不是“关系型数据库不行”,而是“有些问题不是关系模型的强项”。
文档数据库:以 MongoDB 为例
MongoDB 是最典型的文档数据库。它适合的数据,天然更像一个完整对象,而不是一个要拆成多张表的严格关系模型。
它解决什么问题
以下场景通常很适合文档数据库:
- 用户画像字段经常变
- 商品属性很多且不固定
- 内容系统的结构高度灵活
- 表单定义和配置项迭代快
- 你更关心“整体读出一个对象”,而不是复杂 JOIN
文档是什么样子
{
"_id": "ord_202606270001",
"userId": 1001,
"status": "PAID",
"totalAmount": 199.00,
"items": [
{
"productId": 2001,
"name": "机械键盘",
"quantity": 1,
"price": 199.00
}
],
"shippingAddress": {
"province": "浙江",
"city": "杭州",
"detail": "xx 路 xx 号"
},
"tags": ["首单", "活动订单"],
"createdAt": "2026-06-27T10:00:00Z"
}
如果同样建到关系型数据库里,往往会拆成多张表。文档数据库的优点是对象整体自然、读写路径短。
MongoDB 示例
插入文档:
db.orders.insertOne({
orderNo: "ORD202606270001",
userId: 1001,
status: "PAID",
totalAmount: 199.0,
items: [
{ productId: 2001, name: "机械键盘", quantity: 1, price: 199.0 }
],
createdAt: new Date()
});
条件查询:
db.orders.find(
{
userId: 1001,
status: "PAID",
totalAmount: { $gte: 100 }
},
{
_id: 0,
orderNo: 1,
totalAmount: 1,
status: 1
}
);
数组字段查询:
db.orders.find({
"items.productId": 2001
});
创建索引:
db.orders.createIndex({ userId: 1, createdAt: -1 });
MongoDB 的优势
| 优势 | 说明 |
|---|---|
| 模型灵活 | 字段调整快,适合业务迭代 |
| 与应用对象接近 | JSON 对前后端都友好 |
| 整体读写自然 | 聚合对象存储方便 |
| 扩展能力较好 | 分片方案成熟 |
MongoDB 的边界
| 边界 | 说明 |
|---|---|
| 复杂关系不是强项 | 跨集合强关联建模不如关系型自然 |
| 强事务核心系统要谨慎 | 并不是它最舒服的工作负载 |
| 结构治理容易失控 | “灵活”如果缺少规范,很快变成“混乱” |
最适合的场景
- 内容系统
- 用户画像
- 配置中心
- 表单与模板定义
- 半结构化业务快照
一个常见误区
MongoDB 很适合某些业务,但它不是“因为写 JSON 更舒服,所以可以替代所有关系型数据库”。
Key-Value 数据库:简单模型背后的高价值
Key-Value 数据模型看起来极其简单:
key -> value
但很多高性能场景正是因为足够简单,才足够高效。
它适合解决什么问题
- 已知 key,直接取值
- 不需要复杂条件查询
- 需要低延迟
- 需要高吞吐
- 数据生命周期较短或结构简单
典型场景
- 登录会话
- 验证码
- token
- 限流计数
- 去重标记
- 缓存
代表产品包括:
- Redis
- DynamoDB
- Aerospike
其中最常见、最容易被用到的就是 Redis,但 Redis 又不只是一个普通 KV 库,这一点后面会单独展开。
列族数据库:为海量宽表而生
列族数据库平时在普通业务系统里没有 MySQL、Redis 那么常见,但在大规模数据场景里很重要。
代表产品:
- HBase
- Cassandra
- ScyllaDB
它解决什么问题
- 海量写入
- 宽表存储
- 按 RowKey 访问
- 分布式扩展
- 时序、埋点、设备数据
它的特点
- 不擅长复杂关系
- 不以事务和 JOIN 为核心能力
- 更关注海量数据下的分布式读写能力
典型场景
- 用户行为埋点
- 设备遥测
- 海量日志底层存储
- 超大规模时序数据
如果你的系统本质上还是标准 OLTP 业务,不要因为“听说大厂在用”就盲目引入列族数据库。
图数据库:当关系本身成为查询对象
图数据库不是为了“数据之间有关系”,而是为了“关系链路本身就是核心分析对象”。
代表产品:
- Neo4j
- JanusGraph
- TigerGraph
典型问题
- 两个账号之间通过几跳关联
- 欺诈团伙是否共享设备、手机号、IP
- 知识图谱中的概念关系
- 推荐路径传播
数据模型
- 节点(Node)
- 边(Edge)
- 属性(Property)
最适合的场景
- 风控图谱
- 社交网络
- 推荐系统中的关系传播
- 知识图谱
不适合的场景
- 订单、支付、库存主库
- 常规事务型业务系统
很多系统“有关系”,但只有少数系统真的“以关系遍历为核心”。别把这个边界搞混。
向量数据库是什么
向量数据库是这几年随着 AI、Embedding、RAG 普及而迅速升温的一类基础设施。
典型代表包括:
- Milvus
- Qdrant
- Weaviate
- Pinecone
- pgvector
- Elasticsearch / OpenSearch 的向量检索能力
它到底解决什么问题
传统数据库擅长:
- 精确匹配
- 范围过滤
- 排序聚合
- 结构化条件查询
向量数据库擅长的是:
- 语义相似检索
- 相似内容召回
- 高维向量近邻搜索
举个例子,用户输入:
- “怎么防止订单重复提交”
文档里未必写“重复提交”,可能写的是:
- “幂等控制”
- “防重设计”
- “请求去重”
关键词检索不一定能很好命中,但向量检索可能把这些内容召回出来。
什么是向量
文本、图片、音频都可以被编码成高维向量,例如:
[0.021, -0.337, 0.114, ...]
语义越相近,通常向量距离越近。
向量数据库的数据模型
一般包括:
idvectorpayload / metadata
例如:
{
"id": "doc-1001",
"vector": [0.12, -0.08, 0.44, "..."],
"payload": {
"title": "订单幂等设计",
"category": "payment",
"lang": "zh",
"source": "internal-wiki"
}
}
向量数据库与全文检索的区别
| 维度 | 全文检索 | 向量检索 |
|---|---|---|
| 核心依据 | 倒排索引、词项匹配 | 向量距离、语义相似 |
| 擅长 | 精确关键词、短语、过滤聚合 | 语义召回、相似案例查找 |
| 优势 | 可解释、过滤强、成熟稳定 | 同义表达、改写表达召回能力强 |
| 局限 | 对语义改写不敏感 | 结果可解释性较弱,易出现“看起来相近但并不准确”的内容 |
实际系统里,最稳的方案通常不是二选一,而是:
- 全文检索负责关键词召回
- 向量检索负责语义召回
- 两路结果再重排
向量检索示例:Python 伪真实代码
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct, Filter, FieldCondition, MatchValue
from sentence_transformers import SentenceTransformer
client = QdrantClient(url="http://localhost:6333")
model = SentenceTransformer("BAAI/bge-small-zh-v1.5")
collection_name = "knowledge_docs"
client.recreate_collection(
collection_name=collection_name,
vectors_config=VectorParams(size=512, distance=Distance.COSINE)
)
docs = [
{"id": 1, "text": "幂等是指同一个请求重复执行多次,结果仍然一致。", "category": "payment"},
{"id": 2, "text": "分布式锁用于协调并发访问共享资源,但要谨慎处理超时和误释放。", "category": "infra"},
{"id": 3, "text": "消息队列可以实现异步解耦和流量削峰。", "category": "mq"}
]
vectors = model.encode([d["text"] for d in docs], normalize_embeddings=True)
client.upsert(
collection_name=collection_name,
points=[
PointStruct(
id=d["id"],
vector=vectors[i].tolist(),
payload={
"text": d["text"],
"category": d["category"]
}
)
for i, d in enumerate(docs)
]
)
query = "如何避免重复扣款"
query_vector = model.encode(query, normalize_embeddings=True).tolist()
result = client.search(
collection_name=collection_name,
query_vector=query_vector,
query_filter=Filter(
must=[
FieldCondition(
key="category",
match=MatchValue(value="payment")
)
]
),
limit=3
)
for item in result:
print(item.score, item.payload["text"])
向量数据库的优势
| 优势 | 说明 |
|---|---|
| 语义召回强 | 适合 RAG、语义搜索、案例检索 |
| 可以带元数据过滤 | 不只是“相似”,还能按分类、权限、来源筛选 |
| 适合高维向量索引 | 专门为 ANN 检索设计 |
向量数据库的边界
| 边界 | 说明 |
|---|---|
| 不适合事务主库 | 订单、支付、库存不该放这里 |
| 检索结果不是“绝对正确” | 它返回的是“相似”,不是“真相” |
| 调优是系统工程 | chunk、embedding、topK、rerank 都影响效果 |
常见误解
误解一:向量数据库就是 AI 时代的新主数据库
不是。它主要是检索层或召回层。
误解二:做 RAG 必须独立部署向量库
不一定。很多团队第一阶段直接用 PostgreSQL + pgvector 就够了。
Redis 到底是什么
Redis 最常被简化成“缓存”,这会误导很多工程判断。
更准确地说,Redis 是:
- 高性能内存数据结构服务器
- 支持一定持久化能力
- 经常扮演缓存层、状态层、协调层、轻量流处理层
Redis 解决什么问题
Redis 适合这些问题:
- 访问延迟必须非常低
- 数据结构简单,但访问频繁
- 需要计数、限流、去重、排序
- 需要短期状态管理
- 需要基于过期时间控制数据生命周期
Redis 的数据结构,不只是字符串
| 数据结构 | 典型用途 |
|---|---|
| String | 缓存对象、计数器、锁 key |
| Hash | 会话数据、对象字段 |
| List | 简单队列、时间线 |
| Set | 去重、标签集合 |
| Sorted Set | 排行榜、延时任务 |
| Bitmap | 活跃统计、签到 |
| HyperLogLog | UV 近似去重 |
| Stream | 轻量消息流 |
| Geo | 地理位置 |
Redis 示例
缓存商品详情:
SET product:2001 '{"id":2001,"name":"机械键盘","price":199.0}' EX 300
GET product:2001
会话存储:
HSET session:token:abc123 userId 1001 role admin loginAt 1719450000
EXPIRE session:token:abc123 1800
HGETALL session:token:abc123
限流计数:
INCR api:rate_limit:user:1001
EXPIRE api:rate_limit:user:1001 60
排行榜:
ZADD sales_rank 1200 product:2001
ZADD sales_rank 800 product:2002
ZREVRANGE sales_rank 0 9 WITHSCORES
Stream 示例:
XADD order_events * orderNo ORD202606270001 status PAID
XGROUP CREATE order_events order_group 0 MKSTREAM
XREADGROUP GROUP order_group consumer_1 COUNT 10 STREAMS order_events >
Redis 持久化怎么理解
Redis 不是完全不落盘。常见机制有:
- RDB 快照
- AOF 追加日志
- 混合持久化
但不要因为它“能持久化”,就把它理解成主交易数据库。它的核心优势是内存速度和数据结构,不是复杂业务持久化治理。
Redis 分布式锁:能用,但别神化
最常见的加锁写法:
SET lock:order:1001 "request-uuid-123" NX PX 30000
这能解决一些简单并发协调问题,但要注意:
- 释放锁时不能无脑
DEL - 要校验 value,避免误删别人的锁
- 锁超时、网络抖动、GC 暂停都可能导致误判
- 很多问题本质上更适合用数据库唯一约束或业务幂等解决
经验上说一句直白的话:
如果一个系统到处都在上 Redis 分布式锁,往往说明业务状态设计本身出了问题。
Redis 的优势
| 优势 | 说明 |
|---|---|
| 性能极高 | 内存读写非常快 |
| 原子操作丰富 | 计数、集合、排序都方便 |
| 数据结构多样 | 能解决很多实时状态问题 |
| 生态成熟 | 各语言支持完善 |
Redis 的边界
| 边界 | 说明 |
|---|---|
| 内存成本高 | 不适合存一切 |
| 复杂查询能力弱 | 不是关系查询引擎 |
| 作为主存储风险高 | 一致性与治理语义不是主打 |
| 容易被滥用 | key 设计失控会很难维护 |
常见误解
误解一:Redis 就是缓存
不是。缓存只是它最常见的一个角色。
误解二:有了 Redis,数据库性能问题就解决了
不一定。很多项目只是把问题从慢 SQL 转成了缓存击穿、热 key、双写不一致和大 key。
Elasticsearch 是什么,为什么经常和数据库一起出现
Elasticsearch 经常被顺手提到,但如果不单独讲清楚,很多人会对它的定位产生混淆。
Elasticsearch 本质上是:
- 搜索引擎
- 基于倒排索引
- 擅长全文检索、复杂过滤、聚合分析
- 常被当成数据库的检索副本,而不是业务真相源
它解决什么问题
以下问题不是关系型数据库的强项,却是 Elasticsearch 的强项:
- 商品搜索
- 文章全文搜索
- 日志检索
- 错误码、接口名、关键字搜索
- 复杂筛选 + 排序 + 聚合统计
为什么数据库不直接做搜索
关系型数据库当然能做 LIKE,PostgreSQL 也有全文检索能力,但当你要处理这些需求时,搜索引擎通常更合适:
- 中文分词
- 倒排索引
- 多字段加权
- 高亮
- 拼写纠错
- 复杂布尔搜索
- 搜索相关性排序
Elasticsearch 示例:商品索引文档
{
"id": 2001,
"name": "机械键盘 K87",
"brand": "KeyTech",
"category": "keyboard",
"tags": ["机械键盘", "蓝牙", "热插拔"],
"description": "支持蓝牙与有线双模连接,适合办公与编程。",
"price": 399.00,
"stock": 120
}
搜索示例:
POST /products/_search
Content-Type: application/json
{
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": "蓝牙机械键盘",
"fields": ["name^3", "tags^2", "description"]
}
}
],
"filter": [
{ "term": { "category": "keyboard" } },
{ "range": { "price": { "lte": 500 } } }
]
}
},
"sort": [
{ "_score": "desc" },
{ "price": "asc" }
]
}
Elasticsearch 的优势
| 优势 | 说明 |
|---|---|
| 全文检索强 | 倒排索引成熟高效 |
| 搜索体验好 | 分词、相关性排序、高亮都成熟 |
| 聚合强 | 很适合日志与搜索分析场景 |
| 可扩展 | 支持大规模检索集群 |
Elasticsearch 的边界
| 边界 | 说明 |
|---|---|
| 不适合作为交易主库 | 一致性和事务语义不是核心设计目标 |
| 通常是副本数据 | 需要从主库同步索引 |
| 索引更新有成本 | 设计不好容易造成数据延迟和同步复杂度 |
一个很实用的定位原则
如果你在做:
- “把东西存下来” -> 优先想数据库
- “把东西搜出来” -> 优先想搜索引擎
- “把相似内容召回出来” -> 优先想向量检索
这三个动作看起来接近,底层目标完全不同。
RocketMQ 是什么
RocketMQ 不是数据库,它是 消息队列,或者更准确一点,是 面向分布式系统的消息中间件。
它要解决的问题不是“如何把数据存起来”,而是:
- 事件怎么异步传播
- 服务怎么解耦
- 高峰流量怎么削峰
- 失败怎么重试
- 多系统如何最终一致地协作
RocketMQ 解决什么问题
举一个非常常见的支付场景:
用户支付成功后,往往不止一件事要做:
- 更新订单状态
- 发优惠券
- 发短信或邮件
- 加积分
- 同步物流
- 记录审计日志
- 通知推荐系统
如果支付服务同步调用所有这些下游服务:
- 主链路会变长
- 一个下游抖动会拖慢整体
- 系统之间强耦合
- 高峰流量容易把下游打垮
MQ 的思路是:
- 支付服务先发一个“支付成功事件”
- 谁关心这个事件,谁自己订阅
- 主链路尽快返回
- 下游异步处理、失败重试
RocketMQ 的核心概念
- Producer:生产者
- Consumer:消费者
- Topic:主题
- Message:消息
- Consumer Group:消费组
- Retry:重试
- Dead Letter Queue:死信
- Ordered Message:顺序消息
- Transaction Message:事务消息
RocketMQ Producer 示例
@Service
public class OrderEventProducer {
private final RocketMQTemplate rocketMQTemplate;
public OrderEventProducer(RocketMQTemplate rocketMQTemplate) {
this.rocketMQTemplate = rocketMQTemplate;
}
public void sendOrderPaidEvent(String orderNo, Long userId, BigDecimal amount) {
Map<String, Object> payload = new HashMap<>();
payload.put("orderNo", orderNo);
payload.put("userId", userId);
payload.put("amount", amount);
payload.put("eventType", "ORDER_PAID");
rocketMQTemplate.convertAndSend("order-events", payload);
}
}
RocketMQ Consumer 示例
@Slf4j
@Service
@RocketMQMessageListener(
topic = "order-events",
consumerGroup = "coupon-service-group"
)
public class CouponConsumer implements RocketMQListener<Map<String, Object>> {
@Override
public void onMessage(Map<String, Object> message) {
String orderNo = (String) message.get("orderNo");
String eventType = (String) message.get("eventType");
if (!"ORDER_PAID".equals(eventType)) {
return;
}
log.info("receive paid event, orderNo={}", orderNo);
// 这里必须做幂等控制
// 例如以 orderNo + eventType 建唯一键,防止重复消费发券
}
}
顺序消息怎么理解
顺序消息通常不是全局顺序,而是 同一业务实体的局部顺序。
例如同一个订单的事件:
- 创建
- 支付
- 发货
- 完成
这类消息可以按 orderNo 作为 sharding key 路由到同一队列,从而保证同一订单事件按顺序被消费。
RocketMQ 的重点,不是“能发”,而是“消费正确”
真正难的从来不是生产一条消息,而是下面这些问题:
- 消费会不会重复
- 重试会不会造成副作用
- 幂等怎么保证
- 堆积了怎么处理
- 死信怎么回放
- 事务消息怎么和本地事务对齐
RocketMQ 的优势
| 优势 | 说明 |
|---|---|
| 解耦能力强 | 服务之间不用强同步依赖 |
| 能削峰 | 高峰流量不必瞬间压垮下游 |
| 支持可靠投递 | 重试、死信、顺序等能力成熟 |
| 适合事件驱动架构 | 方便系统演进 |
RocketMQ 的边界
| 边界 | 说明 |
|---|---|
| 不是数据库 | 不适合承载查询和主存储职责 |
| 会增加复杂度 | 监控、补偿、重试都要治理 |
| 最终一致性不是自动获得的 | 需要业务设计配合 |
常见误解
误解一:有 MQ 之后就不需要事务
不对。MQ 解决的是跨系统异步协作,不替代本地事务。
误解二:消息发出去了,业务就算完成了
不对。消息发出去只表示“事件进入通道”,并不表示所有消费者都已经正确完成业务动作。
如何在真实系统里分工:MySQL、PostgreSQL、MongoDB、Redis、Elasticsearch、向量数据库、RocketMQ
这是最有价值的一部分。不要只比较单个组件,要看系统组合。
一张对照表
| 技术 | 最适合的问题 | 不建议拿来做什么 |
|---|---|---|
| MySQL | 标准 OLTP 业务、订单、用户、支付 | 语义检索 |
| PostgreSQL | OLTP + 更复杂的数据能力与扩展能力 | 超高性能缓存层 |
| MongoDB | 半结构化文档、内容、画像、配置 | 强关系事务核心系统 |
| Redis | 缓存、会话、计数、排行、状态 | 业务长期真相源 |
| Elasticsearch | 全文搜索、日志搜索、复杂筛选 | 强事务主库 |
| 向量数据库 | 语义搜索、RAG、相似召回 | 订单支付主库 |
| RocketMQ | 异步解耦、削峰、事件驱动 | 查询数据库 |
MySQL 还是 PostgreSQL
工程上最靠谱的判断不是“谁更先进”,而是:
- 团队是否熟悉
- 业务模型是否标准
- 是否需要更丰富扩展能力
- 是否希望一个数据库承载更多高级能力
一个很务实的建议:
- 如果是典型业务系统,团队对 MySQL 很熟,MySQL 没问题
- 如果团队熟悉 PostgreSQL,且确实会用到 JSONB、全文检索、地理空间、向量扩展等能力,PostgreSQL 往往更有上限
MongoDB 什么时候合适
当数据天然更像“一个对象文档”,而不是“多表关系事务模型”时,MongoDB 很合适。
Redis 什么时候几乎必选
当系统开始出现以下需求时,Redis 往往会自然出现:
- 热点缓存
- 会话管理
- 原子计数
- 排行榜
- 限流
- 幂等标记
Elasticsearch 什么时候必须上
当用户明确是“搜索”行为时,例如:
- 商品检索
- 文档搜索
- 日志排查
- 关键字分析
这时 Elasticsearch 很有价值。别让数据库去硬扛搜索引擎的工作。
向量数据库什么时候值得上
当业务核心是:
- 语义搜索
- 企业知识库问答
- RAG
- 相似案例召回
- 图文相似匹配
这时向量数据库值得引入。
RocketMQ 什么时候值得上
当你出现这些信号时:
- 主链路同步调用越来越长
- 一个动作要通知很多下游系统
- 流量高峰压不住
- 多系统之间靠轮询同步状态
- 业务允许最终一致而非强同步
这时上 MQ 通常是合理的。
实战架构示例一:典型电商订单系统
技术分工
- MySQL / PostgreSQL:订单、支付、库存、用户主数据
- Redis:商品热点缓存、会话、幂等标记、限流
- Elasticsearch:商品搜索
- RocketMQ:支付成功事件、发券、通知、物流同步
- 向量数据库:智能客服知识库、商品语义召回
架构示意
flowchart LR
A[用户请求] --> B[API 服务]
B --> C[(MySQL / PostgreSQL)]
B --> D[(Redis)]
B --> E[(Elasticsearch)]
B --> F[(Vector DB)]
C --> G[RocketMQ]
G --> H[优惠券服务]
G --> I[通知服务]
G --> J[物流服务]
订单链路说明
- 用户提交订单。
- 订单服务校验幂等和库存可用性。
- 核心订单写入关系型数据库。
- 热点商品和部分会话状态走 Redis。
- 支付成功后更新订单状态。
- 事务提交后发送
ORDER_PAID事件到 RocketMQ。 - 发券、通知、积分、物流等服务异步消费。
- 商品检索走 Elasticsearch。
- 智能客服问答走向量检索与 RAG。
这个分工的关键在于:
- 真相放数据库
- 性能放 Redis
- 搜索放 Elasticsearch
- 语义召回放向量检索
- 事件传播放 RocketMQ
职责清晰之后,系统复杂度其实是下降的。
实战架构示例二:企业知识库与 RAG 系统
技术分工
- PostgreSQL:文档元数据、权限、版本、知识库配置
- 对象存储:原始文件
- Elasticsearch:关键词检索
- 向量数据库:语义召回
- Redis:会话缓存、热点问题缓存
- RocketMQ:异步切片、抽取、向量化、索引更新
数据流
- 用户上传 PDF、Word、Markdown。
- 元数据写入 PostgreSQL。
- 文件保存到对象存储。
- 系统发送异步处理消息到 RocketMQ。
- 文档处理服务执行:
- 文本抽取
- 切片
- 生成 embedding
- 写入向量数据库
- 写入 Elasticsearch
- 查询阶段:
- 权限与知识库配置从 PostgreSQL 获取
- 热点问题优先查 Redis
- 关键词召回走 Elasticsearch
- 语义召回走向量数据库
- 结果重排后再喂给大模型
为什么这个架构比“只上向量库”更稳
因为企业知识库从来不只是“找语义相近文档”,还包括:
- 标题和关键字搜索
- 权限过滤
- 版本控制
- 数据回溯
- 来源标记
- 更新流水线治理
只靠向量数据库,通常做不完整。
SQL、MongoDB、Redis、Elasticsearch、RocketMQ 在同一业务里的角色差异
以“订单支付成功”这个动作举例。
1. 关系型数据库:记录最终业务状态
UPDATE orders
SET status = 'PAID', paid_at = NOW()
WHERE order_no = 'ORD202606270001'
AND status = 'CREATED';
它负责“真相”。
2. Redis:做快速状态判断或幂等辅助
SETNX idem:pay:ORD202606270001 1
EXPIRE idem:pay:ORD202606270001 86400
它负责“防重”和“加速”。
3. MongoDB:记录支付事件快照
db.order_snapshots.insertOne({
orderNo: "ORD202606270001",
status: "PAID",
paidAt: new Date(),
snapshotType: "ORDER_PAID_EVENT",
payload: {
amount: 199.0,
channel: "wechat"
}
});
它适合“半结构化快照”。
4. Elasticsearch:让运营或客服能快速搜索订单文档副本
POST /order_search/_search
Content-Type: application/json
{
"query": {
"multi_match": {
"query": "ORD202606270001 微信支付",
"fields": ["orderNo^3", "paymentChannel", "status"]
}
}
}
它负责“搜”。
5. RocketMQ:通知别的系统后续处理
rocketMQTemplate.convertAndSend("order-events", Map.of(
"eventType", "ORDER_PAID",
"orderNo", "ORD202606270001",
"amount", 199.0
));
它负责“传事件”。
看清这五种角色之后,很多选型争论会自然消失。
常见误区集中澄清
误区一:NoSQL 比关系型数据库更先进
不是。技术不是按“先进落后”选,而是按“问题匹配”选。
误区二:MongoDB 没 schema,所以开发更快
短期可能快,长期如果缺少约束和治理,也可能变成维护灾难。
误区三:Redis 可以替代数据库
除非你的业务极其特殊,否则大多数时候不应该这么做。
误区四:Elasticsearch 既然也能存 JSON,就可以直接当主库
不建议。能存不等于适合承担核心交易真相源职责。
误区五:向量数据库会取代 Elasticsearch
不会。两者解决的问题重叠一部分,但不是同一类引擎。
误区六:有了 RocketMQ,系统自然就解耦了
错。没有清晰事件边界、没有幂等、没有补偿,MQ 只会把复杂度从同步调用搬到异步调用里。
误区七:组件越多,架构越先进
大多数时候正好相反。组件越多,治理成本越高。真正成熟的架构不是组件堆叠,而是职责分离恰到好处。
如何避免把存储选型做成过度设计
这是最后必须讲的一段。很多系统不是技术做错,而是“想得太大、上得太早”。
一个很实用的反过度设计清单
在你准备新引入一种存储或中间件之前,先过一遍下面这张表。
| 检查项 | 应该问自己的问题 |
|---|---|
| 问题是否真实存在 | 是真的有性能瓶颈,还是只是预设会有? |
| 现有系统是否已经够用 | 当前数据库或现有组件真的扛不住了吗? |
| 新组件职责是否清晰 | 它到底负责真相、缓存、搜索,还是异步事件? |
| 数据边界是否清楚 | 谁是主数据,谁是副本,谁可以重建? |
| 团队是否有运维能力 | 监控、备份、告警、容量规划、故障演练能否跟上? |
| 是否会引入双写一致性问题 | 主库与缓存、索引、向量库之间如何同步? |
| 是否会让排查变复杂 | 出问题后是更容易定位,还是更难? |
| 是否真有替代价值 | 新组件带来的收益,是否明显大于治理成本? |
一条非常实用的经验
如果一个需求能用现有主库 + 少量索引 + 合理 SQL 解决,就先别急着上 MongoDB。
如果一个语义检索需求还没验证价值,就先别急着上独立向量数据库,先试 PostgreSQL + pgvector。
如果一个异步流程只有一个消费者、量也不大,就先别急着拆成完整事件总线。
如果只是热点查询慢,就先看数据库索引和 SQL,再决定是否加 Redis,而不是一上来就把全链路缓存化。
架构设计最难的不是“知道有哪些组件”,而是知道什么时候不要引入它们。
最后的选型速查表
优先选关系型数据库的场景
- 订单、支付、库存、财务
- 用户、权限、组织主数据
- 需要事务和约束
- 需要报表、聚合和审计
优先选 MongoDB 的场景
- 文档结构经常变化
- 对象整体读写多
- 内容、画像、配置、模板型数据
优先选 Redis 的场景
- 热点缓存
- 登录态和 token
- 限流、计数、排行、去重
- 轻量实时状态
优先选 Elasticsearch 的场景
- 商品搜索
- 文档全文搜索
- 日志检索
- 多字段相关性搜索与聚合
优先选向量数据库的场景
- RAG
- 语义搜索
- 相似案例召回
- 图文相似检索
优先选 RocketMQ 的场景
- 异步解耦
- 流量削峰
- 事件驱动协作
- 多系统最终一致性
总结
如果把整篇文章压缩成几句话,其实结论并不复杂:
- 关系型数据库 负责核心业务真相,特别适合事务和约束要求高的系统。
- NoSQL 不是替代关系型数据库,而是为特定问题提供更合适的数据模型。
- MongoDB 适合半结构化对象数据,但不等于天然适合所有业务主库。
- Redis 不是“只是缓存”,而是高性能内存数据结构服务,擅长做性能层和状态层。
- Elasticsearch 负责“搜”,不是负责“真相”。
- 向量数据库 负责语义召回,不负责订单和支付。
- RocketMQ 负责异步事件和系统解耦,不负责业务查询存储。
真正成熟的架构,不是每个问题都引入一个新组件,也不是执着于“全都放到一个库里”。
真正成熟的架构是:
- 真相源明确
- 边界清晰
- 组件职责单一
- 数据流可追踪
- 故障时能排查
- 团队能长期维护
做到这些,存储与基础设施选型通常就不会走偏。