搜索引擎完全教程 — 从"倒排索引是什么"到"线上怎么不挂"
LIKE '%碎花%'),那就得等到明天。而且 LIKE 只能匹配字面文字,不懂"碎花"和"小碎花"有关联。| 维度 | MySQL | Elasticsearch |
|---|---|---|
| 定位 | 关系型数据库,存数据 | 搜索引擎,找数据 |
| 底层结构 | B+Tree 索引 | 倒排索引(Inverted Index) |
| 全文搜索 | LIKE '%关键词%' — 慢,不智能 | 分词 + 倒排索引 — 快,智能 |
| 数据结构 | 表(固定列) | JSON 文档(灵活) |
| 强项 | 事务、JOIN、数据一致性 | 搜索、聚合分析、近实时 |
| 弱项 | 全文搜索慢,不适合日志分析 | 不支持事务,不适合做主存储 |
| 典型场景 | 订单、账户、库存 | 搜索框、日志分析、监控大屏 |
| 数据库 | Database | Index(索引) |
| 表 | Table | Type(7.x 后废弃,一个 Index 只有一个 Type) |
| 行 | Row | Document(文档) |
| 列 | Column | Field(字段) |
| 主键 | Primary Key | _id |
| SQL | SELECT * FROM ... | GET /index/_search { ... } |
不需要扫描所有文档!直接查"碎花"的倒排表和"连衣裙"的倒排表,取交集就是结果。
这就是为什么 ES 能在 1 亿条数据中毫秒级返回结果。
一个 Index 就是一个逻辑命名空间,包含一堆 JSON 文档。比如:products 索引存所有商品,orders 索引存所有订单。
| 节点类型 | 作用 | 类比 |
|---|---|---|
| Master Node | 管理集群状态、创建/删除索引、分配分片 | 图书馆馆长(管目录的完整性) |
| Data Node | 存储数据、执行 CRUD 和搜索 | 书架(真正存书的地方) |
| Coordinating Node | 接收请求、分发到各分片、汇总结果 | 前台咨询台(帮你查,自己不存书) |
| Ingest Node | 数据预处理(Pipeline) | 图书编目室(新书上架前的整理、贴标签) |
Mapping 就是 ES 的"表结构定义"。它告诉 ES:这个字段是什么类型、要不要分词、用什么分词器。
重要:ES 可以自动推断类型(Dynamic Mapping),但生产中强烈建议手动定义 Mapping,避免自动推断出错。
| 类型 | 说明 | 是否分词 | 典型场景 |
|---|---|---|---|
| text | 全文搜索 | ✅ 分词 | 商品名称、文章标题、描述 |
| keyword | 精确匹配 | ❌ 不分词 | 分类、标签、状态、排序字段 |
| integer/long | 整数 | ❌ | 价格、数量、ID |
| float/double | 浮点数 | ❌ | 评分、金额 |
| boolean | 布尔 | ❌ | 是否上架、是否删除 |
| date | 日期 | ❌ | 创建时间、更新时间 |
| object | 嵌套JSON | 按字段 | 内嵌对象 |
| nested | 嵌套数组(独立查询) | 按字段 | 订单商品列表(需要独立匹配) |
| geo_point | 经纬度 | ❌ | 门店位置、附近搜索 |
| completion | 自动补全 | 特殊 | 搜索建议、下拉提示 |
ES 自带的标准分词器对中文只能逐字拆分,不能理解词意。生产中必须装 IK 分词器。
ik_max_word(最细粒度,搜索时更容易命中),搜索时用 ik_smart(粗粒度,减少无关结果)。
term 不分词,如果字段是 text 类型,存储时已经分词了。{"name": "芝士草莓淘宝搜索"},name 是 text 类型,存储时分词为 ["芝士","草莓","淘宝搜索"]。term: {name: "芝士草莓淘宝搜索"} 去查 → 查不到!因为倒排索引里根本没有"芝士草莓淘宝搜索"这个完整的词。match,精确匹配用 keyword 类型的字段。
为什么搜索也快?因为查询被并行发送到所有分片,每个分片只处理一部分数据。最后协调节点合并结果。
但:如果分片数太多,协调节点合并的开销也很大。
| 数据量 | 推荐主分片数 | 推荐副本数 | 说明 |
|---|---|---|---|
| < 10GB | 1 | 1 | 小数据量不需要分片 |
| 10GB ~ 100GB | 3~5 | 1 | 中等规模 |
| 100GB ~ 1TB | 5~10 | 1~2 | 大中型项目 |
| > 1TB | 10~20 | 1~2 | 大型项目,注意分片不要太多 |
_cat/shards API 查看各分片大小java.lang.IllegalArgumentException: Limit of total fields [1000] has been exceeded
"index.mapping.total_fields.limit": 1000 限制最大字段数"dynamic": "strict" 禁止自动添加新字段(推荐生产环境配置)"dynamic": "false" 忽略未知字段(不报错但不索引)from=50000, size=10 报错:Result window is too large, from + size must be less than or equal to: [10000]
PUT /index/_settings {"index.max_result_window": 50000}(不推荐)GET /products/_search { "sort": [{ "name": "asc" }] }Fielddata is disabled on text fields by default. Set fielddata=true on [name]name.keyword 子字段(Multi-field)!
mapper [name] cannot be changed from type [text] to [keyword]| 项目 | 检查内容 | 推荐配置 |
|---|---|---|
| Mapping | 手动定义了吗?dynamic=strict? | 生产必须预定义,禁止自动推断 |
| 分词器 | 中文项目装了IK吗? | IK + 自定义词典 |
| 分片数 | 合理吗?不要太多也不要太少 | 每个分片 10~50GB |
| 副本 | 至少1个副本 | replicas: 1(生产至少1) |
| 刷新间隔 | 默认1秒,写多可以调大 | 写密集场景设 5~30 秒 |
| 内存 | JVM 堆内存 ≤ 32GB | 分配物理内存的 50%,不超过 32GB |
| 磁盘 | 水位线设了吗? | cluster.routing.allocation.disk.watermark: 85% |
| ILM | 日志索引有自动清理策略吗? | hot-warm-delete 三阶段 |
| 安全 | 开启了认证和TLS吗? | xpack.security.enabled: true |
| 监控 | 有监控吗? | Kibana + Metricbeat 或 Prometheus |
| 数据同步 | MySQL → ES 的同步方案确定了吗? | MQ 异步 或 Canal |
| 熔断 | 有查询保护吗? | 限制 max_result_window、超时时间 |