一、Elasticsearch 是什麼?
Elasticsearch 是一個基於 Apache Lucene 構建的開源、分佈式、RESTful 搜索和分析引擎。它是 Elastic Stack(ELK Stack)的核心組件,由 Elastic 公司開發和維護。
核心特性
- 分佈式架構:天然支持集羣部署,可橫向擴展
- 實時搜索:近實時(NRT)的數據索引和搜索能力
- 全文檢索:強大的文本分析和搜索功能
- RESTful API:通過 HTTP 接口進行所有操作
- 多租户:支持多索引、多類型的數據存儲
- Schema-free:動態映射,自動識別數據類型
二、為什麼需要 Elasticsearch?
1. 傳統數據庫的侷限性
關係型數據庫的搜索痛點:
-- 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. 全文搜索引擎
典型應用:電商搜索
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(可視化)
典型查詢:
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. 實時數據分析
示例:用户行為分析
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. 地理位置搜索
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. 推薦系統
基於內容的推薦:
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. 索引設計
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)避免深分頁
// ❌ 錯誤:深分頁性能差
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)合理使用過濾器
// ✅ filter 會被緩存,性能更好
{
"query": {
"bool": {
"must": {"match": {"title": "手機"}},
"filter": [ // 精確匹配放 filter
{"term": {"status": "active"}},
{"range": {"price": {"gte": 1000}}}
]
}
}
}