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