文章大綱
一、搜索引擎框架基礎介紹
二、ElasticSearch的簡介
三、ElasticSearch安裝(Windows版本)
四、ElasticSearch操作客戶端工具--Kibana
五、ES的常用命令
六、Java連接ElasticSearch進行數據操作
七、項目源碼與參考資料下載
八、參考文章

一、搜索引擎框架基礎介紹
相關基礎學習可參考:https://www.cnblogs.com/WUXIAOCHANG/p/10855506.html
二、ElasticSearch的簡介
1. ElasticSearch是什么
ElasticSearch是智能搜索,分布式的搜索引擎。是ELK的一個組成,是一個產品,而且是非常完善的產品,ELK代表的是:E就是ElasticSearch,L就是Logstach,K就是kibana
(1)E:EalsticSearch 搜索和分析的功能
(2)L:Logstach 搜集數據的功能,類似於flume(使用方法幾乎跟flume一模一樣),是日志收集系統
(3)K:Kibana 數據可視化(分析),可以用圖表的方式來去展示,文不如表,表不如圖,是數據可視化平台

分析日志的用處:假如一個分布式系統有 1000 台機器,系統出現故障時,我要看下日志,還得一台一台登錄上去查看,是不是非常麻煩?
但是如果日志接入了 ELK 系統就不一樣。比如系統運行過程中,突然出現了異常,在日志中就能及時反饋,日志進入 ELK 系統中,我們直接在 Kibana 就能看到日志情況。如果再接入一些實時計算模塊,還能做實時報警功能。
這都依賴ES強大的反向索引功能,這樣我們根據關鍵字就能查詢到關鍵的錯誤日志了。
2. 全文檢索與倒排索引
全文檢索是指計算機索引程序通過掃描文章中的每一個詞,對每一個詞建立一個索引,指明該詞在文章中出現的次數和位置,當用戶查詢時,檢索程序就根據事先建立的索引進行查找,並將查找的結果反饋給用戶的檢索方式。這個過程類似於通過字典中的檢索字表查字的過程。
全文檢索的方法主要分為按字檢索和按詞檢索兩種。按字檢索是指對於文章中的每一個字都建立索引,檢索時將詞分解為字的組合。對於各種不同的語言而言,字有不同的含義,比如英文中字與詞實際上是合一的,而中文中字與詞有很大分別。按詞檢索指對文章中的詞,即語義單位建立索引,檢索時按詞檢索,並且可以處理同義項等。英文等西方文字由於按照空白切分詞,因此實現上與按字處理類似,添加同義處理也很容易。中文等東方文字則需要切分字詞,以達到按詞索引的目的,關於這方面的問題,是當前全文檢索技術尤其是中文全文檢索技術中的難點,在此不做詳述。
以前是根據ID查內容,倒排索引之后是根據內容查ID,然后再拿着ID去查詢出來真正需要的東西。

3. ElasticSearch的優點
(1)分布式的功能
(2)數據高可用,集群高可用
(3)API更簡單
(4)API更高級。
(5)支持的語言很多
(6)支持PB級別的數據
(7)完成搜索的功能和分析功能
(8)基於Lucene,隱藏了Lucene的復雜性,提供簡單的API
(9)ES的性能比HBase高,咱們的競價引擎最后還是要存到ES中的。
(10)Elasticsearch 也是 Master-slave 架構,也實現了數據的分片和備份。
(11)Elasticsearch 中的索引、類型和文檔的概念比較重要,類似於 MySQL 中的數據庫、表和行
(12)Elasticsearch 一個典型應用就是 ELK 日志分析系統
4. ElasticSearch支持的語言
Curl、java、c#、python、JavaScript、php、perl、ruby
5. ElasticSearch的核心概念
5.1 Node節點
就是集群中的一台服務器
5.2 index 索引(索引庫)
我們為什么使用ES?因為想把數據存進去,然后再查詢出來。
我們在使用Mysql或者Oracle的時候,為了區分數據,我們會建立不同的數據庫,庫下面還有表的。
其實ES功能就像一個關系型數據庫,在這個數據庫我們可以往里面添加數據,查詢數據。
ES中的索引非傳統索引的含義,ES中的索引是存放數據的地方,是ES中的一個概念詞匯
index類似於我們Mysql里面的一個數據庫 create database user; 好比就是一個索引庫
5.3 type類型
類型是用來定義數據結構的
在每一個index下面,可以有一個或者多個type,好比數據庫里面的一張表。
相當於表結構的描述,描述每個字段的類型。
5.4 document:文檔
文檔就是最終的數據了,可以認為一個文檔就是一條記錄。
是ES里面最小的數據單元,就好比表里面的一條數據
5.5 Field 字段
好比關系型數據庫中列的概念,一個document有一個或者多個field組成。
例如:
朝陽區:一個Mysql數據庫
房子:create database chaoyaninfo
房間:create table people
5.6 shard:分片
一台服務器,無法存儲大量的數據,ES把一個index里面的數據,分為多個shard,分布式的存儲在各個服務器上面。
kafka:為什么支持分布式的功能,因為里面是有topic,支持分區的概念。所以topic A可以存在不同的節點上面。就可以支持海量數據和高並發,提升性能和吞吐量
5.7 replica:副本
一個分布式的集群,難免會有一台或者多台服務器宕機,如果我們沒有副本這個概念。就會造成我們的shard發生故障,無法提供正常服務。
我們為了保證數據的安全,我們引入了replica的概念,跟hdfs里面的概念是一個意思。可以保證我們數據的安全。
在ES集群中,我們一模一樣的數據有多份,能正常提供查詢和插入的分片我們叫做 primary shard,其余的我們就管他們叫做 replica shard(備份的分片)
當我們去查詢數據的時候,我們數據是有備份的,它會同時發出命令讓我們有數據的機器去查詢結果,最后誰的查詢結果快,我們就要誰的數據(這個不需要我們去控制,它內部就自己控制了)
5.8 總結
在默認情況下,我們創建一個庫的時候,默認會幫我們創建5個主分片(primary shrad)和5個副分片(replica shard),所以說正常情況下是有10個分片的。
同一個節點上面,副本和主分片是一定不會在一台機器上面的,就是擁有相同數據的分片,是不會在同一個節點上面的。
所以當你有一個節點的時候,這個分片是不會把副本存在這僅有的一個節點上的,當你新加入了一台節點,ES會自動的給你在新機器上創建一個之前分片的副本。

舉例:
比如一首詩,有詩題、作者、朝代、字數、詩內容等字段,那么首先,我們可以建立一個名叫 Poems 的索引,然后創建一個名叫 Poem 的類型,類型是通過 Mapping 來定義每個字段的類型。
比如詩題、作者、朝代都是 Keyword 類型,詩內容是 Text 類型,而字數是 Integer 類型,最后就是把數據組織成 Json 格式存放進去了。


5.9 ElasticSearch配置文件詳解
配置文件位於%ES_HOME%/config/elasticsearch.yml文件中,用Editplus打開它,你便可以進行配置。
所有的配置都可以使用環境變量,例如:
node.rack: ${RACK_ENV_VAR}
表示環境變量中有一個RACK_ENV_VAR變量。
下面列舉一下elasticsearch的可配置項:
1. 集群名稱,默認為elasticsearch: cluster.name: elasticsearch 2. 節點名稱,es啟動時會自動創建節點名稱,但你也可進行配置: node.name: "Franz Kafka" 3. 是否作為主節點,每個節點都可以被配置成為主節點,默認值為true: node.master: true 4. 是否存儲數據,即存儲索引片段,默認值為true: node.data: true master和data同時配置會產生一些奇異的效果: 1) 當master為false,而data為true時,會對該節點產生嚴重負荷; 2) 當master為true,而data為false時,該節點作為一個協調者; 3) 當master為false,data也為false時,該節點就變成了一個負載均衡器。 你可以通過連接http://localhost:9200/_cluster/health或者http://localhost:9200/_cluster/nodes,或者使用插件http://github.com/lukas-vlcek/bigdesk或http://mobz.github.com/elasticsearch-head來查看集群狀態。 5. 每個節點都可以定義一些與之關聯的通用屬性,用於后期集群進行碎片分配時的過濾: node.rack: rack314 6. 默認情況下,多個節點可以在同一個安裝路徑啟動,如果你想讓你的es只啟動一個節點,可以進行如下設置: node.max_local_storage_nodes: 1 7. 設置一個索引的碎片數量,默認值為5: index.number_of_shards: 5 8. 設置一個索引可被復制的數量,默認值為1: index.number_of_replicas: 1 當你想要禁用公布式時,你可以進行如下設置: index.number_of_shards: 1 index.number_of_replicas: 0 這兩個屬性的設置直接影響集群中索引和搜索操作的執行。假設你有足夠的機器來持有碎片和復制品,那么可以按如下規則設置這兩個值: 1) 擁有更多的碎片可以提升索引執行能力,並允許通過機器分發一個大型的索引; 2) 擁有更多的復制器能夠提升搜索執行能力以及集群能力。 對於一個索引來說,number_of_shards只能設置一次,而number_of_replicas可以使用索引更新設置API在任何時候被增加或者減少。 ElasticSearch關注加載均衡、遷移、從節點聚集結果等等。可以嘗試多種設計來完成這些功能。 可以連接http://localhost:9200/A/_status來檢測索引的狀態。 9. 配置文件所在的位置,即elasticsearch.yml和logging.yml所在的位置: path.conf: /path/to/conf 10. 分配給當前節點的索引數據所在的位置: path.data: /path/to/data 可以可選擇的包含一個以上的位置,使得數據在文件級別跨越位置,這樣在創建時就有更多的自由路徑,如: path.data: /path/to/data1,/path/to/data2 11. 臨時文件位置: path.work: /path/to/work 12. 日志文件所在位置: path.logs: /path/to/logs 13. 插件安裝位置: path.plugins: /path/to/plugins 14. 插件托管位置,若列表中的某一個插件未安裝,則節點無法啟動: plugin.mandatory: mapper-attachments,lang-groovy 15. JVM開始交換時,ElasticSearch表現並不好:你需要保障JVM不進行交換,可以將bootstrap.mlockall設置為true禁止交換: bootstrap.mlockall: true 請確保ES_MIN_MEM和ES_MAX_MEM的值是一樣的,並且能夠為ElasticSearch分配足夠的內在,並為系統操作保留足夠的內存。 16. 默認情況下,ElasticSearch使用0.0.0.0地址,並為http傳輸開啟9200-9300端口,為節點到節點的通信開啟9300-9400端口,也可以自行設置IP地址: network.bind_host: 192.168.0.1 17. publish_host設置其他節點連接此節點的地址,如果不設置的話,則自動獲取,publish_host的地址必須為真實地址: network.publish_host: 192.168.0.1 18. bind_host和publish_host可以一起設置: network.host: 192.168.0.1 19. 可以定制該節點與其他節點交互的端口: transport.tcp.port: 9300 20. 節點間交互時,可以設置是否壓縮,轉為為不壓縮: transport.tcp.compress: true 21. 可以為Http傳輸監聽定制端口: http.port: 9200 22. 設置內容的最大長度: http.max_content_length: 100mb 23. 禁止HTTP http.enabled: false 24. 網關允許在所有集群重啟后持有集群狀態,集群狀態的變更都會被保存下來,當第一次啟用集群時,可以從網關中讀取到狀態,默認網關類型(也是推薦的)是local: gateway.type: local 25. 允許在N個節點啟動后恢復過程: gateway.recover_after_nodes: 1 26. 設置初始化恢復過程的超時時間: gateway.recover_after_time: 5m 27. 設置該集群中可存在的節點上限: gateway.expected_nodes: 2 28. 設置一個節點的並發數量,有兩種情況,一種是在初始復蘇過程中: cluster.routing.allocation.node_initial_primaries_recoveries: 4 另一種是在添加、刪除節點及調整時: cluster.routing.allocation.node_concurrent_recoveries: 2 29. 設置復蘇時的吞吐量,默認情況下是無限的: indices.recovery.max_size_per_sec: 0 30. 設置從對等節點恢復片段時打開的流的數量上限: indices.recovery.concurrent_streams: 5 31. 設置一個集群中主節點的數量,當多於三個節點時,該值可在2-4之間: discovery.zen.minimum_master_nodes: 1 32. 設置ping其他節點時的超時時間,網絡比較慢時可將該值設大: discovery.zen.ping.timeout: 3s http://elasticsearch.org/guide/reference/modules/discovery/zen.html上有更多關於discovery的設置。 33. 禁止當前節點發現多個集群節點,默認值為true: discovery.zen.ping.multicast.enabled: false 34. 設置新節點被啟動時能夠發現的主節點列表(主要用於不同網段機器連接): discovery.zen.ping.unicast.hosts: ["host1", "host2:port", "host3[portX-portY]"] 35.設置是否可以通過正則或者_all刪除或者關閉索引 action.destructive_requires_name 默認false 允許 可設置true不允許
三、ElasticSearch安裝(Windows版本)
1. 安裝前准備
ElasticSearch是一個基於Lucene構建的開源,分布式,RESTful搜索引擎,而Lucene的開發語言是Java,所以電腦上面需要配置好jdk才能運行es數據庫。
2. 在官網下載安裝包
地址https://www.elastic.co/cn/downloads/elasticsearch
3. 解壓到本地,在cmd中運行elasticsearch.bat文件

4. 啟動測試
在瀏覽器中輸入:http://localhost:9200/

如果出現上圖所示內容,表示ElasticSearch啟動成功。中小型項目直接使用即可,大型項目還是要調一調參數的。
四、ElasticSearch操作客戶端工具--Kibana
1. 為什么要使用Kibana
為了方便我們去操作ES,如果不安裝去操作ES很麻煩,需要通過shell命令的方式。
2. 下載Kibana
地址:https://www.elastic.co/cn/downloads/kibana

3. 安裝並啟動
直接解壓即可,進入bin目錄下,本文為G:\myProgram\kibana\kibana-6.3.2-windows-x86_64\bin 的cmd,執行kibana

不需要配置任何參數,自動識別localhost,在瀏覽器中輸入 http://localhost:5601

點擊下面按鈕,進行ES命令操作



五、ElasticSearch的常用命令
1. CURD操作
1.1 GET _cat/health 查看集群的健康狀況

溫馨提示:green代表是健康的,yellow表示亞健康,red表示異常。
1.2 GET _all 查詢所有數據

1.3 PUT wxc_index 增加一個wxc_index的index庫

1.4 GET _cat/indices 查詢ES中所有的index

1.5 DELETE /wxc_index 刪除一個wxc_index的index庫

1.6 插入一條數據

溫馨提示:
(1)shop代表庫名,product代表表名,1代碼數據序號
(2)我們插入數據的時候,如果我們的語句中指明了index和type,如果ES里面不存在,默認幫我們自動創建
1.7 查詢數據
使用語法:GET /index/type/id

1.8 修改數據

1.9 刪除數據

1.10 現在查看所有數據,類似於全表掃描

took:耗費了6毫秒
shards:分片的情況
hits:獲取到的數據的情況
total:3 總的數據條數
max_score:1 所有數據里面打分最高的分數
_index:"ecommerce" index名稱
_type:"product" type的名稱
_id:"2" id號
_score:1 分數,這個分數越大越靠前出來,百度也是這樣。除非是花錢。否則匹配度越高越靠前

2. DSL語言
ES最主要是用來做搜索和分析的。所以DSL還是對於ES很重要的
案例:我們要進行全表掃描使用DSL語言,查詢所有的商品

溫馨提示:使用match_all 可以查詢到所有文檔,是沒有查詢條件下的默認語句。
案例:查詢所有名稱里面包含chenyi的商品,同時按價格進行降序排序
如上圖所示,name為dior chenyi的數據會在ES中進行倒排索引分詞的操作,這樣的數據也會被查詢出來。

match查詢是一個標准查詢,不管你需要全文本查詢還是精確查詢基本上都要用到它。
下面我們按照價格進行排序:因為不屬於查詢的范圍了。所以要寫一個 逗號

這樣我們的排序就完成了
案例:實現分頁查詢
條件:根據查詢結果(包含chenyi的商品),再進行每頁展示2個商品

案例:進行全表掃面,但返回指定字段的數據

案例:搜索名稱里面包含chenyi的,並且價格大於250元的商品
相當於 select * form product where name like %chenyi% and price >250;
因為有兩個查詢條件,我們就需要使用下面的查詢方式
如果需要多個查詢條件拼接在一起就需要使用bool
bool 過濾可以用來合並多個過濾條件查詢結果的布爾邏輯,它包含以下操作符:
must :: 多個查詢條件的完全匹配,相當於 and。
must_not :: 多個查詢條件的相反匹配,相當於 not。
should :: 至少有一個查詢條件匹配, 相當於 or。
這些參數可以分別繼承一個過濾條件或者一個過濾條件的數組

3. 聚合分析
案例:對商品名稱里面包含chenyi的,計算每個tag下商品的數量

案例:查詢商品名稱里面包含chenyi的數據,並且按照tag進行分組,計算每個分組下的平均價格

案例:查詢商品名稱里面包含chenyi的數據,並且按照tag進行分組,計算每個分組下的平均價格,按照平均價格進行降序排序

六、Java連接ElasticSearch進行數據操作
1. 創建maven項目




創建后項目結構如下:

2. pom.xml添加maven依賴
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.wxc</groupId> <artifactId>com-elasticsearch</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>transport</artifactId> <version>5.6.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.6.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.6.2</version> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.5</version> </dependency> </dependencies> </project>
3. 新建包,並創建測試類
新建com.wxc.es包

com.wxc.es包下新建測試類EsUtils.java
package com.wxc.es; import com.google.gson.JsonObject; import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse; import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.client.IndicesAdminClient; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.TermQueryBuilder; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; import org.elasticsearch.transport.client.PreBuiltTransportClient; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Date; import java.util.HashMap; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; public class EsUtils { public final static String HOST = "127.0.0.1"; public final static int PORT = 9300;//http請求的端口是9200,客戶端是9300 private TransportClient client = null; /** * 測試Elasticsearch客戶端連接 * @Title: test1 * @author sunt * @date 2017年11月22日 * @return void * @throws UnknownHostException */ @SuppressWarnings("resource") @Test public void test1() throws UnknownHostException { //創建客戶端 TransportClient client = new PreBuiltTransportClient(Settings.EMPTY).addTransportAddresses( new InetSocketTransportAddress(InetAddress.getByName(HOST),PORT)); System.out.println("Elasticsearch connect info:" + client.toString()); //關閉客戶端 client.close(); } /** * 獲取客戶端連接信息 * @Title: getConnect * @author sunt * @date 2017年11月23日 * @return void * @throws UnknownHostException */ @SuppressWarnings({ "resource", "unchecked" }) @Before public void getConnect() throws UnknownHostException { client = new PreBuiltTransportClient(Settings.EMPTY).addTransportAddresses( new InetSocketTransportAddress(InetAddress.getByName(HOST),PORT)); System.out.println("連接信息:" + client.toString()); } /** * 關閉連接 * @Title: closeConnect * @author sunt * @date 2017年11月23日 * @return void */ @After public void closeConnect() { if(null != client) { System.out.println("執行關閉連接操作..."); client.close(); } } /** * 創建索引庫 * @Title: addIndex1 * @author sunt * @date 2017年11月23日 * @return void * 需求:創建一個索引庫為:msg消息隊列,類型為:tweet,id為1 * 索引庫的名稱必須為小寫 * @throws IOException */ @Test public void addIndex1() throws IOException { IndexResponse response = client.prepareIndex("msg", "tweet", "1").setSource(XContentFactory.jsonBuilder() .startObject().field("userName", "張三") .field("sendDate", new Date()) .field("msg", "你好李四") .endObject()).get(); System.out.println("索引名稱:" + response.getIndex() + "\n類型:" + response.getType() + "\n文檔ID:" + response.getId() + "\n當前實例狀態:" + response.status()); } /** * 根據索引名稱,類別,文檔ID 刪除索引庫的數據 * @Title: deleteData * @author sunt * @date 2017年11月23日 * @return void */ @Test public void deleteData() { DeleteResponse deleteResponse = client.prepareDelete("msg", "tweet", "1").get(); System.out.println("deleteResponse索引名稱:" + deleteResponse.getIndex() + "\n deleteResponse類型:" + deleteResponse.getType() + "\n deleteResponse文檔ID:" + deleteResponse.getId() + "\n當前實例deleteResponse狀態:" + deleteResponse.status()); } /** * 更新索引庫數據 * @Title: updateData * @author sunt * @date 2017年11月23日 * @return void */ @Test public void updateData() { JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("userName", "王五"); jsonObject.addProperty("sendDate", "2008-08-08"); jsonObject.addProperty("msg","你好,張三,好久不見"); UpdateResponse updateResponse = client.prepareUpdate("msg", "tweet", "1") .setDoc(jsonObject.toString(),XContentType.JSON).get(); System.out.println("updateResponse索引名稱:" + updateResponse.getIndex() + "\n updateResponse類型:" + updateResponse.getType() + "\n updateResponse文檔ID:" + updateResponse.getId() + "\n當前實例updateResponse狀態:" + updateResponse.status()); } /** * 添加索引:傳入json字符串 * @Title: addIndex2 * @author sunt * @date 2017年11月23日 * @return void */ @Test public void addIndex2() { String jsonStr = "{" + "\"userName\":\"張三\"," + "\"sendDate\":\"2017-11-30\"," + "\"msg\":\"你好李四\"" + "}"; IndexResponse response = client.prepareIndex("weixin", "tweet").setSource(jsonStr,XContentType.JSON).get(); System.out.println("json索引名稱:" + response.getIndex() + "\njson類型:" + response.getType() + "\njson文檔ID:" + response.getId() + "\n當前實例json狀態:" + response.status()); } }
4. 運行項目
運行addIndex1方法

運行updateData方法

運行deleteData方法

運行addIndex2方法

七、項目源碼與參考資料下載
鏈接:https://pan.baidu.com/s/1pRyg_1OEQeeS18AF7X17TA
提取碼:2pqc