Elasticsearch 和 Solr 比較
- 當單純的對已有數(shù)據進行搜索時,Solr更快。
- 當實時建立索引時,Solr會產生io阻塞,查詢性能較差,Elasticsearch具有明顯的優(yōu)勢。
- 隨著數(shù)據量的增加,Solr的搜索效率會變得更低,而Elasticsearch卻沒有明顯的變化。
- 此時如果轉變我們的搜索基礎設施后,從Solr到Elasticsearch,我們看見一個即時,50倍提高搜索性能。
ElasticSearch vs Solr 總結
- es基本是開箱即用(解壓就可以用!),非常簡單,Solr安裝略微復雜一丟丟!
- Solr 利用 Zookeeper 進行分布式管理,而Elasticsearch 自身帶有分布式協(xié)調管理功能。
- Solr 支持更多格式的數(shù)據,比如JSON、XML、CSV,而Elasticsearch 僅支持json文件格式。
- Solr 官方提供的功能更多,而Elasticsearch本身更注重核心功能,高級功能多由第三方插件提供,例如圖形化界面需要kibana友好支撐
- Solr 查詢快,但更新索引時慢(即插入刪除慢),用于電商等查詢多的應用;
- ES 建立索引快(即查詢慢),==即實時性查詢快==,用于facebook新浪等搜索。
- Solr 是傳統(tǒng)搜索應用的有力解決方案,但 Elasticsearch 更適用于新興的實時搜索應用。
- Solr比較成熟,有一個更大,更成熟的用戶、開發(fā)和貢獻者社區(qū),而 Elasticsearch 相對開發(fā)維護者較少,更新太快,學習使用成本較高。(es 大趨勢?。?/li>
ElasticSearch 安裝
聲明:JDK1.8,最低要求!ElasticSearch 客戶端,界面工具!
Java開發(fā),ElasticSearch 的版本和我們之后對應的Java 的核心jar包!版本對應!JDK 環(huán)境是正常的。
下載


下載地址:https://www.elastic.co/cn/downloads/elasticsearch
官網下載巨慢,翻墻,或從網上找已下載好的資源
==這里在Window下學習!==
ELK三劍客,解壓即用!
window 下安裝!
1.解壓就可以使用了

2.熟悉目錄
bin 啟動文件
config 配置文件
log4j2 日志配置文件
jvm.options java虛擬機相關的配置
elasticsearch.yml elasticsearch的配置文件,默認 9200 端口,跨域問題!
lib 相關jar包
logs 日志
modules 功能模塊
plugins 插件 ik分詞器
3.啟動,訪問 9200(注意,第一次在本機上嘗試啟動報錯,需要在yaml文件中加一行配置:xpack.ml.enabled: false)

4.訪問測試!

安裝可視化界面 es head的插件
此處必須要有 VUE 基礎,并且需要有 node.js 的基本環(huán)境已安裝
1.下載地址:https://github.com/mobz/elasticsearch-head/
2.啟動
npm install
npm run start
3.連接測試發(fā)現(xiàn),存在跨域問題,配置es的yaml配置文件
# 解決跨域
http.cors.enabled: true
http.cors.allow-origin: "*"
4.重啟es服務器,然后再次連接

我們初學時,就把es當做一個數(shù)據庫~(可以建立索引(庫),文檔(庫中的數(shù)據?。?/p>
這個head我們就把它當做數(shù)據展示工具,我們后面所有的查詢,Kibana
了解ELK
ELK是Elasticsearch、Logstash、Kibana三大開源框架首字母大寫簡稱。市面上也被稱為Elastic Stack。其中Elasticsearch 是一個基于Luncene、分布式、通過Restful方式進行交互的近實時搜索平臺框架。像類似百度,谷歌這種大數(shù)據全文搜索引擎的場景都可以使用Elasticsearch作為底層支持框架,可見Elasticsearch提供的搜索能力確實強大,市面上很多時候我們簡稱Elasticsearch為es。Logstash是ELK的中央數(shù)據流引擎,用于從不同目標(文件/數(shù)據存儲/MQ)收集不同格式數(shù)據,經過過濾后支持輸出到不同目的地(文件/MQ/redis/elasticsearch/kafka等)。Kibana可以將elasticsearch的數(shù)據通過友好的頁面展示出來,提供實時分析功能。
收集清洗數(shù)據 -- 》 搜索,存儲 --》 展示Kibana
市面上很多開發(fā)只要提到ELK能夠一致說出他是一個日志分析架構技術總稱,但實際上ELK不僅僅適用于日志分析,它還可以支持其它任何數(shù)據分析和收集的場景,日志分析和收集只是更具有代表性。并非唯一性。

安裝 Kibana
Kibana 是一個針對Elasticsearch的開源分析及可視化平臺,用來搜索、查看交互存儲在Elasticsearch索引中的數(shù)據。使用Kibana,可以通過各種圖表進行高級數(shù)據分析及展示。Kibana讓海量數(shù)據更容易理解。它操作簡單,基于瀏覽器的用戶界面可以快速創(chuàng)建儀表板(dashboard)實時顯示Elasticsearch查詢動態(tài)。設置Kibana非常簡單。無需編碼或者額外的基礎架構,幾分鐘內就可以完成Kibana安裝并啟動Elasticsearch索引監(jiān)測。
官網:https://www.elastic.co/cn/kibana
Kibana版本要和es版本一致!
下載完畢后,解壓需要一些時間。
好處:ELK基本上都是拆箱即用!
啟動測試
1.解壓后的目錄

2.啟動

3.訪問測試

4.開發(fā)工具(Post,curl,head,谷歌瀏覽器插件測試)

我們之后的所有操作都在這里進行編寫
5.漢化,修改Kibana的配置文件yaml即可,修改完成后重啟項目

ES 核心概念
1.索引
2.字段類型(mapping)
3.文檔(documents)
概述
在前面的學習中,我們已經掌握了es是什么,同時也把es的服務已經安裝啟動了,那么es是如何去存儲數(shù)據,數(shù)據結構是什么,又是如何實現(xiàn)搜索的呢?我們先來聊聊Elasticsearch的相關概念吧!
==集群,節(jié)點,索引,類型,文檔,分片,映射是什么?==
elasticsearch 是面向文檔,關系型數(shù)據庫 和 elasticsearch 客觀的對比!
| Relational DB | Elasticsearch |
|---|---|
| 數(shù)據庫(database) | 索引(indices) |
| 表(tables) | types |
| 行(rows) | documents |
| 字段(columns) | fields |
elasticsearch(集群)中可以包含多個索引(數(shù)據庫),每個索引中可以包含多個類型(表),每個類型下又包含多個文檔(行),每個文檔中又包含多個字段(列)。
物理設計:
elasticsearch 在后臺把每個索引劃分成多個分片,每分分片可以在集群中的不同服務器間遷移
一個人就是一個集群!默認的集群名字就是 elasticsearch
IK分詞器插件
什么是IK分詞器?
分詞:即把一段中文或別的劃分成一個個的關鍵字,我們在搜索時候會把自己的信息進行分詞,會把數(shù)據庫中或者索引庫中的數(shù)據進行分詞,然后進行一個匹配操作,默認的中文分詞是將每個字看成一個詞,比如“大程子”會被分為“大”,“程”,“子”,這顯然是不符合要求的,所以我們需要安裝中文分詞器ik來解決這個問題。
如果要使用中文,建議選擇使用ik分詞器!
IK提供了兩個分詞算法:ik_smart 和 ik_max_word , 其中 ik_smart 為最少切分,ik_max_word 為最細粒度劃分,后面進行測試。
安裝
1.https://github.com/medcl/elasticsearch-analysis-ik
2.下載完畢之后,放入到我們的elasticsearch插件中即可

3.重啟觀察ES,可以看到ik分詞器被加載了!

4.elasticsearch-plugin 可以通過這個命令來查看加載進來的插件

5.使用kibana測試!
查看不同的分詞器效果
ik_smart 為最少切分

ik_max_word 為最細粒度劃分,窮盡詞庫的可能,字典

我們輸入“超級喜歡大程子學Java”

發(fā)現(xiàn)問題:大程子被拆開了!
這種自己需要的詞,需要自己加到我們的分詞器的字典中!
ik 分詞器增加自己的配置

重啟es,看細節(jié)

再次測試以下大程子,看下效果

以后的話,我們需要自己配置分詞就在自已定義的dic文件中進行配置即可!
Rest風格說明
一種軟件架構風格,而不是標準,只是提供了一組設計原則和約束條件。它主要用于客戶端和服務器交互類的軟件?;谶@個風格設計的軟件可以更簡潔,更有層次,更易于實現(xiàn)緩存等機制。
基于Rest命令說明:
| method | url地址 | 描述 |
|---|---|---|
| PUT | localhost:9200/索引名稱/類型名稱/文檔id | 創(chuàng)建文檔(指定文檔id) |
| POST | localhost:9200/索引名稱/類型名稱 | 創(chuàng)建文檔(隨機文檔id) |
| POST | localhost:9200/索引名稱/類型名稱/文檔id/_update | 修改文檔 |
| DELETE | localhost:9200/索引名稱/類型名稱/文檔id | 刪除文檔 |
| GET | localhost:9200/索引名稱/類型名稱/文檔id | 查詢文檔通過文檔id |
| POST | localhost:9200/索引名稱/類型名稱/_search | 查詢所有數(shù)據 |
關于索引的基本操作
1.創(chuàng)建一個索引
put /索引名/~類型名~/文檔id
{請求體}

完成了自動增加索引!數(shù)據也成功的添加了,這就是為什么在初期可以把它當做數(shù)據庫學習的原因!

3.那么name這個字段用不用指定類型呢。畢竟我們關系型數(shù)據庫 是需要指定類型的
-
字符串類型
text、keyword
-
數(shù)值類型
long、integer、short、byte、double、float、half_float、scaled_float
-
日期類型
date
-
布爾值類型
boolean
-
二進制類型
binary
等等……
4.指定字段的類型

獲得這個規(guī)則,可以通過GET請求獲取具體的信息!

5.查看默認的信息


如果自己的文檔字段沒有指定,那么es就會給我們默認配置字段類型!
擴展:通過命令 elasticsearch 索引情況!通過 get _cat/ 可以獲得es的當前的很多信息!

修改 提交還是使用PUT 即可! 然后覆蓋! 最新辦法
曾經

現(xiàn)在的方法

刪除索引
通過 DELETE 命令實現(xiàn)刪除、根據你的請求來判斷是刪除索引還是刪除文檔記錄!
使用 RESTFULL 風格是我們ES推薦大家使用的!
關于文檔的基本操作(es的重點)
基本操作
1.添加數(shù)據
PUT /wangcp/user/3
{
"name":"李四",
"age":30,
"desc":"emm,不知道如何形容",
"tags":["靚女","旅游","唱歌"]
}

2.查詢獲取數(shù)據 GET

3.更新數(shù)據 PUT

- Post _update,推薦使用這種更新方式!

簡單的搜索
GET wangcp/user/1
簡單的條件查詢,可以根據默認的映射規(guī)則,產生基本的查詢!


復雜操作搜索 select(排序,分頁,高亮,模糊查詢,精準查詢!)


輸出結果過濾,不想要那么多,select name,desc

我們之后使用Java操作es,所有的方法和對象就是這里面的key!
排序

分頁查詢

數(shù)據索引下標還是從0開始的,和學的所有數(shù)據結構還是一樣的。
/search/{current}/{pagesize}
布爾值查詢
must(and),所有的條件都要符合 where id=1 and name=xxx

should(or),所有的條件都要符合 where id=1 or name=xxx

must_not( not )

過濾器 filter

- gt 大于
- gte 大于等于
- lt 小于
- lte 小于等于

匹配多個條件

精確查詢
term 查詢時直接通過倒排索引指定的詞條進行精確查找的!
關于分詞:
term:直接查詢精確的
match:會使用分詞器解析?。ㄏ确治鑫臋n,然后在通過分析的文檔進行查詢!)
兩個類型 text keyword



多個值匹配的精確查詢

高亮查詢


這些其實 MySQL 也可以做,只是 MySQL 效率較低
- 匹配
- 按照條件匹配
- 精確匹配
- 區(qū)間范圍匹配
- 匹配字段過濾
- 多條件查詢
- 高亮查詢
- 倒排索引
集成SpringBoot
找官方文檔



1.找到原生的依賴
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.10.1</version>
</dependency>
2.找對象

3.分析這個類中的方法
配置基本的項目
==問題:一定保證 我們導入導入的依賴和我們的es版本一致==


源碼中提供的對象

雖然這里導入3個類,靜態(tài)內部類,核心類就一個。
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.elasticsearch.rest;
import java.time.Duration;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Elasticsearch rest client infrastructure configurations.
*
* @author Brian Clozel
* @author Stephane Nicoll
*/
class RestClientConfigurations {
@Configuration(proxyBeanMethods = false)
static class RestClientBuilderConfiguration {
// RestClientBuilder
@Bean
@ConditionalOnMissingBean
RestClientBuilder elasticsearchRestClientBuilder(RestClientProperties properties,
ObjectProvider<RestClientBuilderCustomizer> builderCustomizers) {
HttpHost[] hosts = properties.getUris().stream().map(HttpHost::create).toArray(HttpHost[]::new);
RestClientBuilder builder = RestClient.builder(hosts);
PropertyMapper map = PropertyMapper.get();
map.from(properties::getUsername).whenHasText().to((username) -> {
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
Credentials credentials = new UsernamePasswordCredentials(properties.getUsername(),
properties.getPassword());
credentialsProvider.setCredentials(AuthScope.ANY, credentials);
builder.setHttpClientConfigCallback(
(httpClientBuilder) -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));
});
builder.setRequestConfigCallback((requestConfigBuilder) -> {
map.from(properties::getConnectionTimeout).whenNonNull().asInt(Duration::toMillis)
.to(requestConfigBuilder::setConnectTimeout);
map.from(properties::getReadTimeout).whenNonNull().asInt(Duration::toMillis)
.to(requestConfigBuilder::setSocketTimeout);
return requestConfigBuilder;
});
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder;
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestHighLevelClient.class)
static class RestHighLevelClientConfiguration {
// RestHighLevelClient 高級客戶端,也是我們這里要講的,后面項目要用到的客戶端
@Bean
@ConditionalOnMissingBean
RestHighLevelClient elasticsearchRestHighLevelClient(RestClientBuilder restClientBuilder) {
return new RestHighLevelClient(restClientBuilder);
}
// RestClient 普通的客戶端
@Bean
@ConditionalOnMissingBean
RestClient elasticsearchRestClient(RestClientBuilder builder,
ObjectProvider<RestHighLevelClient> restHighLevelClient) {
RestHighLevelClient client = restHighLevelClient.getIfUnique();
if (client != null) {
return client.getLowLevelClient();
}
return builder.build();
}
}
@Configuration(proxyBeanMethods = false)
static class RestClientFallbackConfiguration {
@Bean
@ConditionalOnMissingBean
RestClient elasticsearchRestClient(RestClientBuilder builder) {
return builder.build();
}
}
}
具體的API測試!
1.創(chuàng)建索引
@Test
void testCreateIndex() throws IOException {
// 1.創(chuàng)建索引請求 相當于 PUT wang_index
CreateIndexRequest request = new CreateIndexRequest("wang_index");
// 2.客戶端執(zhí)行請求
CreateIndexResponse createIndexResponse =
client.indices().create(request, RequestOptions.DEFAULT);
System.out.println(createIndexResponse);
}
2.判斷索引是否存在
@Test
void textExistIndex() throws IOException {
GetIndexRequest request = new GetIndexRequest("wang_index");
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
System.out.println(exists);
}
3.刪除索引
@Test
void textDeleteIndex() throws IOException {
DeleteIndexRequest request = new DeleteIndexRequest("wang_index");
AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
System.out.println(delete.isAcknowledged());
}
4.創(chuàng)建文檔
// 測試添加文檔
@Test
void testAddDocument() throws IOException {
//創(chuàng)建對象
User user = new User("大程子", 3);
//創(chuàng)建請求
IndexRequest request = new IndexRequest("wang_index");
// 規(guī)則 put /wang_index/_doc/1
request.id("1");
request.timeout(TimeValue.timeValueSeconds(1));
// 將我們的數(shù)據放入請求
IndexRequest source = request.source(JSON.toJSONString(user), XContentType.JSON);
// 客戶端發(fā)送請求,獲取響應的結果
IndexResponse indexResponse = client.index(source, RequestOptions.DEFAULT);
System.out.println(indexResponse);
System.out.println(indexResponse.status()); //對應我們命令返回的狀態(tài) CREATED
}
5.添加文檔
@Test
void testAddDocument() throws IOException {
//創(chuàng)建對象
User user = new User("大程子", 3);
//創(chuàng)建請求
IndexRequest request = new IndexRequest("wang_index");
// 規(guī)則 put /wang_index/_doc/1
request.id("1");
request.timeout(TimeValue.timeValueSeconds(1));
// 將我們的數(shù)據放入請求
IndexRequest source = request.source(JSON.toJSONString(user), XContentType.JSON);
// 客戶端發(fā)送請求,獲取響應的結果
IndexResponse indexResponse = client.index(source, RequestOptions.DEFAULT);
System.out.println(indexResponse);
System.out.println(indexResponse.status()); //對應我們命令返回的狀態(tài) CREATED
}
6.獲取文檔判斷是否存在
// 獲取文檔判斷是否存在
@Test
void testIsExists() throws IOException {
GetRequest getRequest = new GetRequest("wang_index", "1");
// 不獲取返回的 _source 的上下文了
getRequest.fetchSourceContext(new FetchSourceContext(false));
getRequest.storedFields("_none_");
boolean exists = client.exists(getRequest, RequestOptions.DEFAULT);
System.out.println(exists);
}
7.獲得文檔信息
// 獲得文檔的信息
@Test
void testGetDocument() throws IOException {
GetRequest getRequest = new GetRequest("wang_index", "1");
GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
System.out.println(getResponse.getSourceAsString()); // 打印文檔內容
System.out.println(getResponse);//獲得的全部內容和使用命令是一致的
}
8.更新文檔信息
// 更新文檔的信息
@Test
void testUpdateDocument() throws IOException {
UpdateRequest updateRequest = new UpdateRequest("wang_index", "1");
updateRequest.timeout("1s");
User user = new User("大程子的技術成長路", 18);
updateRequest.doc(JSON.toJSONString(user),XContentType.JSON);
UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);
System.out.println(updateResponse.status());
}
9.刪除文檔信息
// 刪除文檔信息
@Test
void testDeleteDocument() throws IOException {
DeleteRequest request = new DeleteRequest("wang_index", "1");
request.timeout("1s");
DeleteResponse delete = client.delete(request, RequestOptions.DEFAULT);
System.out.println(delete.status());
}
10.批量插入數(shù)據
// 特殊的,真實項目一般都會批量插入數(shù)據
@Test
void testBulkRequest() throws IOException {
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("10s");
ArrayList<User> userList = new ArrayList<>();
userList.add(new User("wangcp1",3));
userList.add(new User("wangcp3",6));
userList.add(new User("wangcp2",9));
userList.add(new User("wangcp4",12));
userList.add(new User("wangcp5",15));
userList.add(new User("wangcp6",18));
userList.add(new User("dachengzi1",3));
userList.add(new User("dachengzi2",6));
userList.add(new User("dachengzi3",9));
for (int i = 0; i < userList.size(); i++) {
bulkRequest.add(
//批量更新和批量刪除,就在這里修改對應的請求就可以了
new IndexRequest("wang_index")
.id("" + (i+1))
.source(JSON.toJSONString(userList.get(i)),XContentType.JSON));
}
BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(bulkResponse.hasFailures());// 是否失敗,返回 false 代表成功!
}
11.搜索查詢
// 查詢
// SearchRequest 搜索請求
// SearchSourceBuilder 條件構造
// HighlightBuilder 高亮構建
// TermQueryBuilder 構建精確查詢
@Test
void testSearch() throws IOException {
SearchRequest searchRequest = new SearchRequest("wang_index");
//構建搜索條件
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.highlighter();
// 查詢條件,我們可以使用 QueryBuilders 工具來實現(xiàn)
// QueryBuilders.termQuery 精確查找
// QueryBuilders.matchAllQuery() 匹配所有
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name","wangcp1");
// MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
sourceBuilder.query(termQueryBuilder);
// 設置查詢最大時間
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(JSON.toJSONString(searchResponse.getHits()));
System.out.println("=======================================");
for (SearchHit documentFields : searchResponse.getHits().getHits()) {
System.out.println(documentFields.getSourceAsMap());
}
}
以上為日常學習ElasticSearch對應的記錄,存在的不足或問題希望大家留言指出!共學共勉。