一、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}}}
]
}
}
}