內(nèi)容概述
本文內(nèi)容主要集中在應(yīng)用層,通過下面幾個(gè)部分介紹當(dāng)前最流行的搜索工具:Elasticsearch,了解這些內(nèi)容后,可以快速開始使用它。
- 什么是
Elasticsearch,為什么要使用它? - 基礎(chǔ)概念:節(jié)點(diǎn),索引,類型映射和文檔
- 本地環(huán)境搭建,創(chuàng)建第一個(gè)
index - 常用
RESTful Api示例
什么是Elasticsearch,為什么要使用它?
Elasticsearch 是一個(gè)分布式、RESTful 風(fēng)格的搜索和數(shù)據(jù)分析引擎。
它基于Lunece實(shí)現(xiàn),使用java語言編寫。Lunece是一個(gè)優(yōu)秀的搜索引擎庫,但它使用起來非常復(fù)雜。
Elasticsearch通過對(duì) Lunece的封裝,隱藏了復(fù)雜性,提供了使用簡單的RESTful Api。
同時(shí)也實(shí)現(xiàn)了分布式集群特性,具有存儲(chǔ)數(shù)據(jù)大,查詢性能好,擴(kuò)展方便等特點(diǎn)。
為什么要使用它
在業(yè)務(wù)開發(fā)中,基于ES的特性,通常有下面這些場景需要使用它:
- 存儲(chǔ)大量數(shù)據(jù)。通過在使用mysql存儲(chǔ)的時(shí)候,數(shù)據(jù)的單位是
G。使用ES的時(shí)候,數(shù)據(jù)的單位是T。由此可以看出ES使用于大數(shù)據(jù)量的存儲(chǔ)場景,基于分布式特性,它也支持備份和容災(zāi),并且可以很容易水平擴(kuò)展容量。 - 分詞搜索引擎。ES具有強(qiáng)大的分詞能力,可以支持高性能的實(shí)時(shí)搜索。
- 高效數(shù)據(jù)分析。ES提供的聚合分析功能,可實(shí)現(xiàn)對(duì)保存的大量數(shù)據(jù)的近實(shí)時(shí)統(tǒng)計(jì)分析。
基礎(chǔ)概念簡介
要使用ES,需要了解幾個(gè)最基本的概念,節(jié)點(diǎn)(node),索引(index),類型映射(mapping)和文檔(doc)。
節(jié)點(diǎn)(node)
節(jié)點(diǎn)是組成ES集群的基本單位,每個(gè)節(jié)點(diǎn)是一個(gè)運(yùn)行的ES實(shí)例。每個(gè)物理機(jī)器上可以有多個(gè)節(jié)點(diǎn),使用不同的端口和節(jié)點(diǎn)名稱。
節(jié)點(diǎn)按主要功能可以分為三種:主節(jié)點(diǎn)(Master Node),協(xié)調(diào)節(jié)點(diǎn)(Coordianting Node)和數(shù)據(jù)節(jié)點(diǎn)(Data Node)。下面簡單介紹下:
- 主節(jié)點(diǎn):處理創(chuàng)建,刪除索引等請(qǐng)求,維護(hù)集群狀態(tài)信息。可以設(shè)置一個(gè)節(jié)點(diǎn)不承擔(dān)主節(jié)點(diǎn)角色
- 協(xié)調(diào)節(jié)點(diǎn):負(fù)責(zé)處理請(qǐng)求。默認(rèn)情況下,每個(gè)節(jié)點(diǎn)都可以是協(xié)調(diào)節(jié)點(diǎn)。
- 數(shù)據(jù)節(jié)點(diǎn):用來保存數(shù)據(jù)??梢栽O(shè)置一個(gè)節(jié)點(diǎn)不承擔(dān)數(shù)據(jù)節(jié)點(diǎn)角色
索引(index)
索引是ES中的邏輯概念,是文檔的容器。對(duì)ES的操作,基本都是對(duì)索引操作,一個(gè)ES集群中,可以創(chuàng)建多個(gè)索引。
索引定義了一組文檔的數(shù)據(jù)模型和處理方法。每個(gè)索引可以有多個(gè)主分片和副本分片,分別保存在不同的節(jié)點(diǎn)。
- 主分片的作用是對(duì)索引的擴(kuò)容,使一個(gè)索引的容量可以突破單機(jī)的限制。
- 副本分片是對(duì)數(shù)據(jù)的保護(hù),每個(gè)主分片對(duì)應(yīng)一個(gè)或多個(gè)副本分片,當(dāng)主分片所在節(jié)點(diǎn)宕機(jī)時(shí),副本分片會(huì)被提升為對(duì)應(yīng)的主分片使用。
- 一個(gè)主分片和它的副本分片,不會(huì)分配到同一個(gè)節(jié)點(diǎn)上。
- 一個(gè)索引的分片數(shù)在創(chuàng)建時(shí)指定,如果要修改需要重建索引,代價(jià)很高。
類型映射(mapping)
mapping定義了一個(gè)索引中,文檔保存的每個(gè)字段的數(shù)據(jù)類型。根據(jù)數(shù)據(jù)類型的不同,在添加文檔時(shí)對(duì)每個(gè)字段的處理也不同。
例如,對(duì)text類型的字段,會(huì)先使用分詞器分詞,生成倒排索引,用于之后的搜索。對(duì)keyword類型的字段,不會(huì)分詞,搜索時(shí)只能精確查找。
一個(gè)簡單的mapping示例如下:
{
"javalogs": { //索引名稱
"mappings": {
"properties": {
"log_content": { //text類型,分詞,用于之后的分詞索引
"type": "text"
},
"date": {//時(shí)間類型
"type": "date"
},
"log_level": { //keyword類型,不分詞
"type": "keyword"
},
"ip": {
"type": "keyword"
}
}
}
}
}
在6.x版本中,每個(gè)索引中還可以有多個(gè)type,區(qū)分不同的mapping。在7.x中,type被取消,每個(gè)索引只有一個(gè)type:_doc
文檔(doc)
文檔是
Elasticsearch中的最小單位,每個(gè)索引都是有數(shù)量眾多的文檔組成的。文檔中包含多個(gè)字段,每個(gè)字段的類型由
mapping定義。在一個(gè)索引中每個(gè)文檔都有一個(gè)唯一id,可以在添加時(shí)指定,也可以自動(dòng)生成。
下面通過一張圖來描述,節(jié)點(diǎn)(node),索引(index)和文檔(doc)之間的關(guān)系。

本地環(huán)境搭建,創(chuàng)建第一個(gè)index
一切知識(shí)都要通過實(shí)踐掌握,所以在了解基本的概念和邏輯后,下面就進(jìn)入實(shí)踐環(huán)節(jié)。
這里推薦使用docker來搭建本地開發(fā)環(huán)境,docker對(duì)應(yīng)windows和mac系統(tǒng)都有桌面版本,使用非常方便。因?yàn)榫W(wǎng)絡(luò)限制,直接使用docker官方倉庫拉取鏡像會(huì)很慢,所以在安裝完成后,需要在設(shè)置中將倉庫的地址替換為國內(nèi)源,這里推薦https://docker.mirrors.ustc.edu.cn,速度很快,設(shè)置如下:
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn"
]
}

下面我們使用docker安裝Elasticsearch和kibana鏡像,kibana是es官方配套的可視化分析工具,使用它的頁面dev tools可以很方便的通過api操作es。
因?yàn)橐瑫r(shí)部署兩個(gè)docker鏡像,這里推薦使用docker-composer,桌面版安裝完成后就帶有該命令,需要的配置如下:
services:
kibana:
image: kibana:7.2.0
container_name: kibana-simple
environment:
- TIMELION_ENABLED=true
ports:
- "5601:5601"
networks:
- mynetwork
elasticsearch:
image: elasticsearch:7.2.0
container_name: es-simple
environment:
- cluster.name=mytestes #這里就是ES集群的名稱
- node.name=es-simple #節(jié)點(diǎn)名稱
- bootstrap.memory_lock=true
- network.publish_host=elasticsearch #節(jié)點(diǎn)發(fā)布的網(wǎng)絡(luò)名稱
- discovery.seed_hosts=es-simple #設(shè)置集群中的主機(jī)地址
- cluster.initial_master_nodes=es-simple #手動(dòng)設(shè)置可以成為master的節(jié)點(diǎn)集合
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- esdata1:/usr/local/elasticsearch/simpledata
ports:
- 9200:9200
networks:
- mynetwork
volumes:
esdata1:
driver: local
networks:
mynetwork:
driver: bridge
創(chuàng)建一個(gè)名稱為docker-compose.yaml文件,復(fù)制下面的配置到文件中,然后再文件所在目錄執(zhí)行docker-compose up,之后會(huì)啟動(dòng)兩個(gè)docker實(shí)例,分別是elasticsearch和kibana。
在本地瀏覽器中,訪問http://127.0.0.1:5601/,可以看到kibana的界面如下:

創(chuàng)建好的kibana已經(jīng)默認(rèn)添加了Elasticsearch的配置,通過管理工具可以很方便的查看ES集群的狀態(tài),索引情況,刪除索引等。

下面通過dev tools創(chuàng)建索引,dev tools提供的命令提示很方便,并且可以把已寫好的請(qǐng)求保存在瀏覽器緩存中,非常適合用來學(xué)習(xí)Elasticsearch。

這里通過ES提供的RESTful Api創(chuàng)建了第一個(gè)索引, 并且設(shè)置了該索引中的mapping,ES的地址已經(jīng)設(shè)置過,這里可以不寫完整的域名,對(duì)應(yīng)的curl完整請(qǐng)求如下:
curl --location --request PUT 'http://127.0.0.1:9200/javalogs' \
--header 'Content-Type: application/json' \
--data-raw '{
"mappings": {
"properties": {
"log_content": {
"type": "text"
},
"date": {
"type": "date"
},
"log_level": {
"type": "keyword"
},
"ip": {
"type": "keyword"
}
}
}
}'
常用RESTful Api示例
下面介紹下Elasticsearch中常用的api,這些例子都是直接在kibana的dev tools中運(yùn)行的,如果想用curl訪問,可參考前一節(jié)中的轉(zhuǎn)換例子。
新增文檔
//自動(dòng)生成_id
POST javalogs/_doc
{
"log_content" : "get user_id 123456",
"date" : "2020-04-15T11:09:08",
"log_level": "info",
"ip": "10.223.32.67"
}
//指定_id
POST javalogs/_doc/111
{
"log_content" : "api response in 55ms",
"date" : "2020-04-15T11:09:07",
"log_level": "info",
"ip": "10.223.32.67"
}
查詢文檔-不分詞類型
ES在文檔查詢時(shí),對(duì)于不分詞的查詢,直接按值查詢即可,例如下面這樣:
//不分詞類型查詢
POST javalogs/_search
{
"query": {
"match": {
"ip": "10.223.32.67"
}
}
}
查詢文檔-分詞類型
這里主要說下分詞類型的查詢,對(duì)于分析類型的field在查詢時(shí),也會(huì)默認(rèn)把查詢的語句分詞。假設(shè)有兩個(gè)文檔如下:
//文檔1
{
"log_content" : "call aaa service error",
"date" : "2020-04-15T11:09:07",
"log_level": "error",
"ip": "10.223.32.67"
}
//文檔2
{
"log_content" : "call bbb service error",
"date" : "2020-04-15T11:09:08",
"log_level": "error",
"ip": "10.223.32.67"
}
當(dāng)搜索條件為call aaa service時(shí),實(shí)際上會(huì)把兩個(gè)文檔都搜索出來。
這是因?yàn)樵谒阉鲿r(shí),條件call aaa service會(huì)被分詞為call,aaa和service,所有包含這三個(gè)詞的文檔都會(huì)被搜索出來,例如下面:
//普通搜索,兩個(gè)文檔都會(huì)返回
POST javalogs/_search
{
"query": {
"match": {
"log_content": "call aaa service"
}
}
}
那如果想要只搜索包含call aaa service的文檔,應(yīng)該如何做呢?
按照上面的分析,需要同時(shí)包含這三個(gè)詞,并且按照給定的順序,才返回對(duì)應(yīng)的文檔,那么這個(gè)可以使用match_phrase實(shí)現(xiàn),示例如下:
//文檔必須同時(shí)包含三個(gè)詞,并且順序與搜索條件一致才會(huì)返回。這里只會(huì)返回-文檔1
POST javalogs/_search
{
"profile": "true",
"query": {
"match_phrase": {
"log_content": "call aaa service"
}
}
}
那如果條件是包含call,aaa和service,但是不一定是連著的,該如何搜索呢?可以使用operator操作符實(shí)現(xiàn)。
例如有第三個(gè)文檔如下:
//文檔3
{
"log_content" : "call inner aaa service error",
"date" : "2020-04-15T11:09:08",
"log_level": "error",
"ip": "10.223.32.67"
}
要想把文檔1和文檔2都搜索出來,查詢的示例如下:
//文檔中同時(shí)包含call,aaa和service就會(huì)返回,不按順序。會(huì)返回-文檔1和文檔2
POST javalogs/_search
{
"query": {
"match": {
"log_content":
{
"query": "call aaa service",
"operator": "and"
}
}
}
}
上面就是對(duì)Elasticsearch的簡單介紹和實(shí)戰(zhàn)操作示例,希望能幫助大家快速入門使用ES。
以上內(nèi)容屬個(gè)人學(xué)習(xí)總結(jié),如有不當(dāng)之處,歡迎在評(píng)論中指正