ElasticSearch學習文檔

第一章 ElasticSearch入門篇

第一節 ElasticSearch概述

1.1 ElasticSearch是一個基於Lucene的搜索服務器。

它提供了一個分佈式多用戶能力的全文搜索引擎,基於RESTfulweb接口。ElasticSearch是用Java開發的, 並做爲Apache許可條款下的開放源碼發佈,是當前流行的企業級搜索引擎。設計用於雲計算中,可以達到實時搜索,穩定,可靠,快速,安裝使用方便。構建在全 文檢索開源軟件Lucene之上的Elasticsearch,不只能對海量規模的數據完成分佈式索引與檢索,還能提供數據聚合分析。據國際權威的 數據庫產品評測機構DBEngines的統計,在2016年1月,Elasticsearch已超過Solr等,成爲排名第一的搜索引擎類應用 歸納:基於Restful標準的高擴展高可用的實時數據分析的全文搜索工具html

1.2 ElasticSearch的基本概念

Index

相似於mysql數據庫中的databasejava

Type

相似於mysql數據庫中的table表,es中能夠在Index中創建type(table),經過mapping進行映射。node

Document

因爲es存儲的數據是文檔型的,一條數據對應一篇文檔即至關於mysql數據庫中的一行數據row, 一個文檔中能夠有多個字段也就是mysql數據庫一行能夠有多列。python

Field

es中一個文檔中對應的多個列與mysql數據庫中每一列對應mysql

Mapping

能夠理解爲mysql或者solr中對應的schema,只不過有些時候es中的mapping增長了動態識別功能,感受很強大的樣子, 其實實際生產環境上不建議使用,最好仍是開始制定好了對應的schema爲主。nginx

indexed

就是名義上的創建索引。mysql中通常會對常用的列增長相應的索引用於提升查詢速度,而在es中默認都是會加 上索引的,除非你特殊制定不創建索引只是進行存儲用於展現,這個須要看你具體的需求和業務進行設定了。git

Query DSL

相似於mysql的sql語句,只不過在es中是使用的json格式的查詢語句,專業術語就叫:QueryDSLgithub

GET/PUT/POST/DELETE

分別相似與mysql中的select/update/delete......web

1.3 Elasticsearch的架構

image

Gateway層

es用來存儲索引文件的一個文件系統且它支持不少類型,例如:本地磁盤、共享存儲(作snapshot的時候須要用到)、hadoop 的hdfs分佈式存儲、亞馬遜的S3。它的主要職責是用來對數據進行長持久化以及整個集羣重啓以後能夠經過gateway從新恢復數據。算法

Distributed Lucene Directory

Gateway上層就是一個lucene的分佈式框架,lucene是作檢索的,可是它是一個單機的搜索引擎,像這種es分佈式搜索引擎系 統,雖然底層用lucene,可是須要在每一個節點上都運行lucene進行相應的索引、查詢以及更新,因此須要作成一個分佈式的運 行框架來知足業務的須要。

四大模塊組件

districted lucene directory之上就是一些es的模塊

1.Index Module是索引模塊,就是對數據創建索引也就是一般所說的創建一些倒排索引等;

2.Search Module是搜索模塊,就是對數據進行查詢搜索;

3.Mapping模塊是數據映射與解析模塊,就是你的數據的每一個字段能夠根據你創建的表結構 經過mapping進行映射解析,若是你沒有創建表結構,es就會根據你的數據類型推測你 的數據結構以後本身生成一個mapping,而後都是根據這個mapping進行解析你的數據;

4.River模塊在es2.0以後應該是被取消了,它的意思表示是第三方插件,例如能夠經過一 些自定義的腳本將傳統的數據庫(mysql)等數據源經過格式化轉換後直接同步到es集羣裏, 這個river大部分是本身寫的,寫出來的東西質量良莠不齊,將這些東西集成到es中會引起 不少內部bug,嚴重影響了es的正常應用,因此在es2.0以後考慮將其去掉。

Discovery、Script

es4大模塊組件之上有 Discovery模塊:es是一個集羣包含不少節點,不少節點須要互相發現對方,而後組成一個集羣包括選 主的,這些es都是用的discovery模塊,默認使用的是Zen,也但是使用EC2;es查詢還能夠支撐多種script即腳本語言,包括 mvel、js、python等等。

Transport協議層

再上一層就是es的通信接口Transport,支持的也比較多:Thrift、Memcached以及Http,默認的是http,JMX就是java的一個 遠程監控管理框架,由於es是經過java實現的。

RESTful接口層

最上層就是es暴露給咱們的訪問接口,官方推薦的方案就是這種Restful接口,直接發送http請求,方便後續使用nginx作代理、 分發包括可能後續會作權限的管理,經過http很容易作這方面的管理。若是使用java客戶端它是直接調用api,在作負載均衡以 及權限管理仍是不太好作。

1.4 RESTful API

一種軟件架構風格、設計風格,而不是標準,只是提供了一組設計原則和約束條件。它主要用於客戶端和服務器交互類的軟件。 基於這個風格設計的軟件能夠更簡潔,更有層次,更易於實現緩存等機制。在目前主流的三種Web服務交互方案中,REST相比於S OAP(Simple Object Access protocol,簡單對象訪問協議)以及XML-RPC更加簡單明瞭

(Representational State Transfer 意思是:表述性狀態傳遞)

它使用典型的HTTP方法,諸如GET,POST.DELETE,PUT來實現資源的獲取,添加,修改,刪除等操做。即經過HTTP動詞來實現資源的狀態扭轉
複製代碼

GET 用來獲取資源

POST 用來新建資源(也能夠用於更新資源)

PUT 用來更新資源

DELETE 用來刪除資源
複製代碼

1.5 CRUL命令

以命令的方式執行HTTP協議的請求
GET/POST/PUT/DELETE

示例:
訪問一個網頁

curl www.baidu.com

curl -o tt.html www.baidu.com

顯示響應的頭信息

curl -i www.baidu.com

顯示一次HTTP請求的通訊過程

curl -v www.baidu.com

執行GET/POST/PUT/DELETE操做

curl -X GET/POST/PUT/DELETE url
複製代碼

1.6 CentOS7下安裝ElasticSearch6.2.4

配置JDK環境

配置環境變量
複製代碼
export JAVA_HOME="/opt/jdk1.8.0_144"

export PATH="$JAVA_HOME/bin:$PATH"

export CLASSPATH=".:$JAVA_HOME/lib"
複製代碼

安裝ElasticSearch6.2.4

下載地址:www.elastic.co/cn/download…

啓動報錯:

image

解決方式: bin/elasticsearch -Des.insecure.allow.root=true

或者修改bin/elasticsearch,加上ES_JAVA_OPTS屬性: ES_JAVA_OPTS="-Des.insecure.allow.root=true"

再次啓動:

image

這是出於系統安全考慮設置的條件。因爲ElasticSearch能夠接收用戶輸入的腳本而且執行,爲了系統安全考 慮,建議建立一個單獨的用戶用來運行ElasticSearch。

建立用戶組和用戶:

groupadd esgroup

useradd esuser -g esgroup -p espassword

更改elasticsearch文件夾及內部文件的所屬用戶及組:

cd /opt

chown -R esuser:esgroup elasticsearch-6.2.4

切換用戶並運行:

su esuser

./bin/elasticsearch

再次啓動顯示已殺死:

image

須要調整JVM的內存大小:

vi bin/elasticsearch

ES_JAVA_OPTS="-Xms512m -Xmx512m"

再次啓動:啓動成功

若是顯示以下相似信息:

[INFO ][o.e.c.r.a.DiskThresholdMonitor] [ZAds5FP] low disk watermark [85%] exceeded on     [ZAds5FPeTY-ZUKjXd7HJKA][ZAds5FP][/opt/elasticsearch-6.2.4/data/nodes/0] free: 1.2gb[14.2%],     replicas will not be assigned to this node
複製代碼

須要清理磁盤空間。

後臺運行:./bin/elasticsearch -d

測試鏈接:curl 127.0.0.1:9200

會看到一下JSON數據:

[root@localhost ~]# curl 127.0.0.1:9200
  {
  "name" : "rBrMTNx",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "-noR5DxFRsyvAFvAzxl07g",
  "version" : {
    "number" : "5.1.1",
    "build_hash" : "5395e21",
    "build_date" : "2016-12-06T12:36:15.409Z",
    "build_snapshot" : false,
    "lucene_version" : "6.3.0"
  },
  "tagline" : "You Know, for Search"
 }
複製代碼

實現遠程訪問: 須要對config/elasticsearch.yml進行 配置: network.host: 192.168.25.131

再次啓動報錯:

image

處理第一個錯誤:

vim /etc/security/limits.conf //文件最後加入

esuser soft nofile 65536

esuser hard nofile 65536

esuser soft nproc 4096

esuser hard nproc 4096

處理第二個錯誤:

進入limits.d目錄下修改配置文件。

vim /etc/security/limits.d/20-nproc.conf 修改成 esuser soft nproc 4096

處理第三個錯誤:

vim /etc/sysctl.conf

vm.max_map_count=655360

執行如下命令生效: sysctl -p

關閉防火牆:systemctl stop firewalld.service

再次啓動成功!

1.7安裝Head插件

Head是elasticsearch的集羣管理工具,能夠用於數據的瀏覽和查詢

(1)elasticsearch-head是一款開源軟件,被託管在github上面,因此若是咱們要使用它,必須先安裝git,經過git獲取elasticsearch-head

(2)運行elasticsearch-head會用到grunt,而grunt須要npm包管理器,因此nodejs是必需要安裝的

(3)elasticsearch5.0以後,elasticsearch-head不作爲插件放在其plugins目錄下了。 使用git拷貝elasticsearch-head到本地

cd /usr/local/

git clone git://github.com/mobz/elasticsearch-head.git

(4)安裝elasticsearch-head依賴包

[root@localhost local]# npm install -g grunt-cli

[root@localhost _site]# cd /usr/local/elasticsearch-head/

[root@localhost elasticsearch-head]# cnpm install

(5)修改Gruntfile.js

[root@localhost _site]# cd /usr/local/elasticsearch-head/

[root@localhost elasticsearch-head]# vi Gruntfile.js

在connect-->server-->options下面添加:hostname:’*’,容許全部IP能夠訪問

(6)修改elasticsearch-head默認鏈接地址 [root@localhost elasticsearch-head]# cd /usr/local/elasticsearch-head/_site/

[root@localhost _site]# vi app.js

將this.base_uri = this.config.base_uri || this.prefs.get("app-base_uri") || "http://localhost:9200";中的localhost修改爲你es的服務器地址

(7)配置elasticsearch容許跨域訪問

打開elasticsearch的配置文件elasticsearch.yml,在文件末尾追加下面兩行代碼便可:

http.cors.enabled: true

http.cors.allow-origin: "*"

(8)打開9100端口

[root@localhost elasticsearch-head]# firewall-cmd --zone=public --add-port=9100/tcp --permanent

重啓防火牆

[root@localhost elasticsearch-head]# firewall-cmd --reload

(9)啓動elasticsearch

(10)啓動elasticsearch-head

[root@localhost _site]# cd /usr/local/elasticsearch-head/

[root@localhost elasticsearch-head]# node_modules/grunt/bin/grunt server

(11)訪問elasticsearch-head

關閉防火牆:systemctl stop firewalld.service

瀏覽器輸入網址:http://192.168.25.131:9100/

1.8安裝Kibana

Kibana是一個針對Elasticsearch的開源分析及可視化平臺,使用Kibana能夠查詢、查看並與存儲在ES索引的數據進行交互操做,使用Kibana能執行高級的數據分析,並能以圖表、表格和地圖的形式查看數據

(1)下載Kibana www.elastic.co/downloads/k…

(2)把下載好的壓縮包拷貝到/soft目錄下

(3)解壓縮,並把解壓後的目錄移動到/user/local/kibana

(4)編輯kibana配置文件

[root@localhost /]# vi /usr/local/kibana/config/kibana.yml

image

將server.host,elasticsearch.url修改爲所在服務器的ip地址

(5)開啓5601端口

Kibana的默認端口是5601

開啓防火牆:systemctl start firewalld.service

開啓5601端口:firewall-cmd --permanent --zone=public --add-port=5601/tcp

重啓防火牆:firewall-cmd –reload

(6)啓動Kibana

[root@localhost /]# /usr/local/kibana/bin/kibana

瀏覽器訪問:http://192.168.25.131:5601

1.9安裝中文分詞器

(1)下載中文分詞器 github.com/medcl/elast…

下載elasticsearch-analysis-ik-master.zip
複製代碼

(2)解壓elasticsearch-analysis-ik-master.zip

unzip elasticsearch-analysis-ik-master.zip

(3)進入elasticsearch-analysis-ik-master,編譯源碼

mvn clean install -Dmaven.test.skip=true

(4)在es的plugins文件夾下建立目錄ik

(5)將編譯後生成的elasticsearch-analysis-ik-版本.zip移動到ik下,並解壓

(6)解壓後的內容移動到ik目錄下

第二節 ElasticSearch基本操做

2.1倒排索引

Elasticsearch 使用一種稱爲 倒排索引 的結構,它適用於快速的全文搜索。一個倒排索引由文檔中全部不重複詞的列表構成,對於其中每一個詞,有一個包含它的文檔列表。

示例:

(1):假設文檔集合包含五個文檔,每一個文檔內容如圖所示,在圖中最左端一欄是每一個文檔對應的文檔編號。咱們的任務就是對這個文檔集合創建倒排索引。

(2):中文和英文等語言不一樣,單詞之間沒有明確分隔符號,因此首先要用分詞系統將文檔自動切分紅單詞序列。這樣每一個文檔就轉換爲由單詞序列構成的數據流,爲了系統後續處理方便,須要對每一個不一樣的單詞賦予惟一的單詞編號,同時記錄下哪些文檔包含這個單詞,在如此處理結束後,咱們能夠獲得最簡單的倒排索引

「單詞ID」一欄記錄了每一個單詞的單詞編號,第二欄是對應的單詞,第三欄即每一個單詞對應的倒排列表

(3):索引系統還能夠記錄除此以外的更多信息,下圖還記載了單詞頻率信息(TF)即這個單詞在某個文檔中的出現次數,之因此要記錄這個信息,是由於詞頻信息在搜索結果排序時,計算查詢和文檔類似度是很重要的一個計算因子,因此將其記錄在倒排列表中,以方便後續排序時進行分值計算。

(4):倒排列表中還能夠記錄單詞在某個文檔出現的位置信息

(1,<11>,1),(2,<7>,1),(3,<3,9>,2)

有了這個索引系統,搜索引擎能夠很方便地響應用戶的查詢,好比用戶輸入查詢詞「Facebook」,搜索系統查找倒排索引,從中能夠讀出包含這個單詞的文檔,這些文檔就是提供給用戶的搜索結果,而利用單詞頻率信息、文檔頻率信息便可以對這些候選搜索結果進行排序,計算文檔和查詢的類似性,按照類似性得分由高到低排序輸出,此即爲搜索系統的部份內部流程。

2.1.2 倒排索引原理

1.The quick brown fox jumped over the lazy dog

2.Quick brown foxes leap over lazy dogs in summer

倒排索引:

Term Doc_1 Doc_2
Quick X
The X
brown X X
dog X
dogs X
fox X
foxes X
in X
jumped X
lazy X X
leap X
over X X
quick X
summer X
the X

搜索quick brown :

Term Doc_1 Doc_2
brown X X
quick X
Total 2 1

計算相關度分數時,文檔1的匹配度高,分數會比文檔2高

問題:

Quick 和 quick 以獨立的詞條出現,然而用戶可能認爲它們是相同的詞。

fox 和 foxes 很是類似, 就像 dog 和 dogs ;他們有相同的詞根。

jumped 和 leap, 儘管沒有相同的詞根,但他們的意思很相近。他們是同義詞。

搜索含有 Quick fox的文檔是搜索不到的

使用標準化規則(normalization): 創建倒排索引的時候,會對拆分出的各個單詞進行相應的處理,以提高後面搜索的時候可以搜索到相關聯的文檔的機率

Term Doc_1 Doc_2
brown X X
dog X X
fox X X
in X
jump X X
lazy X X
over X X
quick X X
summer X
the X X

2.1.3 分詞器介紹及內置分詞器

分詞器:從一串文本中切分出一個一個的詞條,並對每一個詞條進行標準化

包括三部分:

character filter:分詞以前的預處理,過濾掉HTML標籤,特殊符號轉換等

tokenizer:分詞

token filter:標準化

內置分詞器:

standard 分詞器:(默認的)他會將詞彙單元轉換成小寫形式,並去除停用詞和標點符號,支持中文采用的方法爲單字切分

simple 分詞器:首先會經過非字母字符來分割文本信息,而後將詞彙單元統一爲小寫形式。該分析器會去掉數字類型的字符。

Whitespace 分詞器:僅僅是去除空格,對字符沒有lowcase化,不支持中文; 而且不對生成的詞彙單元進行其餘的標準化處理。

language 分詞器:特定語言的分詞器,不支持中文

2.2 使用ElasticSearch API 實現CRUD

添加索引:

PUT /lib/

{

  "settings":{
  
      "index":{
      
        "number_of_shards": 5,
        
        "number_of_replicas": 1
        
        }
        
      }
}

PUT  lib
複製代碼

查看索引信息:

GET /lib/_settings

GET _all/_settings
複製代碼

添加文檔:

PUT /lib/user/1

{
    "first_name" :  "Jane",
    
    "last_name" :   "Smith",
    
    "age" :         32,
    
    "about" :       "I like to collect rock albums",
    
    "interests":  [ "music" ]
}

POST /lib/user/

{
    "first_name" :  "Douglas",
    
    "last_name" :   "Fir",
    
    "age" :         23,
    
    "about":        "I like to build cabinets",
    
    "interests":  [ "forestry" ]
    
}
複製代碼

查看文檔:

GET /lib/user/1

GET /lib/user/

GET /lib/user/1?_source=age,interests
複製代碼

更新文檔:

PUT /lib/user/1

{
    "first_name" :  "Jane",
    
    "last_name" :   "Smith",
    
    "age" :         36,
    
    "about" :       "I like to collect rock albums",
    
    "interests":  [ "music" ]
}

POST /lib/user/1/_update

{

  "doc":{
  
      "age":33
      
      }
}
複製代碼

刪除一個文檔

DELETE /lib/user/1
複製代碼

刪除一個索引

DELETE /lib
複製代碼

2.3 批量獲取文檔

使用es提供的Multi Get API:

使用Multi Get API能夠經過索引名、類型名、文檔id一次獲得一個文檔集合,文檔能夠來自同一個索引庫,也能夠來自不一樣索引庫

使用curl命令:

curl 'http://192.168.25.131:9200/_mget' -d '{ "docs":[ { "_index": "lib", "_type": "user", "_id": 1 }, { "_index": "lib", "_type": "user", "_id": 2 } ] }'
複製代碼

在客戶端工具中:

GET /_mget

{
   
    "docs":[
       
       {
           "_index": "lib",
           "_type": "user",
           "_id": 1
       },
       {
           "_index": "lib",
           "_type": "user",
           "_id": 2
       },
       {
           "_index": "lib",
           "_type": "user",
           "_id": 3
       }
       
     ]
}
複製代碼

能夠指定具體的字段:

GET /_mget

{
   
    "docs":[
       
       {
           "_index": "lib",
           "_type": "user",
           "_id": 1,
           "_source": "interests"
       },
       {
           "_index": "lib",
           "_type": "user",
           "_id": 2,
           "_source": ["age","interests"]
       }
       
     ]
}
複製代碼

獲取同索引同類型下的不一樣文檔:

GET /lib/user/_mget

{
   
    "docs":[
       
       {
           "_id": 1
       },
       {
           "_type": "user",
           "_id": 2,
       }
       
     ]
}

GET /lib/user/_mget

{
   
   "ids": ["1","2"]
   
}
複製代碼

2.4 使用Bulk API 實現批量操做

bulk的格式:

{action:{metadata}}\n

{requstbody}\n

action:(行爲)

  create:文檔不存在時建立
  
  update:更新文檔
  
  index:建立新文檔或替換已有文檔
  
  delete:刪除一個文檔
  
metadata:_index,_type,_id
複製代碼

create 和index的區別

若是數據存在,使用create操做失敗,會提示文檔已經存在,使用index則能夠成功執行。

示例:

{"delete":{"_index":"lib","_type":"user","_id":"1"}}
複製代碼

批量添加:

POST /lib2/books/_bulk

{"index":{"_id":1}}

{"title":"Java","price":55}

{"index":{"_id":2}}

{"title":"Html5","price":45}

{"index":{"_id":3}}

{"title":"Php","price":35}

{"index":{"_id":4}}

{"title":"Python","price":50}
複製代碼

批量獲取:

GET /lib2/books/_mget
{

"ids": ["1","2","3","4"]
}
複製代碼

刪除:沒有請求體

POST /lib2/books/_bulk

{"delete":{"_index":"lib2","_type":"books","_id":4}}

{"create":{"_index":"tt","_type":"ttt","_id":"100"}}

{"name":"lisi"}

{"index":{"_index":"tt","_type":"ttt"}}

{"name":"zhaosi"}

{"update":{"_index":"lib2","_type":"books","_id":"4"}}

{"doc":{"price":58}}
複製代碼

bulk一次最大處理多少數據量:

  bulk會把將要處理的數據載入內存中,因此數據量是有限制的,最佳的數據量不是一個肯定的數值,它取決於你的硬件,你的文檔大小以及複雜性,你的索引以及搜索的負載。

  通常建議是1000-5000個文檔,大小建議是5-15MB,默認不能超過100M,能夠在es的配置文件(即$ES_HOME下的config下的elasticsearch.yml)中。   

2.5 版本控制

ElasticSearch採用了樂觀鎖來保證數據的一致性,也就是說,當用戶對document進行操做時,並不須要對該document做加鎖和解鎖的操做,只須要指定要操做的版本便可。當版本號一致時,ElasticSearch會容許該操做順利執行,而當版本號存在衝突時,ElasticSearch會提示衝突並拋出異常(VersionConflictEngineException異常)。

ElasticSearch的版本號的取值範圍爲1到2^63-1。

內部版本控制:使用的是_version

外部版本控制:elasticsearch在處理外部版本號時會與對內部版本號的處理有些不一樣。它再也不是檢查_version是否與請求中指定的數值_相同_,而是檢查當前的_version是否比指定的數值小。若是請求成功,那麼外部的版本號就會被存儲到文檔中的_version中。

爲了保持_version與外部版本控制的數據一致 使用version_type=external

2.6 什麼是Mapping

PUT /myindex/article/1 
{ 
  "post_date": "2018-05-10", 
  "title": "Java", 
  "content": "java is the best language", 
  "author_id": 119
}

PUT /myindex/article/2
{ 
  "post_date": "2018-05-12", 
  "title": "html", 
  "content": "I like html", 
  "author_id": 120
}

PUT /myindex/article/3
{ 
  "post_date": "2018-05-16", 
  "title": "es", 
  "content": "Es is distributed document store", 
  "author_id": 110
}


GET /myindex/article/_search?q=2018-05

GET /myindex/article/_search?q=2018-05-10

GET /myindex/article/_search?q=html

GET /myindex/article/_search?q=java

#查看es自動建立的mapping

GET /myindex/article/_mapping
複製代碼

es自動建立了index,type,以及type對應的mapping(dynamic mapping)

什麼是映射:mapping定義了type中的每一個字段的數據類型以及這些字段如何分詞等相關屬性

{
  "myindex": {
    "mappings": {
      "article": {
        "properties": {
          "author_id": {
            "type": "long"
          },
          "content": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "post_date": {
            "type": "date"
          },
          "title": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    }
  }
}
複製代碼

建立索引的時候,能夠預先定義字段的類型以及相關屬性,這樣就可以把日期字段處理成日期,把數字字段處理成數字,把字符串字段處理字符串值等

支持的數據類型:

(1)核心數據類型(Core datatypes)

字符型:string,string類型包括
text 和 keyword

text類型被用來索引長文本,在創建索引前會將這些文本進行分詞,轉化爲詞的組合,創建索引。容許es來檢索這些詞語。text類型不能用來排序和聚合。

Keyword類型不須要進行分詞,能夠被用來檢索過濾、排序和聚合。keyword 類型字段只能用自己來進行檢索

數字型:long, integer, short, byte, double, float
日期型:date
布爾型:boolean
二進制型:binary
複製代碼

(2)複雜數據類型(Complex datatypes)

數組類型(Array datatype):數組類型不須要專門指定數組元素的type,例如:
    字符型數組: [ "one", "two" ]
    整型數組:[ 1, 2 ]
    數組型數組:[ 1, [ 2, 3 ]] 等價於[ 1, 2, 3 ]
    對象數組:[ { "name": "Mary", "age": 12 }, { "name": "John", "age": 10 }]
對象類型(Object datatype):_ object _ 用於單個JSON對象;
嵌套類型(Nested datatype):_ nested _ 用於JSON數組;
複製代碼

(3)地理位置類型(Geo datatypes)

地理座標類型(Geo-point datatype):_ geo_point _ 用於經緯度座標;
地理形狀類型(Geo-Shape datatype):_ geo_shape _ 用於相似於多邊形的複雜形狀;
複製代碼

(4)特定類型(Specialised datatypes)

IPv4 類型(IPv4 datatype):_ ip _ 用於IPv4 地址;
Completion 類型(Completion datatype):_ completion _提供自動補全建議;
Token count 類型(Token count datatype):_ token_count _ 用於統計作了標記的字段的index數目,該值會一直增長,不會由於過濾條件而減小。
mapper-murmur3
類型:經過插件,能夠經過 _ murmur3 _ 來計算 index 的 hash 值;
附加類型(Attachment datatype):採用 mapper-attachments
插件,可支持_ attachments _ 索引,例如 Microsoft Office 格式,Open Document 格式,ePub, HTML 等。
複製代碼

支持的屬性:

"store":false//是否單獨設置此字段的是否存儲而從_source字段中分離,默認是false,只能搜索,不能獲取值

"index": true//分詞,不分詞是:false ,設置成false,字段將不會被索引

"analyzer":"ik"//指定分詞器,默認分詞器爲standard analyzer

"boost":1.23//字段級別的分數加權,默認值是1.0

"doc_values":false//對not_analyzed字段,默認都是開啓,分詞字段不能使用,對排序和聚合能提高較大性能,節約內存

"fielddata":{"format":"disabled"}//針對分詞字段,參與排序或聚合時能提升性能,不分詞字段統一建議使用doc_value

"fields":{"raw":{"type":"string","index":"not_analyzed"}} //能夠對一個字段提供多種索引模式,同一個字段的值,一個分詞,一個不分詞

"ignore_above":100 //超過100個字符的文本,將會被忽略,不被索引

"include_in_all":ture//設置是否此字段包含在_all字段中,默認是true,除非index設置成no選項

"index_options":"docs"//4個可選參數docs(索引文檔號) ,freqs(文檔號+詞頻),positions(文檔號+詞頻+位置,一般用來距離查詢),offsets(文檔號+詞頻+位置+偏移量,一般被使用在高亮字段)分詞字段默認是position,其餘的默認是docs

"norms":{"enable":true,"loading":"lazy"}//分詞字段默認配置,不分詞字段:默認{"enable":false},存儲長度因子和索引時boost,建議對須要參與評分字段使用 ,會額外增長內存消耗量

"null_value":"NULL"//設置一些缺失字段的初始化值,只有string可使用,分詞字段的null值也會被分詞

"position_increament_gap":0//影響距離查詢或近似查詢,能夠設置在多值字段的數據上火分詞字段上,查詢時可指定slop間隔,默認值是100

"search_analyzer":"ik"//設置搜索時的分詞器,默認跟ananlyzer是一致的,好比index時用standard+ngram,搜索時用standard用來完成自動提示功能

"similarity":"BM25"//默認是TF/IDF算法,指定一個字段評分策略,僅僅對字符串型和分詞類型有效

"term_vector":"no"//默認不存儲向量信息,支持參數yes(term存儲),with_positions(term+位置),with_offsets(term+偏移量),with_positions_offsets(term+位置+偏移量) 對快速高亮fast vector highlighter能提高性能,但開啓又會加大索引體積,不適合大數據量用

映射的分類:

(1)動態映射:

當ES在文檔中碰到一個之前沒見過的字段時,它會利用動態映射來決定該字段的類型,並自動地對該字段添加映射。

能夠經過dynamic設置來控制這一行爲,它可以接受如下的選項:

true:默認值。動態添加字段
false:忽略新字段
strict:若是碰到陌生字段,拋出異常
複製代碼

dynamic設置能夠適用在根對象上或者object類型的任意字段上。

POST /lib2

#給索引lib2建立映射類型

{

    "settings":{
    
    "number_of_shards" : 3,
    
    "number_of_replicas" : 0
    
    },
    
     "mappings":{
     
      "books":{
      
        "properties":{
        
            "title":{"type":"text"},
            "name":{"type":"text","index":false},
            "publish_date":{"type":"date","index":false},
            
            "price":{"type":"double"},
            
            "number":{"type":"integer"}
        }
      }
     }
}
複製代碼

POST /lib2

#給索引lib2建立映射類型

{

    "settings":{
    
    "number_of_shards" : 3,
    
    "number_of_replicas" : 0
    
    },
    
     "mappings":{
     
      "books":{
      
        "properties":{
        
            "title":{"type":"text"},
            "name":{"type":"text","index":false},
            "publish_date":{"type":"date","index":false},
            
            "price":{"type":"double"},
            
            "number":{
                "type":"object",
                "dynamic":true
            }
        }
      }
     }
}
複製代碼

2.7 基本查詢(Query查詢)

2.7.1 數據準備

PUT /lib3
{
    "settings":{
    "number_of_shards" : 3,
    "number_of_replicas" : 0
    },
     "mappings":{
      "user":{
        "properties":{
            "name": {"type":"text"},
            "address": {"type":"text"},
            "age": {"type":"integer"},
            "interests": {"type":"text"},
            "birthday": {"type":"date"}
        }
      }
     }
}

GET /lib3/user/_search?q=name:lisi

GET /lib3/user/_search?q=name:zhaoliu&sort=age:desc
複製代碼

2.7.2 term查詢和terms查詢

term query會去倒排索引中尋找確切的term,它並不知道分詞器的存在。這種查詢適合keyword 、numeric、date。

term:查詢某個字段裏含有某個關鍵詞的文檔

GET /lib3/user/_search/
{
  "query": {
      "term": {"interests": "changge"}
  }
}
複製代碼

terms:查詢某個字段裏含有多個關鍵詞的文檔

GET /lib3/user/_search
{
    "query":{
        "terms":{
            "interests": ["hejiu","changge"]
        }
    }
}
複製代碼

2.7.3 控制查詢返回的數量

from:從哪個文檔開始 size:須要的個數

GET /lib3/user/_search
{
    "from":0,
    "size":2,
    "query":{
        "terms":{
            "interests": ["hejiu","changge"]
        }
    }
}
複製代碼

2.7.4 返回版本號

GET /lib3/user/_search
{
    "version":true,
    "query":{
        "terms":{
            "interests": ["hejiu","changge"]
        }
    }
}
複製代碼

2.7.5 match查詢

match query知道分詞器的存在,會對filed進行分詞操做,而後再查詢

GET /lib3/user/_search
{
    "query":{
        "match":{
            "name": "zhaoliu"
        }
    }
}
GET /lib3/user/_search
{
    "query":{
        "match":{
            "age": 20
        }
    }
}
複製代碼

match_all:查詢全部文檔

GET /lib3/user/_search
{
  "query": {
    "match_all": {}
  }
}
複製代碼

multi_match:能夠指定多個字段

GET /lib3/user/_search
{
    "query":{
        "multi_match": {
            "query": "lvyou",
            "fields": ["interests","name"]
         }
    }
}
複製代碼

match_phrase:短語匹配查詢

ElasticSearch引擎首先分析(analyze)查詢字符串,從分析後的文本中構建短語查詢,這意味着必須匹配短語中的全部分詞,而且保證各個分詞的相對位置不變:

GET lib3/user/_search
{
  "query":{  
      "match_phrase":{  
         "interests": "duanlian,shuoxiangsheng"
      }
   }
}
複製代碼

2.7.6 指定返回的字段

GET /lib3/user/_search
{
    "_source": ["address","name"],
    "query": {
        "match": {
            "interests": "changge"
        }
    }
}
複製代碼

2.7.7 控制加載的字段

GET /lib3/user/_search
{
    "query": {
        "match_all": {}
    },
    
    "_source": {
          "includes": ["name","address"],
          "excludes": ["age","birthday"]
      }
}
複製代碼

使用通配符*

GET /lib3/user/_search
{
    "_source": {
          "includes": "addr*",
          "excludes": ["name","bir*"]
        
    },
    "query": {
        "match_all": {}
    }
}
複製代碼

2.7.8 排序

使用sort實現排序: desc:降序,asc升序

GET /lib3/user/_search
{
    "query": {
        "match_all": {}
    },
    "sort": [
        {
           "age": {
               "order":"asc"
           }
        }
    ]
        
}

GET /lib3/user/_search
{
    "query": {
        "match_all": {}
    },
    "sort": [
        {
           "age": {
               "order":"desc"
           }
        }
    ]
        
}
複製代碼

2.7.9 前綴匹配查詢

GET /lib3/user/_search
{
  "query": {
    "match_phrase_prefix": {
        "name": {
            "query": "zhao"
        }
    }
  }
}
複製代碼

2.7.10 範圍查詢

range:實現範圍查詢

參數:from,to,include_lower,include_upper,boost

include_lower:是否包含範圍的左邊界,默認是true

include_upper:是否包含範圍的右邊界,默認是true

GET /lib3/user/_search
{
    "query": {
        "range": {
            "birthday": {
                "from": "1990-10-10",
                "to": "2018-05-01"
            }
        }
    }
}


GET /lib3/user/_search
{
    "query": {
        "range": {
            "age": {
                "from": 20,
                "to": 25,
                "include_lower": true,
                "include_upper": false
            }
        }
    }
}
複製代碼

2.7.11 wildcard查詢

容許使用通配符* 和 ?來進行查詢

*表明0個或多個字符

?表明任意一個字符

GET /lib3/user/_search
{
    "query": {
        "wildcard": {
             "name": "zhao*"
        }
    }
}


GET /lib3/user/_search
{
    "query": {
        "wildcard": {
             "name": "li?i"
        }
    }
}
複製代碼

2.7.12 fuzzy實現模糊查詢

value:查詢的關鍵字

boost:查詢的權值,默認值是1.0

min_similarity:設置匹配的最小類似度,默認值爲0.5,對於字符串,取值爲0-1(包括0和1);對於數值,取值可能大於1;對於日期型取值爲1d,1m等,1d就表明1天

prefix_length:指明區分詞項的共同前綴長度,默認是0

max_expansions:查詢中的詞項能夠擴展的數目,默承認以無限大

GET /lib3/user/_search
{
    "query": {
        "fuzzy": {
             "interests": "chagge"
        }
    }
}

GET /lib3/user/_search
{
    "query": {
        "fuzzy": {
             "interests": {
                 "value": "chagge"
             }
        }
    }
}
複製代碼

2.7.13 高亮搜索結果

GET /lib3/user/_search
{
    "query":{
        "match":{
            "interests": "changge"
        }
    },
    "highlight": {
        "fields": {
             "interests": {}
        }
    }
}
複製代碼

2.8 Filter查詢

filter是不計算相關性的,同時能夠cache。所以,filter速度要快於query。

POST /lib4/items/_bulk
{"index": {"_id": 1}}

{"price": 40,"itemID": "ID100123"}

{"index": {"_id": 2}}

{"price": 50,"itemID": "ID100124"}

{"index": {"_id": 3}}

{"price": 25,"itemID": "ID100124"}

{"index": {"_id": 4}}

{"price": 30,"itemID": "ID100125"}

{"index": {"_id": 5}}

{"price": null,"itemID": "ID100127"}
複製代碼

####2.8.1 簡單的過濾查詢

GET /lib4/items/_search
{ 
       "post_filter": {
             "term": {
                 "price": 40
             }
       }
}


GET /lib4/items/_search
{
      "post_filter": {
          "terms": {
                 "price": [25,40]
              }
        }
}

GET /lib4/items/_search
{
    "post_filter": {
        "term": {
            "itemID": "ID100123"
          }
      }
}
複製代碼

查看分詞器分析的結果:

GET /lib4/_mapping
複製代碼

不但願商品id字段被分詞,則從新建立映射

DELETE lib4

PUT /lib4
{
    "mappings": {
        "items": {
            "properties": {
                "itemID": {
                    "type": "text",
                    "index": false
                }
            }
        }
    }
}
複製代碼

2.8.2 bool過濾查詢

能夠實現組合過濾查詢

格式:

{
    "bool": {
        "must": [],
        "should": [],
        "must_not": []
    }
}
複製代碼

must:必須知足的條件---and

should:能夠知足也能夠不知足的條件--or

must_not:不須要知足的條件--not

GET /lib4/items/_search
{
    "post_filter": {
          "bool": {
               "should": [
                    {"term": {"price":25}},
                    {"term": {"itemID": "id100123"}}
                   
                  ],
                "must_not": {
                    "term":{"price": 30}
                   }
                       
                }
             }
}
複製代碼

嵌套使用bool:

GET /lib4/items/_search
{
    "post_filter": {
          "bool": {
                "should": [
                    {"term": {"itemID": "id100123"}},
                    {
                      "bool": {
                          "must": [
                              {"term": {"itemID": "id100124"}},
                              {"term": {"price": 40}}
                            ]
                          }
                    }
                  ]
                }
            }
}
複製代碼

2.8.3 範圍過濾

gt: >

lt: <

gte: >=

lte: <=

GET /lib4/items/_search
{
     "post_filter": {
          "range": {
              "price": {
                   "gt": 25,
                   "lt": 50
                }
            }
      }
}
複製代碼

2.8.5 過濾非空

GET /lib4/items/_search
{
  "query": {
    "bool": {
      "filter": {
          "exists":{
             "field":"price"
         }
      }
    }
  }
}

GET /lib4/items/_search
{
    "query" : {
        "constant_score" : {
            "filter": {
                "exists" : { "field" : "price" }
            }
        }
    }
}
複製代碼

2.8.6 過濾器緩存

ElasticSearch提供了一種特殊的緩存,即過濾器緩存(filter cache),用來存儲過濾器的結果,被緩存的過濾器並不須要消耗過多的內存(由於它們只存儲了哪些文檔能與過濾器相匹配的相關信息),並且可供後續全部與之相關的查詢重複使用,從而極大地提升了查詢性能。

注意:ElasticSearch並非默認緩存全部過濾器, 如下過濾器默認不緩存:

numeric_range
script
geo_bbox
geo_distance
geo_distance_range
geo_polygon
geo_shape
and
or
not
複製代碼

exists,missing,range,term,terms默認是開啓緩存的

開啓方式:在filter查詢語句後邊加上 "_catch":true

2.9 聚合查詢

(1)sum

GET /lib4/items/_search
{
  "size":0,
  "aggs": {
     "price_of_sum": {
         "sum": {
           "field": "price"
         }
     }
  }
}
複製代碼

(2)min

GET /lib4/items/_search
{
  "size": 0, 
  "aggs": {
     "price_of_min": {
         "min": {
           "field": "price"
         }
     }
  }
}
複製代碼

(3)max

GET /lib4/items/_search
{
  "size": 0, 
  "aggs": {
     "price_of_max": {
         "max": {
           "field": "price"
         }
     }
  }
}
複製代碼

(4)avg

GET /lib4/items/_search
{
  "size":0,
  "aggs": {
     "price_of_avg": {
         "avg": {
           "field": "price"
         }
     }
  }
}
複製代碼

(5)cardinality:求基數

GET /lib4/items/_search
{
  "size":0,
  "aggs": {
     "price_of_cardi": {
         "cardinality": {
           "field": "price"
         }
     }
  }
}
複製代碼

(6)terms:分組

GET /lib4/items/_search
{
  "size":0,
  "aggs": {
     "price_group_by": {
         "terms": {
           "field": "price"
         }
     }
  }
}
複製代碼

對那些有唱歌興趣的用戶按年齡分組

GET /lib3/user/_search
{
  "query": {
      "match": {
        "interests": "changge"
      }
   },
   "size": 0, 
   "aggs":{
       "age_group_by":{
           "terms": {
             "field": "age",
             "order": {
               "avg_of_age": "desc"
             }
           },
           "aggs": {
             "avg_of_age": {
               "avg": {
                 "field": "age"
               }
             }
           }
       }
   }
}
複製代碼

2.10 複合查詢

將多個基本查詢組合成單一查詢的查詢

2.10.1 使用bool查詢

接收如下參數:

must: 文檔 必須匹配這些條件才能被包含進來。

must_not: 文檔 必須不匹配這些條件才能被包含進來。

should: 若是知足這些語句中的任意語句,將增長 _score,不然,無任何影響。它們主要用於修正每一個文檔的相關性得分。

filter: 必須 匹配,但它以不評分、過濾模式來進行。這些語句對評分沒有貢獻,只是根據過濾標準來排除或包含文檔。

相關性得分是如何組合的。每個子查詢都獨自地計算文檔的相關性得分。一旦他們的得分被計算出來, bool 查詢就將這些得分進行合併而且返回一個表明整個布爾操做的得分。

下面的查詢用於查找 title 字段匹配 how to make millions 而且不被標識爲 spam 的文檔。那些被標識爲 starred 或在2014以後的文檔,將比另外那些文檔擁有更高的排名。若是 二者 都知足,那麼它排名將更高:

{
    "bool": {
        "must": { "match": { "title": "how to make millions" }},
        "must_not": { "match": { "tag":   "spam" }},
        "should": [
            { "match": { "tag": "starred" }},
            { "range": { "date": { "gte": "2014-01-01" }}}
        ]
    }
}
複製代碼

若是沒有 must 語句,那麼至少須要可以匹配其中的一條 should 語句。但,若是存在至少一條 must 語句,則對 should 語句的匹配沒有要求。 若是咱們不想由於文檔的時間而影響得分,能夠用 filter 語句來重寫前面的例子:

{
    "bool": {
        "must": { "match": { "title": "how to make millions" }},
        "must_not": { "match": { "tag":   "spam" }},
        "should": [
            { "match": { "tag": "starred" }}
        ],
        "filter": {
          "range": { "date": { "gte": "2014-01-01" }} 
        }
    }
}
複製代碼

經過將 range 查詢移到 filter 語句中,咱們將它轉成不評分的查詢,將再也不影響文檔的相關性排名。因爲它如今是一個不評分的查詢,可使用各類對 filter 查詢有效的優化手段來提高性能。

bool 查詢自己也能夠被用作不評分的查詢。簡單地將它放置到 filter 語句中並在內部構建布爾邏輯:

{
    "bool": {
        "must": { "match": { "title": "how to make millions" }},
        "must_not": { "match": { "tag":   "spam" }},
        "should": [
            { "match": { "tag": "starred" }}
        ],
        "filter": {
          "bool": { 
              "must": [
                  { "range": { "date": { "gte": "2014-01-01" }}},
                  { "range": { "price": { "lte": 29.99 }}}
              ],
              "must_not": [
                  { "term": { "category": "ebooks" }}
              ]
          }
        }
    }
}
複製代碼

2.10.2 constant_score查詢

它將一個不變的常量評分應用於全部匹配的文檔。它被常常用於你只須要執行一個 filter 而沒有其它查詢(例如,評分查詢)的狀況下。

{
    "constant_score":   {
        "filter": {
            "term": { "category": "ebooks" } 
        }
    }
}
複製代碼

term 查詢被放置在 constant_score 中,轉成不評分的filter。這種方式能夠用來取代只有 filter 語句的 bool 查詢。

第三節 ElasticSearch原理

3.1 解析es的分佈式架構

3.1.1 分佈式架構的透明隱藏特性

ElasticSearch是一個分佈式系統,隱藏了複雜的處理機制

分片機制:咱們不用關心數據是按照什麼機制分片的、最後放入到哪一個分片中

分片的副本:

集羣發現機制(cluster discovery):好比當前咱們啓動了一個es進程,當啓動了第二個es進程時,這個進程做爲一個node自動就發現了集羣,而且加入了進去

shard負載均衡:好比如今有10shard,集羣中有3個節點,es會進行均衡的進行分配,以保持每一個節點均衡的負載請求

請求路由

3.1.2 擴容機制

垂直擴容:購置新的機器,替換已有的機器

水平擴容:直接增長機器

3.1.3 rebalance

增長或減小節點時會自動均衡

3.1.4 master節點

主節點的主要職責是和集羣操做相關的內容,如建立或刪除索引,跟蹤哪些節點是羣集的一部分,並決定哪些分片分配給相關的節點。穩定的主節點對集羣的健康是很是重要的。

3.1.5 節點對等

每一個節點都能接收請求 每一個節點接收到請求後都能把該請求路由到有相關數據的其它節點上 接收原始請求的節點負責採集數據並返回給客戶端

3.2 分片和副本機制

1.index包含多個shard

2.每一個shard都是一個最小工做單元,承載部分數據;每一個shard都是一個lucene實例,有完整的創建索引和處理請求的能力

3.增減節點時,shard會自動在nodes中負載均衡

4.primary shard和replica shard,每一個document確定只存在於某一個primary shard以及其對應的replica shard中,不可能存在於多個primary shard

5.replica shard是primary shard的副本,負責容錯,以及承擔讀請求負載

6.primary shard的數量在建立索引的時候就固定了,replica shard的數量能夠隨時修改

7.primary shard的默認數量是5,replica默認是1,默認有10個shard,5個primary shard,5個replica shard

8.primary shard不能和本身的replica shard放在同一個節點上(不然節點宕機,primary shard和副本都丟失,起不到容錯的做用),可是能夠和其餘primary shard的replica shard放在同一個節點上

3.3 單節點環境下建立索引分析

PUT /myindex
{
   "settings" : {
      "number_of_shards" : 3,
      "number_of_replicas" : 1
   }
}
複製代碼

這個時候,只會將3個primary shard分配到僅有的一個node上去,另外3個replica shard是沒法分配的(一個shard的副本replica,他們兩個是不能在同一個節點的)。集羣能夠正常工做,可是一旦出現節點宕機,數據所有丟失,並且集羣不可用,沒法接收任何請求。

3.4 兩個節點環境下建立索引分析

將3個primary shard分配到一個node上去,另外3個replica shard分配到另外一個節點上

primary shard 和replica shard 保持同步

primary shard 和replica shard 均可以處理客戶端的讀請求

3.5 水平擴容的過程

1.擴容後primary shard和replica shard會自動的負載均衡

2.擴容後每一個節點上的shard會減小,那麼分配給每一個shard的CPU,內存,IO資源會更多,性能提升

3.擴容的極限,若是有6個shard,擴容的極限就是6個節點,每一個節點上一個shard,若是想超出擴容的極限,好比說擴容到9個節點,那麼能夠增長replica shard的個數

4.6個shard,3個節點,最多能承受幾個節點所在的服務器宕機?(容錯性) 任何一臺服務器宕機都會丟失部分數據

爲了提升容錯性,增長shard的個數: 9個shard,(3個primary shard,6個replicashard),這樣就能容忍最多兩臺服務器宕機了

總結:擴容是爲了提升系統的吞吐量,同時也要考慮容錯性,也就是讓儘量多的服務器宕機還能保證數據不丟失

3.6 ElasticSearch的容錯機制

以9個shard,3個節點爲例:

1.若是master node 宕機,此時不是全部的primary shard都是Active status,因此此時的集羣狀態是red。

容錯處理的第一步:是選舉一臺服務器做爲master 容錯處理的第二步:新選舉出的master會把掛掉的primary shard的某個replica shard 提高爲primary shard,此時集羣的狀態爲yellow,由於少了一個replica shard,並非全部的replica shard都是active status

容錯處理的第三步:重啓故障機,新master會把全部的副本都複製一份到該節點上,(同步一下宕機後發生的修改),此時集羣的狀態爲green,由於全部的primary shard和replica shard都是Active status

3.7 文檔的核心元數據

1._index:

說明了一個文檔存儲在哪一個索引中

同一個索引下存放的是類似的文檔(文檔的field多數是相同的)

索引名必須是小寫的,不能如下劃線開頭,不能包括逗號

2._type:

表示文檔屬於索引中的哪一個類型

一個索引下只能有一個type

類型名能夠是大寫也能夠是小寫的,不能如下劃線開頭,不能包括逗號

3._id:

文檔的惟一標識,和索引,類型組合在一塊兒惟一標識了一個文檔

能夠手動指定值,也能夠由es來生成這個值

3.8 文檔id生成方式

1.手動指定

put /index/type/66
複製代碼

一般是把其它系統的已有數據導入到es時

2.由es生成id值

post /index/type
複製代碼

es生成的id長度爲20個字符,使用的是base64編碼,URL安全,使用的是GUID算法,分佈式下併發生成id值時不會衝突

3.9 _source元數據分析

其實就是咱們在添加文檔時request body中的內容

指定返回的結果中含有哪些字段:

get /index/type/1?_source=name
複製代碼

3.10 改變文檔內容原理解析

替換方式:

PUT /lib/user/4
{ "first_name" : "Jane",

"last_name" :   "Lucy",

"age" :         24,

"about" :       "I like to collect rock albums",

"interests":  [ "music" ]
}
複製代碼

修改方式(partial update):

POST /lib/user/2/_update
{
    "doc":{
       "age":26
     }
}
複製代碼

刪除文檔:標記爲deleted,隨着數據量的增長,es會選擇合適的時間刪除掉

3.11 基於groovy腳本執行partial update

es有內置的腳本支持,能夠基於groovy腳本實現複雜的操做

1.修改年齡

POST /lib/user/4/_update
{
  "script": "ctx._source.age+=1"
}
複製代碼

2.修更名字

POST /lib/user/4/_update
{
  "script": "ctx._source.last_name+='hehe'"
}
複製代碼

3.添加愛好

POST /lib/user/4/_update
{
  "script": {
    "source": "ctx._source.interests.add(params.tag)",
    "params": {
      "tag":"picture"
    }
  }
}
複製代碼

4.刪除愛好

POST /lib/user/4/_update
{
  "script": {
    "source": "ctx._source.interests.remove(ctx._source.interests.indexOf(params.tag))",
    "params": {
      "tag":"picture"
    }
  }
}
複製代碼

5.刪除文檔

POST /lib/user/4/_update
{
  "script": {
    "source": "ctx.op=ctx._source.age==params.count?'delete':'none'",
    "params": {
        "count":29
    }
  }
}

6.upsert

POST /lib/user/4/_update
{
  "script": "ctx._source.age += 1",
  
  "upsert": {
     "first_name" : "Jane",
     "last_name" :   "Lucy",
     "age" :  20,
     "about" :       "I like to collect rock albums",
     "interests":  [ "music" ]
  }
}
複製代碼

3.12 partial update 處理併發衝突

使用的是樂觀鎖:_version

retry_on_conflict:

POST /lib/user/4/_update?retry_on_conflict=3

從新獲取文檔數據和版本信息進行更新,不斷的操做,最多操做的次數就是retry_on_conflict的值

3.13 文檔數據路由原理解析

1.文檔路由到分片上:

一個索引由多個分片構成,當添加(刪除,修改)一個文檔時,es就須要決定這個文檔存儲在哪一個分片上,這個過程就稱爲數據路由(routing)

2.路由算法:

shard=hash(routing) % number_of_pirmary_shards
複製代碼

示例:一個索引,3個primary shard

(1)每次增刪改查時,都有一個routing值,默認是文檔的_id的值

(2)對這個routing值使用哈希函數進行計算

(3)計算出的值再和主分片個數取餘數

餘數確定在0---(number_of_pirmary_shards-1)之間,文檔就在對應的shard上

routing值默認是文檔的_id的值,也能夠手動指定一個值,手動指定對於負載均衡以及提升批量讀取的性能都有幫助

3.primary shard個數一旦肯定就不能修改了

3.14 文檔增刪改內部原理

1:發送增刪改請求時,能夠選擇任意一個節點,該節點就成了協調節點(coordinating node)

2.協調節點使用路由算法進行路由,而後將請求轉到primary shard所在節點,該節點處理請求,並把數據同步到它的replica shard

3.協調節點對客戶端作出響應

3.15 寫一致性原理和quorum機制

1.任何一個增刪改操做均可以跟上一個參數 consistency

能夠給該參數指定的值:

one: (primary shard)只要有一個primary shard是活躍的就能夠執行

all: (all shard)全部的primary shard和replica shard都是活躍的才能執行

quorum: (default) 默認值,大部分shard是活躍的才能執行 (例如共有6個shard,至少有3個shard是活躍的才能執行寫操做)

2.quorum機制:多數shard都是可用的,

int((primary+number_of_replica)/2)+1

例如:3個primary shard,1個replica

int((3+1)/2)+1=3

至少3個shard是活躍的

注意:可能出現shard不能分配齊全的狀況

好比:1個primary shard,1個replica int((1+1)/2)+1=2 可是若是隻有一個節點,由於primary shard和replica shard不能在同一個節點上,因此仍然不能執行寫操做

再舉例:1個primary shard,3個replica,2個節點

int((1+3)/2)+1=3

最後:當活躍的shard的個數沒有達到要求時, es默認會等待一分鐘,若是在等待的期間活躍的shard的個數沒有增長,則顯示timeout

put /index/type/id?timeout=60s

3.16 文檔查詢內部原理

第一步:查詢請求發給任意一個節點,該節點就成了coordinating node,該節點使用路由算法算出文檔所在的primary shard

第二步:協調節點把請求轉發給primary shard也能夠轉發給replica shard(使用輪詢調度算法(Round-Robin Scheduling,把請求平均分配至primary shard 和replica shard)

第三步:處理請求的節點把結果返回給協調節點,協調節點再返回給應用程序

特殊狀況:請求的文檔還在創建索引的過程當中,primary shard上存在,但replica shar上不存在,可是請求被轉發到了replica shard上,這時就會提示找不到文檔

3.17 bulk批量操做的json格式解析

bulk的格式:

{action:{metadata}}\n

{requstbody}\n
複製代碼

爲何不使用以下格式:

[{

"action": {

},

"data": {

}

}]
複製代碼

這種方式可讀性好,可是內部處理就麻煩了:

1.將json數組解析爲JSONArray對象,在內存中就須要有一份json文本的拷貝,另外還有一個JSONArray對象。

2.解析json數組裏的每一個json,對每一個請求中的document進行路由

3.爲路由到同一個shard上的多個請求,建立一個請求數組

4.將這個請求數組序列化

5.將序列化後的請求數組發送到對應的節點上去

耗費更多內存,增長java虛擬機開銷

1.不用將其轉換爲json對象,直接按照換行符切割json,內存中不須要json文本的拷貝

2.對每兩個一組的json,讀取meta,進行document路由

3.直接將對應的json發送到node上去

3.18 查詢結果分析

{
  "took": 419,
  "timed_out": false,
  "_shards": {
    "total": 3,
    "successful": 3,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 3,
    "max_score": 0.6931472,
    "hits": [
      {
        "_index": "lib3",
        "_type": "user",
        "_id": "3",
        "_score": 0.6931472,
        "_source": {
          "address": "bei jing hai dian qu qing he zhen",
          "name": "lisi"
        }
      },
      {
        "_index": "lib3",
        "_type": "user",
        "_id": "2",
        "_score": 0.47000363,
        "_source": {
          "address": "bei jing hai dian qu qing he zhen",
          "name": "zhaoming"
        }
      }
複製代碼

took:查詢耗費的時間,單位是毫秒

_shards:共請求了多少個shard

total:查詢出的文檔總個數

max_score: 本次查詢中,相關度分數的最大值,文檔和這次查詢的匹配度越高,_score的值越大,排位越靠前

hits:默認查詢前10個文檔

timed_out:

GET /lib3/user/_search?timeout=10ms
{
    "_source": ["address","name"],
    "query": {
        "match": {
            "interests": "changge"
        }
    }
}
複製代碼

3.19 多index,多type查詢模式

GET _search

GET /lib/_search

GET /lib,lib3/_search

GET /*3,*4/_search

GET /lib/user/_search

GET /lib,lib4/user,items/_search

GET /_all/_search

GET /_all/user,items/_search
複製代碼

3.20 分頁查詢中的deep paging問題

GET /lib3/user/_search
{
    "from":0,
    "size":2,
    "query":{
        "terms":{
            "interests": ["hejiu","changge"]
        }
    }
}

GET /_search?from=0&size=3
複製代碼

deep paging:查詢的很深,好比一個索引有三個primary shard,分別存儲了6000條數據,咱們要獲得第100頁的數據(每頁10條),相似這種狀況就叫deep paging

如何獲得第100頁的10條數據?

在每一個shard中搜索990到999這10條數據,而後用這30條數據排序,排序以後取10條數據就是要搜索的數據,這種作法是錯的,由於3個shard中的數據的_score分數不同,可能這某一個shard中第一條數據的_score分數比另外一個shard中第1000條都要高,因此在每一個shard中搜索990到999這10條數據而後排序的作法是不正確的。

正確的作法是每一個shard把0到999條數據所有搜索出來(按排序順序),而後所有返回給coordinate node,由coordinate node按_score分數排序後,取出第100頁的10條數據,而後返回給客戶端。

deep paging性能問題

1.耗費網絡帶寬,由於搜索過深的話,各shard要把數據傳送給coordinate node,這個過程是有大量數據傳遞的,消耗網絡,

2.消耗內存,各shard要把數據傳送給coordinate node,這個傳遞回來的數據,是被coordinate node保存在內存中的,這樣會大量消耗內存。

3.消耗cpu coordinate node要把傳回來的數據進行排序,這個排序過程很消耗cpu.

鑑於deep paging的性能問題,因此應儘可能減小使用。

3.21 query string查詢及copy_to解析

GET /lib3/user/_search?q=interests:changge

GET /lib3/user/_search?q=+interests:changge

GET /lib3/user/_search?q=-interests:changge
複製代碼

copy_to字段是把其它字段中的值,以空格爲分隔符組成一個大字符串,而後被分析和索引,可是不存儲,也就是說它能被查詢,但不能被取回顯示。

注意:copy_to指向的字段字段類型要爲:text

當沒有指定field時,就會從copy_to字段中查詢

GET /lib3/user/_search?q=changge
複製代碼

3.22 字符串排序問題

對一個字符串類型的字段進行排序一般不許確,由於已經被分詞成多個詞條了

解決方式:對字段索引兩次,一次索引分詞(用於搜索),一次索引不分詞(用於排序)

GET /lib3/_search

GET /lib3/user/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "interests": {
        "order": "desc"
      }
    }
  ]
}

GET /lib3/user/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "interests.raw": {
        "order": "asc"
      }
    }
  ]
}

DELETE lib3

PUT /lib3
{
    "settings":{
        "number_of_shards" : 3,
        "number_of_replicas" : 0
      },
     "mappings":{
      "user":{
        "properties":{
            "name": {"type":"text"},
            "address": {"type":"text"},
            "age": {"type":"integer"},
            "birthday": {"type":"date"},
            "interests": {
                "type":"text",
                "fields": {
                  "raw":{
                     "type": "keyword"
                   }
                },
                "fielddata": true
             }
          }
        }
     }
}
複製代碼

3.23 如何計算相關度分數

使用的是TF/IDF算法(Term Frequency&Inverse Document Frequency)

1.Term Frequency:咱們查詢的文本中的詞條在document本中出現了多少次,出現次數越多,相關度越高

搜索內容: hello world

Hello,I love china.

Hello world,how are you!
複製代碼

2.Inverse Document Frequency:咱們查詢的文本中的詞條在索引的全部文檔中出現了多少次,出現的次數越多,相關度越低

搜索內容:hello world

hello,what are you doing?

I like the world.

hello 在索引的全部文檔中出現了500次,world出現了100次
複製代碼

3.Field-length(字段長度歸約) norm:field越長,相關度越低

搜索內容:hello world

{"title":"hello,what's your name?","content":{"owieurowieuolsdjflk"}}

{"title":"hi,good morning","content":{"lkjkljkj.......world"}}
複製代碼

查看分數是如何計算的:

GET /lib3/user/_search?explain=true
{
    "query":{
        "match":{
            "interests": "duanlian,changge"
        }
    }
}
複製代碼

查看一個文檔可否匹配上某個查詢:

GET /lib3/user/2/_explain
{
    "query":{
        "match":{
            "interests": "duanlian,changge"
        }
    }
}
複製代碼

3.24 Doc Values 解析

DocValues實際上是Lucene在構建倒排索引時,會額外創建一個有序的正排索引(基於document => field value的映射列表)

{"birthday":"1985-11-11",age:23}

{"birthday":"1989-11-11",age:29}

document     age       birthday

doc1         23         1985-11-11

doc2         29         1989-11-11
複製代碼

存儲在磁盤上,節省內存

對排序,分組和一些聚合操做可以大大提高性能

注意:默認對不分詞的字段是開啓的,對分詞字段無效(須要把fielddata設置爲true)

PUT /lib3
{
    "settings":{
    "number_of_shards" : 3,
    "number_of_replicas" : 0
    },
     "mappings":{
      "user":{
        "properties":{
            "name": {"type":"text"},
            "address": {"type":"text"},
            "age": {
              "type":"integer",
              "doc_values":false
            },
            "interests": {"type":"text"},
            "birthday": {"type":"date"}
        }
      }
     }
}
複製代碼

3.25 基於scroll技術滾動搜索大量數據

若是一次性要查出來好比10萬條數據,那麼性能會不好,此時通常會採起用scoll滾動查詢,一批一批的查,直到全部數據都查詢完爲止。

1.scoll搜索會在第一次搜索的時候,保存一個當時的視圖快照,以後只會基於該舊的視圖快照提供數據搜索,若是這個期間數據變動,是不會讓用戶看到的

2.採用基於_doc(不使用_score)進行排序的方式,性能較高

3.每次發送scroll請求,咱們還須要指定一個scoll參數,指定一個時間窗口,每次搜索請求只要在這個時間窗口內能完成就能夠了

GET /lib3/user/_search?scroll=1m
{
  "query": {
    "match_all": {}
  },
  "sort":["_doc"],
  "size":3
}

GET /_search/scroll
{
   "scroll": "1m",
   "scroll_id": "DnF1ZXJ5VGhlbkZldGNoAwAAAAAAAAAdFkEwRENOVTdnUUJPWVZUd1p2WE5hV2cAAAAAAAAAHhZBMERDTlU3Z1FCT1lWVHdadlhOYVdnAAAAAAAAAB8WQTBEQ05VN2dRQk9ZVlR3WnZYTmFXZw=="
}
複製代碼

3.26 dynamic mapping策略

dynamic:

1.true:遇到陌生字段就 dynamic mapping

2.false:遇到陌生字段就忽略

3.strict:約到陌生字段就報錯

PUT /lib8
{
    "settings":{
    "number_of_shards" : 3,
    "number_of_replicas" : 0
    },
     "mappings":{
      "user":{
        "dynamic":strict,
        "properties":{
            "name": {"type":"text"},
            "address":{
                "type":"object",
                "dynamic":true
            },
        }
      }
     }
}

#會報錯

PUT  /lib8/user/1
{
  "name":"lisi",
  "age":20,
  "address":{
    "province":"beijing",
    "city":"beijing"
  }
}
複製代碼

date_detection:默認會按照必定格式識別date,好比yyyy-MM-dd

能夠手動關閉某個type的date_detection

PUT /lib8
{
    "settings":{
    "number_of_shards" : 3,
    "number_of_replicas" : 0
    },
     "mappings":{
      "user":{
        "date_detection": false,
        }
    }
}
複製代碼

定製 dynamic mapping template(type)

PUT /my_index
{ 
  "mappings": { 
    "my_type": { 
      "dynamic_templates": [ 
        { 
          "en": { 
            "match": "*_en", 
            "match_mapping_type": "string", 
            "mapping": { 
              "type": "text", 
              "analyzer": "english" 
            } 
          } 
        } 
      ] 
     } 
  } 
}
#使用了模板

PUT /my_index/my_type/3
{
  "title_en": "this is my dog"
  
}
#沒有使用模板

PUT /my_index/my_type/5
{
  "title": "this is my cat"
}

GET my_index/my_type/_search
{
  "query": {
    "match": {
      "title": "is"
    }
  }
}
複製代碼

3.27 重建索引

一個field的設置是不能修改的,若是要修改一個field,那麼應該從新按照新的mapping,創建一個index,而後將數據批量查詢出來,從新用bulk api寫入到index中。

批量查詢的時候,建議採用scroll api,而且採用多線程併發的方式來reindex數據,每次scroll就查詢指定日期的一段數據,交給一個線程便可。

PUT /index1/type1/4
{
   "content":"1990-12-12"
}

GET /index1/type1/_search

GET /index1/type1/_mapping



#報錯
PUT /index1/type1/4
{
   "content":"I am very happy."
}
複製代碼

#修改content的類型爲string類型,報錯,不容許修改

PUT /index1/_mapping/type1
{
  "properties": {
    "content":{
      "type": "text"
    }
  }
}
複製代碼

#建立一個新的索引,把index1索引中的數據查詢出來導入到新的索引中 #可是應用程序使用的是以前的索引,爲了避免用重啓應用程序,給index1這個索引發個#別名

PUT /index1/_alias/index2
複製代碼

#建立新的索引,把content的類型改成字符串

PUT /newindex
{
  "mappings": {
    "type1":{
      "properties": {
        "content":{
          "type": "text"
        }
      }
    }
  }
}
複製代碼

#使用scroll批量查詢

GET /index1/type1/_search?scroll=1m
{
  "query": {
    "match_all": {}
  },
  "sort": ["_doc"],
  "size": 2
}
複製代碼

#使用bulk批量寫入新的索引

POST /_bulk
{"index":{"_index":"newindex","_type":"type1","_id":1}}
{"content":"1982-12-12"}
複製代碼

#將別名index2和新的索引關聯,應用程序不用重啓

POST /_aliases
{
  "actions": [
    {"remove": {"index":"index1","alias":"index2"}},
    {"add": {"index": "newindex","alias": "index2"}}
]
}

GET index2/type1/_search
複製代碼

3.28 索引不可變的緣由

倒排索引包括:

文檔的列表,文檔的數量,詞條在每一個文檔中出現的次數,出現的位置,每一個文檔的長度,全部文檔的平均長度

索引不變的緣由:

不須要鎖,提高了併發性能

能夠一直保存在緩存中(filter)

節省cpu和io開銷

第四節 在Java應用中訪問ElasticSearch

4.1 在Java應用中實現查詢文檔

pom中加入ElasticSearch6.2.4的依賴:

<dependencies>
    <dependency>
      <groupId>org.elasticsearch.client</groupId>
      <artifactId>transport</artifactId>
      <version>6.2.4</version>
    </dependency>    
    
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
  
  </dependencies> 
  
  <build>
      <plugins>
			<!-- java編譯插件 -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.2</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>
		</plugins>
  </build>
複製代碼

4.2 在Java應用中實現添加文檔

"{" +
                "\"id\":\"1\"," +
                "\"title\":\"Java設計模式之裝飾模式\"," +
                "\"content\":\"在沒必要改變原類文件和使用繼承的狀況下,動態地擴展一個對象的功能。\"," +
                "\"postdate\":\"2018-05-20 14:38:00\"," +
                "\"url\":\"csdn.net/79239072\"" +
        "}"
        
 XContentBuilder doc1 = XContentFactory.jsonBuilder()
                    .startObject()
                    .field("id","3")
                    .field("title","Java設計模式之單例模式")
                    .field("content","枚舉單例模式能夠防反射攻擊。")
                    .field("postdate","2018-02-03")
                    .field("url","csdn.net/79247746")
                    .endObject();
        
        IndexResponse response = client.prepareIndex("index1", "blog", null)
                .setSource(doc1)
                .get();
        
		System.out.println(response.status());
複製代碼

4.3 在Java應用中實現刪除文檔

DeleteResponse response=client.prepareDelete("index1","blog","SzYJjWMBjSAutsuLRP_P").get();

//刪除成功返回OK,不然返回NOT_FOUND

System.out.println(response.status());
複製代碼

4.4 在Java應用中實現更新文檔

UpdateRequest request=new UpdateRequest();
        request.index("index1")
                .type("blog")
                .id("2")
                .doc(
                		XContentFactory.jsonBuilder().startObject()
                        .field("title","單例模式解讀")
                        .endObject()
                );
UpdateResponse response=client.update(request).get();

//更新成功返回OK,不然返回NOT_FOUND

System.out.println(response.status());

upsert方式:

IndexRequest request1 =new IndexRequest("index1","blog","3")
                .source(
                		XContentFactory.jsonBuilder().startObject()
                                .field("id","3")
                                .field("title","裝飾模式")
                                .field("content","動態地擴展一個對象的功能")
                                .field("postdate","2018-05-23")
                                .field("url","csdn.net/79239072")
                                .endObject()
                );
        UpdateRequest request2=new UpdateRequest("index1","blog","3")
                .doc(
                		XContentFactory.jsonBuilder().startObject()
                        .field("title","裝飾模式解讀")
                        .endObject()
                ).upsert(request1);
        
UpdateResponse response=client.update(request2).get();
        
//upsert操做成功返回OK,不然返回NOT_FOUND

System.out.println(response.status());
複製代碼

4.5 在Java應用中實現批量操做

MultiGetResponse mgResponse = client.prepareMultiGet()
	                .add("index1","blog","3","2")
	                .add("lib3","user","1","2","3")
	                .get();
		    
for(MultiGetItemResponse response:mgResponse){
	            GetResponse rp=response.getResponse();
	            if(rp!=null && rp.isExists()){
	                System.out.println(rp.getSourceAsString());
	            }
	        }
	        
bulk:

BulkRequestBuilder bulkRequest = client.prepareBulk();

bulkRequest.add(client.prepareIndex("lib2", "books", "4")
                .setSource(XContentFactory.jsonBuilder()
                        .startObject()
                        .field("title", "python")
                        .field("price", 68)
                        .endObject()
                )
        );
bulkRequest.add(client.prepareIndex("lib2", "books", "5")
                .setSource(XContentFactory.jsonBuilder()
                        .startObject()
                        .field("title", "VR")
                        .field("price", 38)
                        .endObject()
                )
        );
        //批量執行
BulkResponse bulkResponse = bulkRequest.get();
        
System.out.println(bulkResponse.status());
if (bulkResponse.hasFailures()) { 
            System.out.println("存在失敗操做");
        }

複製代碼
相關文章
相關標籤/搜索