Elasticsearch 是一款分佈式,RESTful 風格的搜索和數據分析引擎,可以從海量的數據中高效的找到相關信息。如 wiki 用 ES 進行全文檢索及其高亮,Github 用其檢索代碼,電商平臺用其做一些商品推薦等,具有豐富的使用場景。
在本篇文章中,主要涉及以下內容:
- ES 的核心功能及其應用場景的介紹
- ES 邏輯架構(文檔,索引)的介紹
- ES 物理架構(集羣,節點,shard 等)的介紹
- ES 環境安裝
- ES 倒排索引
基礎介紹
ES 介紹
Elasticsearch 核心功能:
- 海量數據分佈式存儲及其集羣管理
- 服務高可用 - 允許有節點停止服務,但集羣可正常服務
- 數據高可用 - 允許節點丟失,但不會丟失數據
- 可拓展性 - 很好的面對請求量的提升,和數據的不斷增長。
- 大數據實時搜索引擎
- 結構化數據
- 全文數據
- 地理位置
- 近實時分析
- 聚合
Elasticsearch 核心特性:
- 高性能,非 T +1
- 相較於傳統關係型數據庫,在搜索,算分,模糊查詢上有非常好的體驗。
- 相較於大數據分析 Hadoop,具有更高效率的統計和分析能力。
- 容易擴展
- 本身分佈式架構
- 豐富的社區生態
ES 起源歷史
Lucene 是由 Java 開發的一款搜索引擎類庫,具有高性能,易拓展的優點,但由於其接口只能爲 Java ,並且不支持水平拓展的侷限性。
2004 年 Shay Banon 基於 Lucene 開發了 Compass,2010 年 重寫了 Compass,取名 Elasticsearch,使其支持分佈式,可水平拓展,並提供 restful 接口,讓任何編程語言進行使用。
ES 生態圈
ES 常常搭配一些產品提供一些解決方案,如常提到的 ELK 就是,ES,Logstash 和 Kibana 的統稱,下圖很好的描述了 ES 家族及其生態。
其中 Beat 相較於 Logstash 更加輕量和便攜。
ES 常用案例架構
ES 搜索案例,ES 雖然可以單獨可以存儲引擎,但其無法滿足一些事務性的需要,所以常和關係型數據庫搭配,採用如下架構:
ES 日誌和指標分析案例,一般就是指數據收集,入庫,可視化的過程,常採用如下的架構:
ES 環境搭建
ES 有正常安裝和 docker 安裝兩種方式。考慮的安裝的方便,推薦 docker 的方式,下面是對應的 compose 文件,直接啓動即可。共有 5 個組件,其中 3 個 ES 集羣,kibana 作爲數據展示和操作 es 的重要工具,cerebro 爲查詢集羣狀態的工具。
這裏使用的 ES 爲 7.1 版本。
# docker-compose.yml
version: '2.2'
services:
cerebro:
image: lmenezes/cerebro:0.8.3
container_name: cerebro
ports:
- "9001:9000"
command:
- -Dhosts.0.host=http://elasticsearch:9200
networks:
- es7net
kibana:
image: docker.elastic.co/kibana/kibana:7.1.0
container_name: kibana7
environment:
- I18N_LOCALE=zh-CN
- XPACK_GRAPH_ENABLED=true
- TIMELION_ENABLED=true
- XPACK_MONITORING_COLLECTION_ENABLED="true"
ports:
- "5601:5601"
networks:
- es7net
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.1.0
container_name: es7_01
environment:
- cluster.name=esdemo
- node.name=es7_01
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- discovery.seed_hosts=es7_01,es7_02,es_03
- cluster.initial_master_nodes=es7_01,es7_02,es_03
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- es7data1:/usr/share/elasticsearch/data
ports:
- 9201:9200
networks:
- es7net
elasticsearch2:
image: docker.elastic.co/elasticsearch/elasticsearch:7.1.0
container_name: es7_02
environment:
- cluster.name=esdemo
- node.name=es7_02
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- discovery.seed_hosts=es7_01,es7_02,es_03
- cluster.initial_master_nodes=es7_01,es7_02,es_03
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- es7data2:/usr/share/elasticsearch/data
networks:
- es7net
elasticsearch3:
image: docker.elastic.co/elasticsearch/elasticsearch:7.1.0
container_name: es7_03
environment:
- cluster.name=esdemo
- node.name=es7_03
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- discovery.seed_hosts=es7_01,es7_02,es_03
- cluster.initial_master_nodes=es7_01,es7_02,es_03
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- es7data3:/usr/share/elasticsearch/data
networks:
- es7net
volumes:
es7data1:
driver: local
es7data2:
driver: local
es7data3:
driver: local
networks:
es7net:
driver: bridge
使用 dockr-compose up 命令創建即可:
這裏訪問 http://10.124.207.150:5601/ 即可進入,kibana 管理頁面。
其中開發者工具是學習 ES 非常好的工具,其自動補全功能,可以很好的熟悉 ES 提供的各種 API.
ES 架構
在討論 ES 架構前,我們先來區分兩種類型的架構:
- 一種偏向於開發人員的視角 - 邏輯架構,其中對應索引,文檔等概念。
- 一種偏向於運維人員的視角 - 物理架構,如 節點,集羣,分片等。
ES 邏輯架構
文檔
ES 中最小的單位爲 doc,並且是搜索數據的最小單位。文檔以 JSON 的格式,保存在 ES 中。每個文檔可有不同的字段類型組成。
下面爲一個常見文檔的舉例:
其中:
index 可以理解成所在的索引
_type 表示是文檔類型,將來該字段會被廢棄,默認都是 doc 類型。
_id 表示唯一標識文檔的指示符。
_seq_no 在併發控制會用到,表示併發更新的序列號。
found 表示該 doc 存在。
_source 是我們真正存儲到 ES 的信息。
索引
Index - 索引,是多個文檔的集合,其體現了邏輯空間的概念,每個索引都有自己的 Mapping 定義,表示文檔的字段名和類型。
其中索引較爲重要的設置分爲:
- Mapping 設置:定義了文檔字段的類型
- Setting 設置:定義了不同的數據分佈。
同樣看一個索引的例子:
這裏的 mapping 定義了 company,user 兩個屬性。
settings 定義了對應數據以幾個分片和副本分佈在 ES 中,對應物理架構中的概念。
文檔和索引的類比
爲了方便理解,這裏以我們熟悉的關係型數據庫進行類比。
其中索引的概念,可以理解成數據庫中的一張表,其中 mapping 和 setting 對應表結構和 scheme 的定義。
文檔,可以理解成表中的一行記錄。
文檔中的 field 可以理解成一行記錄中的某一列內容。
拿實際情況舉例,文檔的內容可以想象成日誌文件的一條日誌記錄,一本電影的具體信息,一篇 PDF 文檔的內容,一本書的內容等等。
ES 物理架構
ES 作爲分佈式的系統,可以很好的滿足可用性和拓展性。集羣是分佈式系統中一個常見的概念,在 ES 集羣中,是由多個 ES 節點組成。而每個節點具有不同的角色。
在最新 ES8 中角色如下:
這裏先着重一些常用節點類型:
節點名稱 | 節點角色 | 節點描述 |
---|---|---|
Master-eligible node | master | 參加集羣的選舉,可以成爲 master 節點,進而控制整個集羣,修改集羣的狀態 |
Data Node | data | 保存數據的節點,負責保存分片數據,同時執行相關 CRUD,search,agg 操作 |
coordinating node | 默認每個節點都是 coordinating node 節點,接收 client 請求,然後把結果聚合在一起。(實際上分爲兩個階段,scatter 節點,會去 data node 請求數據,gather 階段,把數據組合到一起。) | |
Hot & Warm node | data_warm/data_cold | 不同硬件配置的 Data Node,用於降低成本。 |
下圖很好的描述了,ES 水平拓展的過程。
每個 ES 節點,其實就是一個 java 進程,當 ES 集羣中只有一個節點時,本身默認就是一個 master eligible 節點。
master 節點會維護集羣的狀態信息:
- 所有節點的信息
- 所有索引和其相關 Mapping 和 Setting 信息
- 分片的路由信息
分片與副本
ES 在保存數據時,會將數據保存到 Shard 中。Shard 共有兩種類型:
- Primary Shard:將數據分佈在整個集羣內,解決數據水平擴展的問題
- 每個 shard 是一個 lucene 實例
- 數據如何分佈在 shard,通過在創建索引時,指定 shard 數量,創建後不允許修改
- Replica Shard:解決數據可用性的問題,是 Primary Shard 的副本
- 副本分片數:可以爲 Primary Shard 設置副本的數據,可以動態調整
- 副本可以增加一定的吞吐量
ES 數據結構
我們知道,ES 在搜索方面有着非常好的性能體驗,這很大就取決於 ES 本身使用了倒排索引作爲存儲的數據結構。
wiki 上對倒排索引的定義是這樣:
被用來存儲在全文搜索下某個單詞在一個文檔或者一組文檔中的存儲位置的映射。
理解起來很抽象是不是,看一個具體的例子:
假設有這樣三句話,我們想要被搜索:
I Love Java.
PHP is the best programming language.
Java awesome.
先來看下正排索引的方式,就是常見關係型數據 MySQL 那類的搜索方式。
正排索引:從 id 到內容的查詢過程
這裏有一行用一個 id 標識對應內容,然後存儲
倒排索引:從內容反向查 id 的過程
將每一句話分詞後,採用 id 加 位置的形式標識。
比如這裏的 java 在文檔 1 和 文檔 3 都出現了,所以記錄了對應的 id 及其 java 在每句話中的位置。
這時我們想搜索 java 的相關文檔信息,採用正排索引就需要逐行的遍歷,可以想象效率很差。但通過倒排索引,可以很快的找到相關的文檔信息。
倒排索引有兩部分內容組成:
- 單詞詞典(Term Dictionary),記錄所有文檔的單詞,以及單詞到倒排列表的關係
- 一般具體有 B+ 和哈希拉鍊發實現
- 倒排列表(Posting List),記錄到此對應的文檔集合,由倒排索引項組成。
- 倒排索引項:
- 文檔 ID
- 詞頻 TF:該單詞在文檔中出現的次數,相關性評分
- 位置 - 單詞在文檔中分詞的位置,用於語句搜索。
- 偏移 - 記錄單詞的開始和結束位置,高亮顯示。
- 倒排索引項:
比如上面的 Java 就組成的倒排索引就是這個樣子:
單詞 ID | 詞項內容 | 倒排列表(doc_id/TF/POS/Offset) |
---|---|---|
1 | java | [(1/1/2/4), (3/1/1/4)] |
當想要搜索 java 時,會查找由 B+ 或者哈希拉鍊法構成的單詞詞典,然後根據詞典記錄的倒排列表關係,獲取相應的倒排列表。
總結
本篇文章是 ES 部分的第一篇文章,主要對 ES 的基本概念,安裝方式,整體架構,數據結構做了整體的入門介紹,便於對 ES 有一定初步的認識,後續的文章對某些部分進入深入的講解。
最後分享功夫熊貓中的一段話:
昨天已成過去,明天還沒有到來,但今天是一個禮物。😊