ES簡介
一個叫Shay Banon開發(fā)者,在2010年2月發(fā)布了第一個公開版本。
Elasticsearch(ES)是一個基于Lucene(Lucene:Lucene是一個Java全文搜索引擎,完全用 Java 編寫。Lucene不是一個完整的應用程序,而是一個代碼庫和API)構(gòu)建的開源、分布式、RESTful接口的全文搜索引擎。Elasticsearch還是一個分布式文檔數(shù)據(jù)庫,其中每個字段均可被索引,而且每個字段的數(shù)據(jù)均可被搜索,ES能夠橫向擴展至數(shù)以百計的服務器存儲以及處理PB級的數(shù)據(jù)??梢栽跇O短的時間內(nèi)存儲、搜索和分析大量的數(shù)據(jù)。通常作為具有復雜搜索場景情況下的核心發(fā)動機。
相關(guān)概念解讀
全文檢索引擎
其工作原理是計算機索引程序通過掃描文章中的每一個詞,對每一個詞建立一個索引,指明該詞再文章中出現(xiàn)的次數(shù)和位置。當用戶查詢時,檢索程序就根據(jù)實現(xiàn)建立的索引進行查找,并將查找的結(jié)果反饋給用戶的檢索方式(舉例:字典)
數(shù)據(jù)類型
- 結(jié)構(gòu)化數(shù)據(jù):具有固定格式或有限長度的數(shù)據(jù)
- 非結(jié)構(gòu)化數(shù)據(jù):又稱全文數(shù)據(jù),指不定長度或無固定格式的數(shù)據(jù)
搜索方式
針對數(shù)據(jù)類型的不同,對應的搜索方式也有別:
- 結(jié)構(gòu)化數(shù)據(jù):通過關(guān)系型數(shù)據(jù)庫的table方式存儲和搜索
- 非結(jié)構(gòu)化數(shù)據(jù):
- 順序掃描————按照順序依次查找->耗時、低效
- 全文檢索————將數(shù)據(jù)中的一部分信息提取出來,重新組織使其變得有一定結(jié)構(gòu),然后對此有一定結(jié)構(gòu)的數(shù)據(jù)進行搜索,從而達到搜索相對較快的目的(這部分從非結(jié)構(gòu)化數(shù)據(jù)中心提取出來的然后重新組織的信息,就叫索引)
全文檢索使用背景
- 搜索數(shù)據(jù)對象是大量的非結(jié)構(gòu)化文本數(shù)據(jù)
- 文件記錄量達到數(shù)十萬或數(shù)百萬甚至更多
- 支持大量基于交互式文本的查詢
- 需要非常靈活的全文搜索查詢
- 對高度相關(guān)的搜索結(jié)構(gòu)有特殊需求
- 對不同記錄類型,非文本數(shù)據(jù)操作或事務處理需求相對較少
主流全文檢索引擎
Lucene、Solr、Elastic Search
ES特點
- 基于分布式————將數(shù)據(jù)進行分段分片存儲并在各個分片上進行查詢操作
- 容差容錯機制————集群部署時,通過設(shè)置副本分片的機制,實現(xiàn)即時一個節(jié)點掛掉,數(shù)據(jù)也能在別的節(jié)點分片上正常進行查詢
- 基于文檔————文檔存儲
- 全文全庫檢索
- 可處理數(shù)據(jù)量大
- 分布式實施文檔存儲,每個字段可以被索引和搜索
- 能勝任上百個服務節(jié)點的擴展,支持PB級別的結(jié)構(gòu)化數(shù)據(jù)和非結(jié)構(gòu)化數(shù)據(jù)
ES索引方式————倒排索引
一個倒排索引由文檔中所有不重復詞的列表構(gòu)成,對于其中每個詞,有一個包含它的文檔列表。
對于不同的文檔中每個詞的出現(xiàn)與否做出標記。
例如有兩個文檔:
- The quick brown fox jumped over the lazy dog
- Quick brown foxes leap over lazy dogs in summer
生成的倒排索引如下圖所示:

ES核心概念
- Cluster集群: 一個集群是由一個或多個節(jié)點組成的(集群中的節(jié)點通過設(shè)置相同的 cluster.name,來分類集群的)
- Node節(jié)點: 一個運行中的Elasticsearch實力稱為一個節(jié)點
- Shade分片: 一個分片是一個底層的工作單元,它僅保存了全部數(shù)據(jù)中的一部分(主分片即主要存儲數(shù)據(jù)的分片,副分片即主分片的備份,存在于不同的節(jié)點上,若節(jié)點越多,主分片對應的副分片存在的節(jié)點也會越多。容差容錯機制就是在備份中產(chǎn)生的,如果運行中一個節(jié)點出現(xiàn)故障、主分片上的數(shù)據(jù)全部丟失,那么存有該主分片副本的節(jié)點就會自動接替之前的工作)
- Index索引: 相當于sql中的database
- Type類型: 相當于db中的table
- Document文檔: 相當于table中的一條數(shù)據(jù)
集群內(nèi)的原理
- 一個運行中的Elasticsearch實例稱為一個節(jié)點,而集群是由一個或者多個擁有相同 cluster.name 配置的節(jié)點組成,它們共同承擔數(shù)據(jù)和負載的壓力。當有節(jié)點加入集群中或者從集群中移除節(jié)點時,集群將會重新平均分布所有的數(shù)據(jù)。
- 當一個節(jié)點被選舉成為主節(jié)點時,它將負責管理集群范圍內(nèi)的所有變更,例如增加、刪除索引,或者增加、刪除節(jié)點等。而主節(jié)點并不需要涉及到文檔級別的變更和搜索等操作。任何節(jié)點都可以成為主節(jié)點。
一個集群中有一個包含空內(nèi)容的節(jié)點

同一個集群中有兩個節(jié)點,其中Node1為主節(jié)點

分片與副本分片
- 一個分片是一個底層的工作單元,它僅保存了全部數(shù)據(jù)中的一部分。
- Elasticsearch 是利用分片將數(shù)據(jù)分發(fā)到集群內(nèi)各處的。分片是數(shù)據(jù)的容器,文檔保存在分片內(nèi),分片又被分配到集群內(nèi)的各個節(jié)點里。當你的集群規(guī)模擴大或者縮小時,Elasticsearch會自動的在各節(jié)點中遷移分片,使得數(shù)據(jù)仍然均勻分布在集群里。
- 一個分片可以是主分片或者副本分片。索引內(nèi)任意一個文檔都歸屬于一個主分片,所以主分片的數(shù)目決定著索引能夠保存的最大數(shù)據(jù)量。
關(guān)于索引和分片
一個Lucene索引在es稱為分片,一個es索引是分片的集合。
Elasticsearch在索引中搜索時,它發(fā)送查詢到每一個屬于索引的分片,然后合并每個分片的結(jié)果到一個全局的結(jié)果集(分布式搜索,后面有提到)。
以下為分片的例子:

(上圖表示有一個節(jié)點,節(jié)點中有三個主分片)

(上圖表示有兩個節(jié)點<其中Node1為主節(jié)點>,且Node中存有三個主分片,Node2中存有三個副本分片<主要的數(shù)據(jù)被復制了一次,產(chǎn)生了一套主分片的副本分片>)

(上圖表示共有三個節(jié)點,主分片被復制了一次,產(chǎn)生了一套副本分片,主分片與副本分片平均分派在三個節(jié)點中)

(上圖表示共有三個節(jié)點,主分片被復制了兩次,產(chǎn)生了兩套副本分片,主分片與副本分片平均分派在三個節(jié)點中)

(上圖表示共有兩個節(jié)點,其中Node1掛掉,隨機分配Node2為主節(jié)點,并轉(zhuǎn)移分配對應的主分片————容差容錯機制的體現(xiàn))
分片內(nèi)的原理
以索引一個新文檔為例:
當用戶向一個節(jié)點提交了一個索引新文檔的請求,節(jié)點會計算新文檔應該加入到哪個分片(shard)中。每個節(jié)點都存儲有每個分片存儲在哪個節(jié)點的信息,因此協(xié)調(diào)節(jié)點會將請求發(fā)送給對應的節(jié)點。注意這個請求會發(fā)送給主分片,等主分片完成索引,會并行將請求發(fā)送到其所有副本分片,保證每個分片都持有最新數(shù)據(jù)。

(一個Lucene索引包含一個提交點和三個段,如上圖所示)

(一個文檔被索引之后,就會被添加到內(nèi)存緩沖區(qū),并且追加到了translog)

(分片每秒被刷新(refresh)一次,這些在內(nèi)存緩沖區(qū)中的文檔被寫入到一個新的段中,這個段被打開,當前可以被搜索到,但該段并未被寫入到磁盤中,因此,此時節(jié)點掛掉的話,該段將不復存在;內(nèi)存緩沖區(qū)被清空,但事務日志不會<為節(jié)點掛掉重啟后做數(shù)據(jù)恢復>)

(這個進程繼續(xù)工作,更多的文檔被添加到內(nèi)存緩沖區(qū)和追加到事務日志)

(每隔一段時間--索引被刷新(flush);一個新的translog被創(chuàng)建,并且一個全量提交被執(zhí)行;所有在內(nèi)存緩沖區(qū)中的文檔被寫入一個新的段中,緩沖區(qū)被清空;一個提交點被寫入磁盤中;文件系統(tǒng)緩存通過fsync被刷新;老的事務日志被刪除)
段合并
圖示(兩個提交了的段和一個未提交的段正在被合并到一個更大的段)

- 背景:由于自動刷新流程每秒會創(chuàng)建一個新的段,這樣會導致短時間內(nèi)的段數(shù)量暴增。而段數(shù)目太多會帶來較大的麻煩。每一個段都會消耗文件句柄、內(nèi)存和cpu運行周期。更重要的是,每個搜索請求都必須輪流檢查每個段;所以段越多,搜索也就越慢。
- 實施:合并進程選擇一小部分大小相似的段,并且在后臺將它們合并到更大的段中。這并不會中斷索引和搜索。
- 結(jié)果:新的段被刷新(flush)到了磁盤;新的段被打開用來搜索;老的段被刪除。
結(jié)果圖示

總流程圖

分布式搜索
圖示

- 查詢請求可以被某個主分片或某個副本分片處理,協(xié)調(diào)節(jié)點將在之后的請求中輪詢所有的分片拷貝來分攤負載。
- 如果客戶端要求返回結(jié)果排序中從第from名開始的數(shù)量為size的結(jié)果集,則每個節(jié)點都需要生成一個from+size大小的結(jié)果集,因此優(yōu)先級隊列的大小也是from+size。分片僅會返回一個輕量級的結(jié)果給協(xié)調(diào)節(jié)點,包含結(jié)果集中的每一個文檔的ID和進行排序所需要的信息。
- 協(xié)調(diào)節(jié)點會將所有分片的結(jié)果匯總,并進行全局排序,得到最終的查詢排序結(jié)果。此時查詢階段結(jié)束。
ES安裝
系統(tǒng)環(huán)境
- CentOS 7.6.1810
- jdk 1.8.0_201
elasticsearch安裝方法
tar -zxvf elasticsearch-6.6.0.tar.gz -C /opt/module/ # 解壓安裝包
vi elasticsearch.yml # 修改配置文件
cluster.name: my-application # 集群名稱(多集群時候只需節(jié)點名稱一直即可)
node.name: node-1 # 節(jié)點名稱————同一集群中不能重復
path.data: /opt/module/elasticsearch-6.6.0/data # 數(shù)據(jù)路徑
path.logs: /opt/module/elasticsearch-6.6.0/logs # 日志路徑
bootstrap.memory_lock: false
bootstrap.system_call_filter: false
network.host: 192.168.1.8 # 網(wǎng)絡地址
http.port: 9200 # 端口
discovery.zen.ping.unicast.hosts: ["Hosts"] # 主機名
ES啟動
elasticsearch禁止使用root用戶啟動,需要新建一個用戶
./bin/elasticsearch // 解壓目錄下執(zhí)行
Spring Boot 集成
使用Spring Data Elasticsearch
增加POM文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
Spring data elasticsearch 與 es 版本對照
| spring data elasticsearch | elasticsearch |
|---|---|
| 3.2.x | 6.5.0 |
| 3.1.x | 6.2.2 |
| 3.0.x | 5.5.0 |
| 2.1.x | 2.4.0 |
| 2.0.x | 2.2.0 |
| 1.3.x | 1.5.2 |
增加配置
spring:
data:
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: 172.0.0.1:9200
repositories:
enabled: true
使用
@Data
@AllArgsConstructor
@Document(indexName = "human", type = "student") ////indexName索引名稱,type類別
@Id
public class Student {
private String name;
private Integer age;
private String sex;
}
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Test
public void test() {
Student student = new Student(1, "張三", 100, "女");
//新增
IndexQuery indexQuery = new IndexQueryBuilder()
.withIndexName("human")
.withType("student")
.withId(student.getId()+"")
.withObject(student) //對象或集合
.build();
elasticsearchTemplate.index(indexQuery);
}
也可通過創(chuàng)建接口實現(xiàn)ElasticsearchRepository<?,?>方法,通過方法注解@Query來實現(xiàn)查詢等操作,示例:
public interface StudentRepository extends ElasticsearchRepository<Student, String> {
@Query("{"bool" : {"must" : {"field" : {"name" : "?0"}}}}")
Page<Student> findByName(String name,Pageable pageable);
}
ES查詢示例
查詢相關(guān)轉(zhuǎn)到