摘要:Elasticsearch
《Elasticsearch搜索引擎構(gòu)建入門與實(shí)戰(zhàn)》第一章讀書筆記
Elasticsearch特色優(yōu)勢(shì)
-
實(shí)時(shí)性好:相比于Lucene,新增的數(shù)據(jù)數(shù)秒甚至1秒(緩存)內(nèi)可以搜索到 -
分布式架構(gòu)設(shè)計(jì):可以拓展到多臺(tái)機(jī)器進(jìn)行搜索 -
提供REST風(fēng)格API,相比于Lucene需要Java開(kāi)發(fā),ES支持HTTP請(qǐng)求來(lái)進(jìn)行查詢 -
提供了聚合查詢:ES可以對(duì)索引中的數(shù)據(jù)做聚合統(tǒng)計(jì)分析
Elasticsearch基本概念
-
索引:要對(duì)進(jìn)行數(shù)據(jù)存儲(chǔ)和查詢操作需要先建立索引,ES中的索引相當(dāng)于傳統(tǒng)數(shù)據(jù)庫(kù)的庫(kù) -
文檔:相當(dāng)于傳統(tǒng)數(shù)據(jù)庫(kù)的一條記錄,就是ES中一個(gè)文檔,一個(gè)文檔可以包含一個(gè)或多個(gè)字段,每個(gè)字段可以有各種類型,文檔的初始版本是1,每次寫操作會(huì)自動(dòng)版本號(hào)+1,查詢時(shí)ES返回最大版本號(hào)的文檔 -
字段:常見(jiàn)的類型包括字符串,文本,數(shù)值,還有ES提供其他類型,比如數(shù)組,地理經(jīng)緯,IP地址等,不同的數(shù)據(jù)類型ES支持不同的所有功能,比如文本可以基于分詞搜索,地理經(jīng)緯可以所有某點(diǎn)附近的文檔 -
映射:映射就是定義的數(shù)據(jù)結(jié)構(gòu)schema,一旦設(shè)定之后不能更改,ES也提供了自動(dòng)映射的功能,如果要寫入的數(shù)據(jù)沒(méi)有給定類型ES會(huì)自動(dòng)推斷 -
集群和節(jié)點(diǎn):多臺(tái)機(jī)器一起協(xié)作作為集群,ES集群的節(jié)點(diǎn)數(shù)不受限制,一個(gè)節(jié)點(diǎn)就對(duì)應(yīng)一臺(tái)機(jī)器 -
分片:ES會(huì)對(duì)數(shù)據(jù)進(jìn)行切分存儲(chǔ)到多臺(tái)計(jì)算機(jī)中,均勻分?jǐn)倖闻_(tái)機(jī)器的存儲(chǔ)壓力,ES默認(rèn)一個(gè)索引5個(gè)分片,分片一定設(shè)置是不可以修改的,只能新建新索引解決,一個(gè)節(jié)點(diǎn)可以被分配多個(gè)主分片。每個(gè)分片可以設(shè)置副分片,當(dāng)主分片故障離線時(shí)副分片會(huì)充當(dāng)主分片繼續(xù)提供服務(wù),保證了高可用 -
副分片:在一個(gè)索引中主分片的副分片個(gè)數(shù)沒(méi)有限制,默認(rèn)ES不會(huì)啟用副分片。一個(gè)主分片的所有副分片都存儲(chǔ)在不同的機(jī)器上,保證一臺(tái)機(jī)器宕機(jī)的情況下,其他機(jī)器的副分片可以提供服務(wù)。因此如果只設(shè)置了一個(gè)ES節(jié)點(diǎn),且啟動(dòng)了至少一個(gè)副分片,實(shí)際上ES只會(huì)分配一個(gè)主分片,不會(huì)分配副分片

如上圖,三個(gè)主分片P0,P1,P2,各自的副分片R0,R1,R2都分別在另外的機(jī)器上,一個(gè)三個(gè)節(jié)點(diǎn)存儲(chǔ)分別分配了一個(gè)主分片,主分片的備份數(shù)量是1
-
DSL:領(lǐng)域特定語(yǔ)言,是用來(lái)定義查詢的,HTML,CSS,SQL都屬于DSL,ES中DSL使用JSON表達(dá),查詢的返回?cái)?shù)據(jù)也是JSON格式
Elasticsearch和傳統(tǒng)關(guān)系型數(shù)據(jù)庫(kù)對(duì)比
-
索引方式不同:關(guān)系型數(shù)據(jù)庫(kù)大多是B-Tree索引,ES是倒排索引 -
事務(wù)支持:ES不支持事務(wù)支持,ES使用樂(lè)觀鎖,不阻塞數(shù)據(jù)的更新操作,每次更新采用增加版本號(hào)的方式,導(dǎo)致某些更新操作可能失效,數(shù)據(jù)未更新成功。
悲觀鎖和樂(lè)觀鎖
- 悲觀鎖是基于一種悲觀的態(tài)度類來(lái)防止一切數(shù)據(jù)沖突,它是以一種預(yù)防的姿態(tài)在修改數(shù)據(jù)之前把數(shù)據(jù)鎖住,然后再對(duì)數(shù)據(jù)進(jìn)行讀寫,是一種串行的方式,只要開(kāi)始執(zhí)行就會(huì)鎖表,一般數(shù)據(jù)庫(kù)本身鎖的機(jī)制都是基于悲觀鎖的機(jī)制實(shí)現(xiàn)的
- 樂(lè)觀鎖是對(duì)于數(shù)據(jù)沖突保持一種樂(lè)觀態(tài)度,操作數(shù)據(jù)時(shí)不會(huì)對(duì)操作的數(shù)據(jù)進(jìn)行加鎖(這使得多個(gè)任務(wù)可以并行的對(duì)數(shù)據(jù)進(jìn)行操作),只有到數(shù)據(jù)提交的時(shí)候才通過(guò)一種機(jī)制來(lái)驗(yàn)證數(shù)據(jù)是否存在沖突(一般實(shí)現(xiàn)方式是通過(guò)加版本號(hào)然后進(jìn)行版本號(hào)的對(duì)比方式實(shí)現(xiàn)),樂(lè)觀鎖并不會(huì)真的加鎖,而是通過(guò)一些狀態(tài)的檢驗(yàn)達(dá)到操作互斥的效果
例如有以下數(shù)據(jù)采用樂(lè)觀鎖的方式,用戶A,B分別查詢Name=zhangsan的這條記錄,并進(jìn)行修改再次寫入數(shù)據(jù)庫(kù)
因?yàn)椴捎脴?lè)觀鎖,因此A,B都可以并行地拿到數(shù)據(jù),用戶A拿到之后將Name修改為lisi,在提交操作時(shí),數(shù)據(jù)庫(kù)會(huì)把之前查詢到的version與現(xiàn)在的數(shù)據(jù)的version進(jìn)行比較,版本相同則可以提交,版本不同則視為數(shù)據(jù)過(guò)期,過(guò)期則提交失敗,如以下SQL更新語(yǔ)句
update A set Name=lisi,version=version+1 where ID=#{id} and version=#{version}
更新成功后數(shù)據(jù)庫(kù)該記錄變?yōu)?br>
此時(shí)用戶B將Name改為wangwu,再提交時(shí)會(huì)失敗,因?yàn)樽陨韛erson=1,而數(shù)據(jù)庫(kù)內(nèi)的version=2,數(shù)據(jù)過(guò)期
查詢語(yǔ)句不同:關(guān)系型數(shù)據(jù)庫(kù)采用SQL,原因是查詢比較簡(jiǎn)單直接,ES采用DSL來(lái)完成比較復(fù)雜的查詢所有需求查詢速度:如果字段少,數(shù)據(jù)量不打,關(guān)系型數(shù)據(jù)庫(kù)查詢速度很快,但是單表有上百字段和幾十億條數(shù)據(jù)查詢速度就會(huì)變慢,隨著數(shù)據(jù)量的增長(zhǎng)速度越來(lái)越慢,ES可以支持對(duì)全字段做索引,單個(gè)索引存儲(chǔ)上百字段或幾十億條數(shù)據(jù)查詢速度都不會(huì)變慢數(shù)據(jù)的實(shí)時(shí)性:關(guān)系型數(shù)據(jù)庫(kù)是實(shí)時(shí)的,插入之后立馬可以查到,ES內(nèi)存中的數(shù)據(jù)先寫入緩存,默認(rèn)1s之后統(tǒng)一刷入磁盤,才能被ES查到,因此ES是準(zhǔn)實(shí)時(shí)的
Elasticsearch架構(gòu)原理
(1)節(jié)點(diǎn)的職責(zé)
節(jié)點(diǎn)按照職責(zé)可以分為master節(jié)點(diǎn),數(shù)據(jù)節(jié)點(diǎn),協(xié)調(diào)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)的角色可以單獨(dú)配置,默認(rèn)每臺(tái)機(jī)器都可以擔(dān)任這三種角色。
-
master節(jié)點(diǎn):維護(hù)ES集群工作,如創(chuàng)建刪除索引,節(jié)點(diǎn)上線下線,健康狀態(tài)監(jiān)測(cè)等,master通過(guò)選舉算法產(chǎn)生,在候選的機(jī)器中只能有一個(gè)master節(jié)點(diǎn),可以配置node.master為true將當(dāng)前節(jié)點(diǎn)作為master節(jié)點(diǎn)的候選 -
數(shù)據(jù)節(jié)點(diǎn):負(fù)責(zé)數(shù)據(jù)保存,修改,刪除,查詢,數(shù)據(jù)節(jié)點(diǎn)的工作是調(diào)用Lucene庫(kù)進(jìn)行索引操作,對(duì)內(nèi)存和I/O消耗比較大 -
協(xié)調(diào)節(jié)點(diǎn):負(fù)責(zé)對(duì)接客戶端請(qǐng)求,默認(rèn)情況下協(xié)調(diào)節(jié)點(diǎn)可以是集群中的任一節(jié)點(diǎn),當(dāng)一個(gè)請(qǐng)求開(kāi)始時(shí)該節(jié)點(diǎn)作為協(xié)調(diào)節(jié)點(diǎn)存在,請(qǐng)求結(jié)束該協(xié)調(diào)節(jié)點(diǎn)生命結(jié)束。協(xié)調(diào)節(jié)點(diǎn)將請(qǐng)求轉(zhuǎn)發(fā)給其他節(jié)點(diǎn),最終匯總結(jié)果輸出。為了降低集群負(fù)荷,可以設(shè)置某些節(jié)點(diǎn)作為單獨(dú)的協(xié)調(diào)節(jié)點(diǎn),將這些節(jié)點(diǎn)的node.master,node.data全部設(shè)置為false即可,所有的數(shù)據(jù)請(qǐng)求都會(huì)發(fā)到這些單獨(dú)設(shè)置的協(xié)調(diào)節(jié)點(diǎn),例如圖中的Client Node就是協(xié)調(diào)節(jié)點(diǎn)
(2)主分片和副分片
ES中索引由一個(gè)或多個(gè)分片組成,一個(gè)分片可以擁有0個(gè)或多個(gè)副分片,保證了高可用。
例如當(dāng)node1發(fā)生宕機(jī),P0消失,集群感知到P0消失之后會(huì)找到node3的R0充當(dāng)為新的P0,此時(shí)只有node2和node3對(duì)外提供服務(wù)。
當(dāng)node1恢復(fù)時(shí),node1上面的主分片和副分片會(huì)分別從node2,node3同步數(shù)據(jù),node1本來(lái)的P0變?yōu)镽0
(3)文檔讀寫流程
寫流程:
ES協(xié)調(diào)節(jié)點(diǎn)接受客戶端請(qǐng)求,默認(rèn)根據(jù)文檔的_id值通過(guò)路由計(jì)算獲得目標(biāo)主分片,和主分片所在的節(jié)點(diǎn),將客戶端請(qǐng)求轉(zhuǎn)發(fā),寫入該節(jié)點(diǎn)的主分片,然后對(duì)于該主分片的副分片,分別找到所在的節(jié)點(diǎn)再寫入,等主副全部寫好了,ES協(xié)調(diào)節(jié)點(diǎn)告訴客戶端寫入完成
路由計(jì)算:
路由計(jì)算的目的是對(duì)于客戶端發(fā)來(lái)的文檔,找到文檔要讀取或者寫入的主分片和主分片所在的節(jié)點(diǎn),實(shí)際上是計(jì)算的分片ID,公式如下
shard=hash(routing)%number_of_primary_shards
routing:客戶端提交的參數(shù),默認(rèn)是文檔的_id值
number_of_primary_shards:索引中的主分片個(gè)數(shù)
相當(dāng)于就是對(duì)文檔hash取模計(jì)算出到底去哪個(gè)分片,計(jì)算出主分片ID之后,ES的協(xié)調(diào)節(jié)點(diǎn)上存儲(chǔ)了一份節(jié)點(diǎn)-分片的對(duì)照表,根據(jù)這個(gè)表找到主分片和副分片所在的機(jī)器節(jié)點(diǎn),找到節(jié)點(diǎn)進(jìn)行寫入操作
讀流程:
當(dāng)客戶端收到請(qǐng)求文檔的讀取要求時(shí),同樣根據(jù)路由算法找到對(duì)應(yīng)的主分片,協(xié)調(diào)節(jié)點(diǎn)根據(jù)對(duì)照表找到所有該主分片以及對(duì)應(yīng)的副分片的節(jié)點(diǎn),然后使用輪詢算法從主/副中選取一個(gè),數(shù)據(jù)傳遞給協(xié)調(diào)節(jié)點(diǎn),再返回給客戶端
輪詢算法:
輪詢算法是最簡(jiǎn)單的一種負(fù)載均衡算法。它的原理是把來(lái)自用戶的請(qǐng)求輪流分配給內(nèi)部的節(jié)點(diǎn),從節(jié)點(diǎn)1開(kāi)始,直到節(jié)點(diǎn)N,然后重新開(kāi)始循環(huán)。
Elasticsearch安裝
(1)單機(jī)安裝
進(jìn)入該網(wǎng)站下載對(duì)應(yīng)的壓縮包 https://www.elastic.co/cn/downloads/elasticsearch

開(kāi)始解壓
tar -zxvf elasticsearch-8.0.0-linux-x86_64.tar.gz
cd elasticsearch-8.0.0
ll
total 884
drwxr-xr-x 9 root root 4096 2月 4 00:55 ./
drwxr-xr-x 11 root root 4096 2月 26 19:37 ../
drwxr-xr-x 2 root root 4096 2月 4 00:55 bin/
drwxr-xr-x 3 root root 4096 2月 26 19:37 config/
drwxr-xr-x 9 root root 4096 2月 4 00:55 jdk/
drwxr-xr-x 3 root root 4096 2月 4 00:55 lib/
-rw-r--r-- 1 root root 3860 2月 4 00:47 LICENSE.txt
drwxr-xr-x 2 root root 4096 2月 4 00:52 logs/
drwxr-xr-x 65 root root 4096 2月 4 00:55 modules/
-rw-r--r-- 1 root root 858789 2月 4 00:52 NOTICE.txt
drwxr-xr-x 2 root root 4096 2月 4 00:52 plugins/
-rw-r--r-- 1 root root 2710 2月 4 00:47 README.asciidoc
ES不允許root用戶啟動(dòng),需要?jiǎng)?chuàng)建一個(gè)用戶,并且將整個(gè)目錄修改擁有者為該用戶
root@ubuntu:/opt/elasticsearch-8.0.0# useradd -s /bin/bash /us^C
root@ubuntu:/opt/elasticsearch-8.0.0# useradd -s /bin/bash -m es
root@ubuntu:/opt/elasticsearch-8.0.0# passwd es
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
root@ubuntu:/opt/elasticsearch-8.0.0# su es
修改擁有者
root@ubuntu:/opt/elasticsearch-8.0.0# chown -R es /opt/elasticsearch-8.0.0
es默認(rèn)配置進(jìn)程占用內(nèi)存1GB,如果機(jī)器內(nèi)存不足可以修改配置
# 打開(kāi) confog/jvm.options
-Xms512m
-Xmx512m
關(guān)閉ssl認(rèn)證和用戶名密碼認(rèn)證
# config/elasticsearch.yml
xpack.security.http.ssl:
enabled: false
xpack.security.enabled: false
下面啟動(dòng)es
root@ubuntu:/opt/elasticsearch-8.0.0/bin# su - es
es@ubuntu:~$ cd /opt/elasticsearch-8.0.0/bin/
es@ubuntu:/opt/elasticsearch-8.0.0/bin$ ./elasticsearch
http訪問(wèn)9200端口
root@ubuntu:~# curl http://127.0.0.1:9200
{
"name" : "ubuntu",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "9T7jMwqUR_qSchEC6Hpn0w",
"version" : {
"number" : "8.0.0",
"build_flavor" : "default",
"build_type" : "tar",
"build_hash" : "1b6a7ece17463df5ff54a3e1302d825889aa1161",
"build_date" : "2022-02-03T16:47:57.507843096Z",
"build_snapshot" : false,
"lucene_version" : "9.0.0",
"minimum_wire_compatibility_version" : "7.17.0",
"minimum_index_compatibility_version" : "7.0.0"
},
"tagline" : "You Know, for Search"
}
目錄下新增的data目錄就是存儲(chǔ)索引數(shù)據(jù)的文件
(2)安裝Kibana
Kibana提供ES的查詢可視化功能,同時(shí)提供DEV Tools,可以與ES進(jìn)行交互請(qǐng)求。Kibana版本最好es版本一致
wget https://artifacts.elastic.co/downloads/kibana/kibana-8.0.0-linux-x86_64.tar.gz
解壓,求改擁有者
tar -zxvf kibana-8.0.0-linux-x86_64.tar.gz
mv kibana-8.0.0 /opt/
chown -R es /opt/kibana-8.0.0
在kibana的配置文件中,默認(rèn)elasticsearch.hosts為對(duì)應(yīng)的ES HTTP服務(wù)地址
#elasticsearch.hosts: ["http://localhost:9200"]
如果允許其他計(jì)算機(jī)訪問(wèn)kibana,修改server.host為0.0.0.0
#server.host: "localhost"
然后先啟動(dòng)es,再啟動(dòng)kibana
es@ubuntu:/opt/kibana-8.0.0$ cd bin/
es@ubuntu:/opt/kibana-8.0.0/bin$./kibana
http端口號(hào)5601打開(kāi)kibana界面

Elasticsearch快速開(kāi)始
以下操作全部基于kibana的console
(1)創(chuàng)建索引
通過(guò)PUT請(qǐng)求,JSON中定義mappings,三個(gè)字段分別是text(文本),keyword(關(guān)鍵詞),double(小數(shù))類型
PUT /my_index
{
"mappings": {
"properties": {
"title":{
"type": "text"
},
"city":{
"type": "keyword"
},
"price": {
"type": "double"
}
}
}
}
(2)寫入文檔
使用POST請(qǐng)求,加入請(qǐng)求體JSON數(shù)據(jù),指定文檔的_id為001
POST /my_index/_doc/001
{
"title":"好再來(lái)酒店",
"city": "青島",
"price": 578.23
}
(3)根據(jù)_id搜索文檔
使用GET請(qǐng)求,在請(qǐng)求路由中指定_id
GET /my_index/_doc/001
查看返回結(jié)果,顯示了該文檔的_id,_version(版本號(hào)1),_source(返回的字段)
{
"_index" : "my_index",
"_id" : "001",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
"title" : "好再來(lái)酒店",
"city" : "青島",
"price" : 578.23
}
}
(4)根據(jù)一般字段搜索文檔
進(jìn)行復(fù)雜搜索時(shí)需要用到GET請(qǐng)求的_search路由和query請(qǐng)求體,本例查找price是578.23 的文檔,使用term搜索
GET /my_index/_search
{
"query": {
"term": {
"price": {
"value": 578.23
}
}
}
}
看一下返回
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "my_index",
"_id" : "001",
"_score" : 1.0,
"_source" : {
"title" : "好再來(lái)酒店",
"city" : "青島",
"price" : 578.23
}
}
]
}
}
返回結(jié)果除了文檔詳情之外還顯示了_shards分片信息,一共一個(gè)分片,hits信息一共一條符合要求的文檔
(5)文本字段搜索
文本模糊搜索需要使用query中的match關(guān)鍵字
GET /my_index/_search
{
"query": {
"match": {
"title": "好再"
}
}
}
查看返回結(jié)果
{
"took" : 5,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.5753642,
"hits" : [
{
"_index" : "my_index",
"_id" : "001",
"_score" : 0.5753642,
"_source" : {
"title" : "好再來(lái)酒店",
"city" : "青島",
"price" : 578.23
}
}
]
}
}
返回來(lái)對(duì)應(yīng)的文檔,耗費(fèi)了5ms(took關(guān)鍵字)




