一、Elasticsearch 是什么?
Elasticsearch 是一个基于 Apache Lucene 构建的开源、分布式、RESTful 搜索和分析引擎。它是 Elastic Stack(ELK Stack)的核心组件,由 Elastic 公司开发和维护。
核心特性
- 分布式架构:天然支持集群部署,可横向扩展
- 实时搜索:近实时(NRT)的数据索引和搜索能力
- 全文检索:强大的文本分析和搜索功能
- RESTful API:通过 HTTP 接口进行所有操作
- 多租户:支持多索引、多类型的数据存储
- Schema-free:动态映射,自动识别数据类型
二、为什么需要 Elasticsearch?
1. 传统数据库的局限性
关系型数据库的搜索痛点:
sql
-- MySQL 模糊查询性能问题
SELECT * FROM products
WHERE description LIKE '%手机%'
OR name LIKE '%手机%';
-- 问题:无法使用索引,全表扫描,性能极差
- 全文搜索效率低:LIKE 查询无法利用索引
- 中文分词能力弱:无法智能分词
- 相关性排序困难:缺乏 TF-IDF、BM25 等算法
- 扩展性差:垂直扩展成本高
2. Elasticsearch 的解决方案
| 需求 | 传统数据库 | Elasticsearch |
|---|---|---|
| 全文检索 | LIKE 全表扫描 | 倒排索引,毫秒级响应 |
| 中文分词 | 不支持 | IK、jieba 等分词器 |
| 相关性排序 | 困难 | BM25、TF-IDF 算法 |
| 数据量扩展 | 垂直扩展 | 水平扩展(分片) |
| 复杂聚合 | GROUP BY 性能差 | 高性能聚合框架 |
三、Elasticsearch 为什么这么快?
1. 倒排索引(Inverted Index)
核心原理:
传统数据库使用正排索引 (文档 → 内容),而 ES 使用倒排索引(词 → 文档)。
示例对比:
文档数据:
Doc1: "快速的棕色狐狸"
Doc2: "懒惰的棕色狗"
Doc3: "快速的棕色狗"
正排索引(传统):
Doc1 → "快速的棕色狐狸"
Doc2 → "懒惰的棕色狗"
Doc3 → "快速的棕色狗"
查询"快速"需要遍历所有文档
倒排索引(ES):
快速 → [Doc1, Doc3]
棕色 → [Doc1, Doc2, Doc3]
狐狸 → [Doc1]
狗 → [Doc2, Doc3]
懒惰 → [Doc2]
查询"快速"直接定位到 Doc1 和 Doc3
倒排索引结构:
Term Dictionary(词典):
├─ 快速 → Posting List
├─ 棕色 → Posting List
└─ 狐狸 → Posting List
Posting List(倒排列表):
快速 → [DocID:1, DocID:3] + [词频, 位置信息]
2. 数据结构优化
(1)FST (Finite State Transducer) - 词典压缩
- 将 Term Dictionary 压缩存储,节省内存
- 支持前缀查询和模糊匹配
- 内存占用极小(MB 级别索引词典)
(2)跳表(Skip List)- 快速求交集
查询: "快速" AND "棕色"
Doc List 1: [1, 3, 5, 7, 9, 100, 200]
Doc List 2: [1, 2, 3, 4, 100, 150, 200]
跳表结构实现快速跳跃:
Level 2: 1 ---------> 100 ---------> 200
Level 1: 1 ---> 9 --> 100 --> 200
Level 0: 1-3-5-7-9-100-200
快速定位交集: [1, 3, 100, 200]
(3)Roaring Bitmap - 整数压缩
- 对 DocID 进行位图压缩存储
- 大幅减少内存占用
- 快速位运算(AND、OR、NOT)
3. 分片与并行
索引分片架构:
Index "products"
├─ Shard 0 (Primary) → 文档 0-999
│ └─ Replica 0
├─ Shard 1 (Primary) → 文档 1000-1999
│ └─ Replica 1
└─ Shard 2 (Primary) → 文档 2000-2999
└─ Replica 2
查询执行:
1. 查询请求分发到所有分片
2. 每个分片并行执行
3. 协调节点合并结果
4. 时间复杂度:O(n/m),m为分片数
4. 文档存储优化
(1)列式存储(Doc Values)
行式存储(传统):
Doc1: {name:"iPhone", price:5999, brand:"Apple"}
Doc2: {name:"Mate60", price:6999, brand:"Huawei"}
列式存储(Doc Values):
price: [5999, 6999, ...] ← 连续存储,聚合超快
brand: ["Apple", "Huawei", ...]
- 聚合、排序性能提升 10-100 倍
- 减少磁盘 I/O
(2)压缩存储
- LZ4 压缩算法(索引数据)
- DEFLATE 压缩(存储数据)
- 压缩比可达 80%
5. 缓存机制
三层缓存体系:
1. Node Query Cache(节点查询缓存)
- 缓存过滤器结果
- LRU 策略
2. Shard Request Cache(分片请求缓存)
- 缓存聚合结果
- size=0 的查询结果
3. Field Data Cache(字段数据缓存)
- 缓存字段值用于排序/聚合
- 占用堆内存
四、核心工作原理
1. 写入流程
写入请求流程:
Client
↓ (1) 写入请求
Coordinating Node(协调节点)
↓ (2) 路由到主分片: hash(routing) % num_primary_shards
Primary Shard
↓ (3) 写入 Memory Buffer
↓ (4) 写入 Transaction Log(持久化保证)
↓ (5) refresh(默认1秒) → 生成 Segment
↓ (6) 同步到副本分片
Replica Shards
↓ (7) 返回成功响应
Client
关键概念:
- Refresh:内存数据写入文件系统缓存,变为可搜索(默认 1 秒)
- Flush:文件系统缓存刷入磁盘,清空 TransLog(默认 30 分钟)
- Merge:合并小 Segment,删除标记文档
2. 搜索流程
查询两阶段:
Phase 1: Query Phase(查询阶段)
Client → Coordinating Node
↓ 广播到所有分片
↓ 每个分片执行查询
↓ 返回 DocID + Score
↓ 协调节点合并排序
↓ 确定需要的文档ID
Phase 2: Fetch Phase(获取阶段)
↓ 向相关分片获取完整文档
↓ 返回最终结果给客户端
3. 分词与分析
分析流程:
原始文本: "Elasticsearch很快!"
↓
Character Filter(字符过滤)
- HTML 标签清理
- 特殊符号处理
↓
Tokenizer(分词器)
- IK分词: ["Elasticsearch", "很", "快"]
- Standard: ["elasticsearch", "很快"]
↓
Token Filter(词元过滤)
- 转小写: elasticsearch
- 停用词: 去除"的""了"
- 同义词: "快速" → "迅速"
↓
最终Term: ["elasticsearch", "快"]
4. 相关性算分(BM25)
BM25 算法(Elasticsearch 5.0+ 默认):
score(q,d) = ∑ IDF(qi) · TF(qi,d) · boost
其中:
- IDF: 逆文档频率(词的稀缺性)
- TF: 词频(归一化后的出现次数)
- boost: 权重提升
示例:
查询: "Elasticsearch 教程"
文档1: "Elasticsearch 完整教程..." (出现3次)
文档2: "Elasticsearch 简介" (出现1次)
IDF(Elasticsearch) = log(总文档数 / 包含该词的文档数)
TF(d1) = 3 / (3 + k*(1-b+b*文档长度/平均长度))
五、主要使用场景
1. 全文搜索引擎
典型应用:电商搜索
json
POST /products/_search
{
"query": {
"multi_match": {
"query": "苹果手机",
"fields": ["title^3", "description", "brand^2"],
"fuzziness": "AUTO"
}
},
"highlight": {
"fields": {
"title": {},
"description": {}
}
},
"aggs": {
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{"to": 2000},
{"from": 2000, "to": 5000},
{"from": 5000}
]
}
}
}
}
应用场景:
- 电商平台商品搜索(淘宝、京东)
- 内容平台文章搜索(知乎、CSDN)
- 企业内部知识库检索
2. 日志分析(ELK Stack)
架构:
日志来源
↓
Filebeat/Logstash(采集)
↓
Elasticsearch(存储+分析)
↓
Kibana(可视化)
典型查询:
json
GET /logs-*/_search
{
"query": {
"bool": {
"must": [
{"range": {"@timestamp": {"gte": "now-1h"}}},
{"term": {"level": "ERROR"}}
]
}
},
"aggs": {
"error_trends": {
"date_histogram": {
"field": "@timestamp",
"interval": "5m"
},
"aggs": {
"top_errors": {
"terms": {"field": "message.keyword"}
}
}
}
}
}
应用场景:
- 应用性能监控(APM)
- 安全事件分析
- 业务指标统计
3. 实时数据分析
示例:用户行为分析
json
POST /user_events/_search
{
"size": 0,
"query": {
"range": {"timestamp": {"gte": "now-7d"}}
},
"aggs": {
"daily_active_users": {
"date_histogram": {
"field": "timestamp",
"interval": "day"
},
"aggs": {
"unique_users": {
"cardinality": {"field": "user_id"}
},
"top_pages": {
"terms": {"field": "page_url", "size": 10}
}
}
}
}
}
应用场景:
- 用户行为分析(UV、PV)
- 实时大屏监控
- 异常检测告警
4. 地理位置搜索
json
POST /restaurants/_search
{
"query": {
"bool": {
"must": {"match": {"type": "川菜"}},
"filter": {
"geo_distance": {
"distance": "5km",
"location": {
"lat": 39.9042,
"lon": 116.4074
}
}
}
}
},
"sort": [
{
"_geo_distance": {
"location": {"lat": 39.9042, "lon": 116.4074},
"order": "asc",
"unit": "km"
}
}
]
}
应用场景:
- 外卖/打车附近搜索
- 地图 POI 检索
- 物流配送优化
5. 推荐系统
基于内容的推荐:
json
POST /articles/_search
{
"query": {
"more_like_this": {
"fields": ["title", "content", "tags"],
"like": [
{"_id": "1"}
],
"min_term_freq": 1,
"max_query_terms": 12
}
}
}
应用场景:
- 内容推荐(相似文章)
- 商品推荐
- 用户画像匹配
六、性能对比
实测数据对比
| 指标 | MySQL | Elasticsearch |
|---|---|---|
| 1000万数据全文检索 | 5-10秒 | 50-200ms |
| 复杂聚合查询 | 10-30秒 | 100-500ms |
| 并发查询 QPS | ~1000 | ~10000+ |
| 水平扩展 | 困难 | 简单(增加节点) |
| 数据写入 TPS | ~5000 | ~20000+ |
适用性对比
选择 Elasticsearch:
- ✅ 全文搜索需求
- ✅ 海量日志分析
- ✅ 复杂聚合统计
- ✅ 需要水平扩展
选择关系型数据库:
- ✅ 强事务需求(ACID)
- ✅ 复杂关联查询
- ✅ 数据强一致性
- ✅ 数据量小(百万级以下)
七、最佳实践建议
1. 索引设计
json
PUT /products
{
"settings": {
"number_of_shards": 3, // 分片数(创建后不可改)
"number_of_replicas": 1, // 副本数(可动态调整)
"refresh_interval": "5s" // 写入密集时可调大
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word", // 索引时分词
"search_analyzer": "ik_smart" // 查询时分词
},
"price": {
"type": "scaled_float",
"scaling_factor": 100
},
"created_at": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
},
"tags": {
"type": "keyword" // 不分词,精确匹配
}
}
}
}
2. 查询优化
(1)避免深分页
json
// ❌ 错误:深分页性能差
GET /products/_search?from=10000&size=10
// ✅ 正确:使用 search_after
GET /products/_search
{
"size": 10,
"search_after": [1234567890, "doc_id"],
"sort": [{"timestamp": "desc"}, {"_id": "desc"}]
}
(2)合理使用过滤器
json
// ✅ filter 会被缓存,性能更好
{
"query": {
"bool": {
"must": {"match": {"title": "手机"}},
"filter": [ // 精确匹配放 filter
{"term": {"status": "active"}},
{"range": {"price": {"gte": 1000}}}
]
}
}
}